summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-14 16:49:22 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-14 16:49:22 +0000
commit0a193e032ba1ecf3f003e027e833dc9d274cb740 (patch)
treea1dcc00cb7f5d26e437e05e658c38fc323fe919d
Initial revision
-rw-r--r--Xedit-color.ad195
-rw-r--r--Xedit-sample233
-rw-r--r--commands.c1064
-rw-r--r--hook.c1234
-rw-r--r--ispell.c2217
-rw-r--r--lisp.c274
-rw-r--r--lisp/README195
-rw-r--r--lisp/TODO81
-rw-r--r--lisp/bytecode.c3707
-rw-r--r--lisp/bytecode.h268
-rw-r--r--lisp/compile.c2225
-rw-r--r--lisp/core.c7040
-rw-r--r--lisp/core.h221
-rw-r--r--lisp/debugger.c828
-rw-r--r--lisp/debugger.h72
-rw-r--r--lisp/env.c151
-rw-r--r--lisp/format.c2121
-rw-r--r--lisp/format.h42
-rw-r--r--lisp/hash.c657
-rw-r--r--lisp/hash.h71
-rw-r--r--lisp/helper.c1124
-rw-r--r--lisp/helper.h115
-rw-r--r--lisp/internal.h784
-rw-r--r--lisp/io.c709
-rw-r--r--lisp/io.h115
-rw-r--r--lisp/lisp.c5507
-rw-r--r--lisp/lisp.h44
-rw-r--r--lisp/lsp.c79
-rw-r--r--lisp/math.c1473
-rw-r--r--lisp/math.h100
-rw-r--r--lisp/mathimp.c5225
-rw-r--r--lisp/modules/indent.lsp1420
-rw-r--r--lisp/modules/lisp.lsp174
-rw-r--r--lisp/modules/progmodes/c.lsp1118
-rw-r--r--lisp/modules/progmodes/html.lsp327
-rw-r--r--lisp/modules/progmodes/imake.lsp188
-rw-r--r--lisp/modules/progmodes/lisp.lsp384
-rw-r--r--lisp/modules/progmodes/make.lsp135
-rw-r--r--lisp/modules/progmodes/man.lsp160
-rw-r--r--lisp/modules/progmodes/rpm.lsp166
-rw-r--r--lisp/modules/progmodes/sgml.lsp428
-rw-r--r--lisp/modules/progmodes/sh.lsp113
-rw-r--r--lisp/modules/progmodes/xconf.lsp68
-rw-r--r--lisp/modules/progmodes/xlog.lsp102
-rw-r--r--lisp/modules/progmodes/xrdb.lsp115
-rw-r--r--lisp/modules/psql.c983
-rw-r--r--lisp/modules/syntax.lsp1452
-rw-r--r--lisp/modules/x11.c666
-rw-r--r--lisp/modules/xaw.c665
-rw-r--r--lisp/modules/xedit.lsp560
-rw-r--r--lisp/modules/xt.c1797
-rw-r--r--lisp/mp/mp.c822
-rw-r--r--lisp/mp/mp.h435
-rw-r--r--lisp/mp/mpi.c1656
-rw-r--r--lisp/mp/mpr.c436
-rw-r--r--lisp/package.c865
-rw-r--r--lisp/package.h62
-rw-r--r--lisp/pathname.c1096
-rw-r--r--lisp/pathname.h78
-rw-r--r--lisp/private.h536
-rw-r--r--lisp/re/README121
-rw-r--r--lisp/re/re.c2648
-rw-r--r--lisp/re/re.h123
-rw-r--r--lisp/re/rec.c1015
-rw-r--r--lisp/re/reo.c685
-rw-r--r--lisp/re/rep.h369
-rw-r--r--lisp/re/tests.c199
-rw-r--r--lisp/re/tests.txt461
-rw-r--r--lisp/read.c2058
-rw-r--r--lisp/read.h47
-rw-r--r--lisp/regex.c223
-rw-r--r--lisp/regex.h46
-rw-r--r--lisp/require.c159
-rw-r--r--lisp/require.h44
-rw-r--r--lisp/stream.c866
-rw-r--r--lisp/stream.h58
-rw-r--r--lisp/string.c1383
-rw-r--r--lisp/string.h95
-rw-r--r--lisp/struct.c371
-rw-r--r--lisp/struct.h53
-rw-r--r--lisp/test/hello.lsp72
-rw-r--r--lisp/test/list.lsp1895
-rw-r--r--lisp/test/math.lsp982
-rw-r--r--lisp/test/psql-1.lsp80
-rw-r--r--lisp/test/psql-2.lsp74
-rw-r--r--lisp/test/psql-3.lsp118
-rw-r--r--lisp/test/regex.lsp440
-rw-r--r--lisp/test/stream.lsp807
-rw-r--r--lisp/test/widgets.lsp71
-rw-r--r--lisp/time.c138
-rw-r--r--lisp/time.h39
-rw-r--r--lisp/write.c2411
-rw-r--r--lisp/write.h80
-rw-r--r--lisp/xedit.c1636
-rw-r--r--lisp/xedit.h94
-rw-r--r--options.c524
-rw-r--r--realpath.c208
-rw-r--r--strcasecmp.c40
-rw-r--r--util.c896
-rw-r--r--xedit.c629
-rw-r--r--xedit.h182
-rw-r--r--xedit.man443
102 files changed, 78061 insertions, 0 deletions
diff --git a/Xedit-color.ad b/Xedit-color.ad
new file mode 100644
index 0000000..0cc3239
--- /dev/null
+++ b/Xedit-color.ad
@@ -0,0 +1,195 @@
+! $XFree86: xc/programs/xedit/Xedit-color.ad,v 1.15 2002/11/13 04:35:45 paulo Exp $
+
+#include "Xedit"
+
+*background: gray70
+*foreground: gray15
+*borderColor: gray40
+*Command.highlightThickness: 1
+*Command.backgroundPixmap: gradient:vertical?dimension=18&start=gray90&end=gray65
+*Command.foreground: gray20
+*Command.displayList:\
+foreground gray92;\
+line 1,0,-2,0;\
+foreground gray95;\
+line 2,1,-3,1;\
+foreground gray74;\
+segments 3,-3,-4,-3, 2,-4,-3,-4, 1,-6,-2,-6;\
+point -1,-1;\
+foreground gray80;\
+line 0,0,0,-1;\
+point -1,0
+
+*Command.translations: #override \
+<Btn1Down>: set-values(1, foreground, gray20, background, rgb:d/5/5) set()\n\
+Button1<Leave>: reset() set-values(1, foreground, gray20, background, gray70)\n\
+<Btn1Up>: notify() unset()
+
+*Toggle.foreground: gray30
+*Toggle.background: gray82
+*Toggle.borderColor: gray90
+*Toggle.displayList:\
+foreground gray75;\
+segments 0,1,0,-2,-1,1,-1,-2;\
+foreground gray80;\
+segments 0,0,-1,0,0,-1,-1,-1;\
+points 1,1,1,-2,-2,1, -2,-2
+
+*bc_label.backgroundPixmap: gradient:vertical?dimension=18&start=gray90&end=gray65
+*bc_label.displayList:\
+foreground gray92;\
+line 1,0,-2,0;\
+foreground gray95;\
+line 2,1,-3,1;\
+foreground gray74;\
+segments 3,-2,-4,-2, 2,-3,-3,-3, 1,-5,-2,-5
+*bc_label.foreground: gray20
+
+*formWindow.backgroundPixmap: gradient:vertical?dimension=18&start=gray90&end=gray65
+*formWindow.displayList:\
+foreground gray92;\
+line 1,0,-2,0;\
+foreground gray95;\
+line 2,1,-3,1;\
+foreground gray74;\
+segments 3,-2,-4,-2, 2,-3,-3,-3, 1,-5,-2,-5
+*formWindow*backgroundPixmap: ParentRelative
+*formWindow*foreground: gray20
+*formWindow*background: gray74
+*formWindow.labelWindow.background: gray80
+*formWindow.labelWindow.displayList:\
+line-style onoffdash;\
+foreground gray30;\
+background gray96;\
+draw-rect 3,2,15,14;\
+foreground gray78;\
+fill-rect 4,3,15,14
+
+*dirlabel.foreground: gray20
+*dirlabel.backgroundPixmap: gradient:vertical?dimension=18&start=gray90&end=gray65
+*dirlabel.displayList:\
+foreground gray92;\
+line 1,0,-2,0;\
+foreground gray95;\
+line 2,1,-3,1;\
+foreground gray74;\
+segments 3,-2,-4,-2, 2,-3,-3,-3, 1,-5,-2,-5
+*dirwindow.background: gray96
+
+*internalBorderColor: gray40
+
+*Text*Text.background: gray96
+*Text.background: gray96
+*Text.?.background: gray96
+*Text*cursorColor: rgb:d/5/5
+*Text.displayList:\
+foreground white;\
+line 1,-1,-2,-1;\
+foreground gray88;\
+line 1,0,-2,0
+
+*Scrollbar.foreground: rgb:a/5/5
+*Scrollbar.background: gray60
+*Text.Scrollbar.background: gray60
+*Scrollbar.backgroundPixmap: gradient:horizontal?dimension=14&start=gray70&end=gray85
+*hScrollbar.backgroundPixmap: gradient:vertical?dimension=14&start=gray85&end=gray70
+*hScrollbar.displayList:\
+foreground gray85;\
+segments 1,-1,-2,-1,1,0,-2,0
+*vScrollbar.displayList:\
+foreground gray85;\
+segments 0,1,0,-2,-1,1,-1,-2
+*Scrollbar.thumb: vlines2
+*hScrollbar.thumb: hlines2
+
+*SimpleMenu*borderWidth: 0
+*SimpleMenu*backgroundPixmap: xlogo11?foreground=gray90&background=gray88
+*SimpleMenu*background: gray90
+*SimpleMenu*foreground: gray20
+*SimpleMenu.VerticalMargins: 3
+*SimpleMenu.HorizontalMargins: 3
+*SimpleMenu.SimpleMenu.VerticalMargins: 3
+*SimpleMenu.SimpleMenu.HorizontalMargins: 3
+*SimpleMenu.menuLabel.foreground: rgb:d/5/5
+*SimpleMenu*displayList:\
+foreground gray70;\
+lines +2,-2,-2,-2,-2,+2;\
+foreground gray95;\
+lines -2,+1,+1,+1,+1,-2;\
+foreground gray30;\
+lines +1,-1,-1,-1,-1,+1;\
+foreground gray80;\
+lines -1,+0,+0,+0,+0,-1
+
+*TransientShell.backgroundPixmap: xlogo11?foreground=gray90&background=gray87
+*TransientShell.Form.backgroundPixmap: ParentRelative
+*TransientShell.Form.Label.backgroundPixmap: ParentRelative
+
+*ispell*Scrollbar.borderColor: gray50
+*ispell*Scrollbar.displayList:
+*ispell*List.background: gray96
+*ispell*word.background: gray96
+*ispell*word.backgroundPixmap: XtUnspecifiedPixmap
+*ispell*word.translations:\
+<Enter>: highlight()\n\
+<Leave>: reset()\n\
+<Btn1Down>: set()\n\
+<Btn1Up>: notify() unset()\n
+*ispell*word.displayList:
+*ispell.Form.Form.borderColor: gray80
+*ispell.Form.Form.displayList: \
+foreground gray90;\
+lines +1,-1,-1,-1,-1,+1;\
+foreground gray30;\
+lines -1,+0,+0,+0,+0,-1;\
+foreground gray60;\
+lines +2,-2,-2,-2,-2,+2
+*ispell*status.backgroundPixmap: XtUnspecifiedPixmap
+*ispell*status.background: gray96
+*ispell*terse.justify: right
+*ispell*terse.internalWidth: 10
+*ispell*terse.background: gray75
+*ispell*terse.foreground: gray20
+*ispell*terse.highlightThickness: 0
+*ispell*terse.displayList:\
+line-style solid;\
+foreground gray70;\
+fill-arc 6,4,14,12;\
+foreground gray90;\
+draw-arc 6,4,14,12,45,180;\
+draw-lines +0,-1,-1,-1,-1,+0;\
+foreground gray50;\
+draw-arc 6,4,14,12,225,180;\
+draw-lines -2,+0,+0,+0,+0,-2;\
+foreground gray45;\
+dashes 2,1;\
+line-style onoffdash;\
+draw-rect +1,+1,-2,-2
+*ispell*options.MenuButton.borderColor: gray70
+*ispell*options.MenuButton.background: gray78
+*ispell*options.MenuButton.backgroundPixmap: gradient:vertical?dimension=18&start=gray90&end=gray65
+*ispell*options.MenuButton.displayList:\
+foreground gray60;\
+lines +2,-2,-2,-2,-2,+2;\
+foreground gray95;\
+lines -3,+1,+1,+1,+1,-3;\
+foreground gray78;\
+points -2,+1,+1,-2
+
+*tip.foreground: rgb:48/48/00
+*tip.backgroundPixmap: None
+*tip.borderWidth: 0
+*tip.rightMargin: 7
+*tip.bottomMargin: 3
+*tip.displayList:\
+foreground rgb:f/e/8;\
+fill-rect 1,1,-2,-2;\
+foreground rgb:d/c/6;\
+draw-segments 0,1,0,-3,1,-2,-3,-2,-2,-3,-2,1,-3,0,1,0;\
+foreground rgb:a/8/4;\
+draw-lines 2,-1, -2,-1, -1,-2, -1,2
+
+*grip.foreground: gray10
+*grip.displayList:\
+foreground gray80;\
+points 0,0,0,-1,-1,-1,-1,0
diff --git a/Xedit-sample b/Xedit-sample
new file mode 100644
index 0000000..36981d2
--- /dev/null
+++ b/Xedit-sample
@@ -0,0 +1,233 @@
+! $XFree86: xc/programs/xedit/Xedit-sample,v 1.10 2002/11/08 08:00:55 paulo Exp $
+
+Xedit*insertFile*text.width: 150
+Xedit*search*searchText.width: 192
+Xedit*search*replaceText.width: 192
+Xedit*search*case.horizDistance: 14
+Xedit*Text.autoFill: false
+Xedit*cursorColor: red
+Xedit.geometry: 600x400
+Xedit*font: -*-courier-bold-r-*-*-12-*-*-*-*-*-*-1
+Xedit*Text.?.font: -*-courier-medium-r-*-*-12-*-*-*-*-*-*-1
+Xedit*Text.rightMargin: 0
+Xedit*filename.backgroundPixmap: gradient:vertical?dimension=3&start=rgb:e/d/d&end=rgb:f/e/e
+Xedit*filename.topMargin: 3
+Xedit*messageWindow.backgroundPixmap: gradient:vertical?dimension=3&start=rgb:e/d/d&end=rgb:f/e/e
+Xedit*editWindow.backgroundPixmap: gradient:vertical?dimension=3&start=rgb:e/d/d&end=rgb:f/e/e
+Xedit*editWindow.wrap: Never
+Xedit*formWindow*font: -*-courier-bold-r-*-*-12-*-*-*-*-*-*-1
+Xedit*formWindow*background: rgb:9/0/2
+Xedit*formWindow*foreground: rgb:d/c/8
+Xedit*formWindow*backgroundPixmap: XtUnspecifiedPixmap
+Xedit*formWindow.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +1,-1,-1,-1,-1,+1;\
+foreground rgb:d/0/3;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground rgb:b/2/3;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:8/0/1;\
+draw-lines -2,+1,+1,+1,+1,-2
+Xedit*bc_label.background: rgb:9/0/2
+Xedit*bc_label.foreground: rgb:d/c/8
+Xedit*bc_label.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +1,-1,-1,-1,-1,+1;\
+foreground rgb:d/0/3;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground rgb:b/2/3;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:8/0/1;\
+draw-lines -2,+1,+1,+1,+1,-2
+Xedit*Scrollbar.background: rgb:e/c/c
+Xedit*Scrollbar.foreground: rgb:a/7/7
+Xedit*Scrollbar.borderColor: rgb:9/8/8
+Xedit*vScrollbar.thumb: hlines2
+Xedit*hScrollbar.thumb: vlines2
+Xedit*Scrollbar.width: 12
+Xedit*Scrollbar.height: 12
+Xedit*Scrollbar.backgroundPixmap: gray3?background=rgb:c/a/a&foreground=rgb:d/b/b
+Xedit*Scrollbar.displayList:\
+foreground rgb:7/4/4;\
+draw-lines +0,-1,-1,-1,-1,+0;\
+foreground rgb:4/2/2;\
+draw-lines -2,+0,+0,+0,+0,-2
+Xedit*Paned.internalBorderColor: rgb:9/8/8
+Xedit*buttons.internalBorderColor: rgb:8/7/7
+Xedit*Grip.width: 10
+Xedit*Grip.height: 10
+Xedit*Grip.foreground: IndianRed3
+Xedit*Grip.displayList:\
+foreground IndianRed1;\
+draw-rect +0,+0,-2,-2;\
+foreground IndianRed4;\
+draw-lines +1,-1,-1,-1,-1,+1
+Xedit*Command.internalWidth: 8
+Xedit*Command.internalHeight: 2
+Xedit*Command.borderWidth: 0
+Xedit*Command.background: rgb:9/0/2
+Xedit*Command.foreground: rgb:d/c/8
+Xedit*Command.highlightThickness: 0
+Xedit*Command.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +1,-1,-1,-1,-1,+1;\
+foreground rgb:d/0/3;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground rgb:b/2/3;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:8/0/1;\
+draw-lines -2,+1,+1,+1,+1,-2
+Xedit*Toggle.borderWidth: 1
+Xedit*Toggle.borderColor: rgb:a/9/9
+Xedit*Toggle.background: rgb:f/e/e
+Xedit*Toggle.foreground: rgb:9/0/2
+Xedit*Toggle.highlightThickness: 1
+Xedit*Toggle.internalWidth: 10
+Xedit*Toggle.internalHeight: 2
+Xedit*Toggle.justify: left
+Xedit*Toggle.displayList:\
+foreground rgb:6/0/1;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:d/2/3;\
+draw-lines -2,+1,+1,+1,+1,-2
+Xedit*Form.background: rgb:f/e/e
+Xedit*Form.Label.background: rgb:f/e/e
+Xedit*Form.backgroundPixmap: gradient:vertical?dimension=3&start=rgb:e/d/d&end=rgb:f/e/e
+Xedit*Form.Label.backgroundPixmap: ParentRelative
+Xedit*Form*Text.backgroundPixmap: gradient:vertical?dimension=3&start=rgb:dc/cc/cc&end=rgb:ec/dc/dc
+Xedit*Form.Text.leftMargin: 5
+Xedit*Form.Text.rightMargin: 4
+Xedit*Form.Text.topMargin: 2
+Xedit*Form.Text.bottomMargin: 2
+Xedit*Form.Text.borderWidth: 2
+Xedit*Form.Text.borderColor: rgb:9/0/2
+Xedit*Form.Text.displayList:\
+foreground gray50;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground gray80;\
+draw-rect +1,+1,-1,-1
+Xedit*Form.Label.font: fixed
+Xedit*Form.Command.internalWidth: 8
+Xedit*Form.Command.internalHeight: 4
+Xedit*Form.Command.borderWidth: 0
+Xedit*Form.Command.background: rgb:9/0/2
+Xedit*Form.Command.foreground: rgb:d/c/8
+Xedit*Form.Command.highlightThickness: 0
+Xedit*Form.Command.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:d/0/3;\
+draw-lines -2,+1,+1,+1,+1,-2;\
+foreground rgb:b/2/3;\
+draw-lines +3,-3,-3,-3,-3,+3;\
+foreground rgb:8/0/1;\
+draw-lines -3,+2,+2,+2,+2,-3;\
+foreground rgb:9/8/8;\
+draw-lines +2,-1,-1,-1,-1,+2;\
+foreground rgb:f/e/e;\
+draw-lines -1,+1,-1,+0,+0,+0,+0,-1,+1,-1
+Xedit*SimpleMenu.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +1,-1,-1,-1,-1,+1;\
+foreground rgb:d/0/3;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground rgb:b/2/3;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:8/0/1;\
+draw-lines -2,+1,+1,+1,+1,-2;\
+foreground rgb:a/1/3;\
+fill-rect +3,+4,-4,+22;\
+foreground rgb:b/a/6;\
+draw-line +4,+4,-5,+4;\
+draw-line +4,+22,-5,+22
+Xedit*SimpleMenu.VerticalMargins: 2
+Xedit*SimpleMenu.HorizontalMargins: 2
+Xedit*SimpleMenu.borderWidth: 0
+Xedit*SimpleMenu.?.foreground: rgb:d/c/8
+Xedit*SimpleMenu.background: rgb:9/0/2
+Xedit*dirlabel*font: -*-courier-bold-r-*-*-12-*-*-*-*-*-*-1
+Xedit*dirlabel*background: rgb:9/0/2
+Xedit*dirlabel*foreground: rgb:d/c/8
+Xedit*dirlabel*backgroundPixmap: XtUnspecifiedPixmap
+Xedit*dirlabel.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +1,-1,-1,-1,-1,+1;\
+foreground rgb:d/0/3;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground rgb:b/2/3;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:8/0/1;\
+draw-lines -2,+1,+1,+1,+1,-2
+Xedit*dirwindow*font: -*-courier-bold-r-*-*-12-*-*-*-*-*-*-1
+Xedit*dirwindow*foreground: rgb:9/0/2
+Xedit*dirwindow.backgroundPixmap: gradient:vertical?dimension=3&start=rgb:e/d/d&end=rgb:f/e/e
+Xedit*viewport*Scrollbar.thumb: hlines2
+Xedit*ispell*text.width: 192
+Xedit*ispell.form.defaultDistance: 4
+Xedit*ispell*commands.Command.vertDistance: 0
+Xedit*ispell*commands.Command.height: 22
+Xedit*ispell*commands.check.vertDistance: 3
+Xedit*ispell*commands.look.vertDistance: 3
+Xedit*ispell*replaceAll.width: 39
+Xedit*ispell*ignoreAll.width: 39
+Xedit*ispell*check.width: 54
+Xedit*ispell*look.width: 50
+Xedit*ispell*undo.width: 104
+Xedit*ispell*add.width: 52
+Xedit*ispell*addUncap.width: 52
+Xedit*ispell*suspend.width: 60
+Xedit*ispell*suspend.internalWidth: 0
+Xedit*ispell*cancel.width: 44
+Xedit*ispell*cancel.internalWidth: 0
+Xedit*ispell*terse.width: 101
+Xedit*ispell*terse.height: 18
+Xedit*ispell*terse.vertDistance: 2
+Xedit*ispell*replace.internalWidth: 0
+Xedit*ispell*viewport.height: 139
+Xedit*ispell*list.foreground: rgb:9/0/2
+Xedit*ispell*list.backgroundPixmap: gradient:vertical?dimension=3&start=rgb:e/d/d&end=rgb:f/e/e
+Xedit*ispell*borderColor: rgb:9/0/2
+Xedit*ispell*word.font: -*-courier-medium-r-*-*-12-*-*-*-*-*-*-1
+Xedit*ispell*word.displayList:
+Xedit*ispell*word.foreground: black
+Xedit*ispell*word.background: rgb:f/e/e
+Xedit*ispell*word.borderWidth: 1
+Xedit*ispell*word.height: 18
+Xedit*SimpleMenu.SimpleMenu.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +1,-1,-1,-1,-1,+1;\
+foreground rgb:d/0/3;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground rgb:b/2/3;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:8/0/1;\
+draw-lines -2,+1,+1,+1,+1,-2
+Xedit*columns*Label.width: 84
+Xedit*columns.minWidth: 130
+Xedit*columns.minHeight: 80
+Xedit*columns.maxWidth: 130
+Xedit*columns.maxHeight: 80
+Xedit*columns*cancel.horizDistance: 6
+Xedit*ispell*SimpleMenu.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +1,-1,-1,-1,-1,+1;\
+foreground rgb:d/0/3;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground rgb:b/2/3;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:8/0/1;\
+draw-lines -2,+1,+1,+1,+1,-2
+Xedit*ispell*MenuButton.borderColor: rgb:f/e/e
+Xedit*ispell*MenuButton.background: rgb:9/0/2
+Xedit*ispell*MenuButton.foreground: rgb:d/c/8
+Xedit*ispell*MenuButton.highlightThickness: 0
+Xedit*ispell*MenuButton.displayList:\
+foreground rgb:3/0/1;\
+draw-lines +1,-1,-1,-1,-1,+1;\
+foreground rgb:d/0/3;\
+draw-lines -1,+0,+0,+0,+0,-1;\
+foreground rgb:b/2/3;\
+draw-lines +2,-2,-2,-2,-2,+2;\
+foreground rgb:8/0/1;\
+draw-lines -2,+1,+1,+1,+1,-2
+Xedit*tip.background: yellow
diff --git a/commands.c b/commands.c
new file mode 100644
index 0000000..69dabe2
--- /dev/null
+++ b/commands.c
@@ -0,0 +1,1064 @@
+/* $XConsortium: commands.c,v 1.33 91/10/21 14:32:18 eswu Exp $ */
+
+/*
+ * 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.
+ */
+/* $XFree86: xc/programs/xedit/commands.c,v 1.29 2002/11/05 06:57:05 paulo Exp $ */
+
+#include <X11/Xfuncs.h>
+#include <X11/Xos.h>
+#include "xedit.h"
+#ifdef CRAY
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <X11/Xmu/SysUtil.h>
+#include <X11/IntrinsicP.h>
+#include <X11/Xaw/TextSrcP.h>
+
+void ResetSourceChanged(xedit_flist_item*);
+static void ResetDC(Widget, XtPointer, XtPointer);
+
+static void AddDoubleClickCallback(Widget, Bool);
+static Bool ReallyDoLoad(char*, char*);
+static char *makeBackupName(String, String, unsigned);
+
+extern Widget scratch, texts[3], labels[3];
+static Boolean double_click = FALSE;
+
+#define DC_UNSAVED 1
+#define DC_LOADED 2
+#define DC_CLOBBER 3
+#define DC_KILL 4
+#define DC_SAVE 5
+static int dc_state;
+
+/* Function Name: AddDoubleClickCallback(w)
+ * Description: Adds a callback that will reset the double_click flag
+ * to false when the text is changed.
+ * Arguments: w - widget to set callback upon.
+ * state - If true add the callback, else remove it.
+ * Returns: none.
+ */
+static void
+AddDoubleClickCallback(Widget w, Bool state)
+{
+ Arg args[1];
+ static XtCallbackRec cb[] = { {NULL, NULL}, {NULL, NULL} };
+
+ if (XtIsSubclass(w, asciiSrcObjectClass)) {
+ if (state)
+ XtAddCallback(w, XtNcallback, ResetDC, NULL);
+ else
+ XtRemoveCallback(w, XtNcallback, ResetDC, NULL);
+ }
+ else {
+ if (state)
+ cb[0].callback = ResetDC;
+ else
+ cb[0].callback = NULL;
+
+ XtSetArg(args[0], XtNcallback, cb);
+ XtSetValues(w, args, ONE);
+ }
+}
+
+/* Function Name: ResetDC
+ * Description: Resets the double click flag.
+ * Arguments: w - the text widget.
+ * junk, garbage - *** NOT USED ***
+ * Returns: none.
+ */
+
+/* ARGSUSED */
+static void
+ResetDC(Widget w, XtPointer junk, XtPointer garbage)
+{
+ double_click = FALSE;
+
+ AddDoubleClickCallback(w, FALSE);
+}
+
+/*ARGSUSED*/
+void
+QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ DoQuit(w, NULL, NULL);
+}
+
+/*ARGSUSED*/
+void
+DoQuit(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ unsigned i;
+ Bool source_changed = False;
+
+ if (!double_click || (dc_state && dc_state != DC_UNSAVED)) {
+ for (i = 0; i < flist.num_itens; i++)
+ if (flist.itens[i]->flags & CHANGED_BIT) {
+ source_changed = True;
+ break;
+ }
+ }
+ if(!source_changed) {
+#ifndef __UNIXOS2__
+ XeditLispCleanUp();
+#endif
+ exit(0);
+ }
+
+ XeditPrintf("Unsaved changes. Save them, or Quit again.\n");
+ Feep();
+ double_click = TRUE;
+ dc_state = DC_UNSAVED;
+ AddDoubleClickCallback(XawTextGetSource(textwindow), True);
+}
+
+static char *
+makeBackupName(String buf, String filename, unsigned len)
+{
+ if (app_resources.backupNamePrefix
+ && strlen(app_resources.backupNamePrefix)) {
+ if (strchr(app_resources.backupNamePrefix, '/'))
+ XmuSnprintf(buf, len, "%s%s%s", app_resources.backupNamePrefix,
+ filename, app_resources.backupNameSuffix);
+ else {
+ char fname[BUFSIZ];
+ char *name, ch;
+
+ strncpy(fname, filename, sizeof(fname) - 1);
+ fname[sizeof(fname) - 1] = '\0';
+ if ((name = strrchr(fname, '/')) != NULL)
+ ++name;
+ else
+ name = filename;
+ ch = *name;
+ *name = '\0';
+ ++name;
+ XmuSnprintf(buf, len, "%s%s%c%s%s",
+ fname, app_resources.backupNamePrefix, ch, name,
+ app_resources.backupNameSuffix);
+ }
+ }
+ else
+ XmuSnprintf(buf, len, "%s%s",
+ filename, app_resources.backupNameSuffix);
+
+ return (strcmp(filename, buf) ? buf : NULL);
+}
+
+#if defined(USG) && !defined(CRAY)
+int rename (from, to)
+ char *from, *to;
+{
+ (void) unlink (to);
+ if (link (from, to) == 0) {
+ unlink (from);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+#endif
+
+/*ARGSUSED*/
+void
+SaveFile(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ DoSave(w, NULL, NULL);
+}
+
+/*ARGSUSED*/
+void
+DoSave(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ String name = GetString(filenamewindow);
+ String filename = ResolveName(name);
+ char buf[BUFSIZ];
+ FileAccess file_access;
+ xedit_flist_item *item;
+ Boolean exists;
+ Widget source = XawTextGetSource(textwindow);
+
+ if (!filename) {
+ XeditPrintf("Save: Can't resolve pathname -- nothing saved.\n");
+ Feep();
+ return;
+ }
+ else if (*name == '\0') {
+ XeditPrintf("Save: No filename specified -- nothing saved.\n");
+ Feep();
+ return;
+ }
+ else {
+ struct stat st;
+
+ if (stat(filename, &st) == 0 && !S_ISREG(st.st_mode)) {
+ XmuSnprintf(buf, sizeof(buf),
+ "Save: file %s is not a regular file -- nothing saved.\n",
+ name);
+ XeditPrintf(buf);
+ Feep();
+ return;
+ }
+ }
+
+ item = FindTextSource(NULL, filename);
+ if (item != NULL && item->source != source) {
+ if (!double_click || (dc_state && dc_state != DC_LOADED)) {
+ XmuSnprintf(buf, sizeof(buf),
+ "Save: file %s is already loaded, "
+ "Save again to unload it -- nothing saved.\n",
+ name);
+ XeditPrintf(buf);
+ Feep();
+ double_click = TRUE;
+ dc_state = DC_LOADED;
+ AddDoubleClickCallback(XawTextGetSource(textwindow), True);
+ return;
+ }
+ KillTextSource(item);
+ item = FindTextSource(source = XawTextGetSource(textwindow), NULL);
+ double_click = FALSE;
+ dc_state = 0;
+ }
+ else if (item && !(item->flags & CHANGED_BIT)) {
+ if (!double_click || (dc_state && dc_state != DC_SAVE)) {
+ XeditPrintf("Save: No changes need to be saved, "
+ "Save again to override.\n");
+ Feep();
+ double_click = TRUE;
+ dc_state = DC_SAVE;
+ AddDoubleClickCallback(XawTextGetSource(textwindow), True);
+ return;
+ }
+ double_click = FALSE;
+ dc_state = 0;
+ }
+
+ file_access = CheckFilePermissions(filename, &exists);
+ if (!item || strcmp(item->filename, filename)) {
+ if (file_access == WRITE_OK && exists) {
+ if (!double_click || (dc_state && dc_state != DC_CLOBBER)) {
+ XmuSnprintf(buf, sizeof(buf),
+ "Save: file %s already exists, "
+ "Save again to overwrite it -- nothing saved.\n",
+ name);
+ XeditPrintf(buf);
+ Feep();
+ double_click = TRUE;
+ dc_state = DC_CLOBBER;
+ AddDoubleClickCallback(XawTextGetSource(textwindow), True);
+ return;
+ }
+ double_click = FALSE;
+ dc_state = 0;
+ }
+ if (!item)
+ item = FindTextSource(source, NULL);
+ }
+
+ if (app_resources.enableBackups && exists) {
+ char backup_file[BUFSIZ];
+
+ if (makeBackupName(backup_file, filename, sizeof(backup_file)) == NULL
+ || rename(filename, backup_file) != 0) {
+ XmuSnprintf(buf, sizeof(buf),"error backing up file: %s\n",
+ filename);
+ XeditPrintf(buf);
+ }
+ }
+
+ switch( file_access = MaybeCreateFile(filename)) {
+ case NO_READ:
+ case READ_OK:
+ XmuSnprintf(buf, sizeof(buf),
+ "File %s could not be opened for writing.\n", name);
+ Feep();
+ break;
+ case WRITE_OK:
+ if ( XawAsciiSaveAsFile(source, filename) ) {
+ int i;
+ Arg args[1];
+ char label_buf[BUFSIZ];
+
+ /* Keep file protection mode */
+ if (item && item->mode)
+ chmod(filename, item->mode);
+
+ XmuSnprintf(label_buf, sizeof(label_buf),
+ "%s Read - Write", name);
+ XtSetArg(args[0], XtNlabel, label_buf);
+ for (i = 0; i < 3; i++)
+ if (XawTextGetSource(texts[i]) == source)
+ XtSetValues(labels[i], args, 1);
+
+ XmuSnprintf(buf, sizeof(buf), "Saved file: %s\n", name);
+
+ if (item && item->source != scratch) {
+ XtSetArg(args[0], XtNlabel, filename);
+ XtSetValues(item->sme, args, 1);
+
+ XtSetArg(args[0], XtNeditType, XawtextEdit);
+ XtSetValues(item->source, args, 1);
+
+ XtFree(item->name);
+ XtFree(item->filename);
+ item->name = XtNewString(name);
+ item->filename = XtNewString(filename);
+ item->flags = EXISTS_BIT;
+ }
+ else {
+ item = flist.itens[0];
+ XtRemoveCallback(scratch, XtNcallback, SourceChanged,
+ (XtPointer)item);
+ item->source = scratch =
+ XtVaCreateWidget("textSource", international ?
+ multiSrcObjectClass : asciiSrcObjectClass,
+ topwindow,
+ XtNtype, XawAsciiFile,
+ XtNeditType, XawtextEdit,
+ NULL, NULL);
+ ResetSourceChanged(item);
+ XtAddCallback(scratch, XtNcallback, SourceChanged,
+ (XtPointer)item);
+
+ item = AddTextSource(source, name, filename, EXISTS_BIT,
+ file_access);
+ XtAddCallback(item->source, XtNcallback, SourceChanged,
+ (XtPointer)item);
+ }
+ item->flags |= EXISTS_BIT;
+ ResetSourceChanged(item);
+ }
+ else {
+ XmuSnprintf(buf, sizeof(buf), "Error saving file: %s\n", name);
+ Feep();
+ }
+ break;
+ default:
+ XmuSnprintf(buf, sizeof(buf), "%s %s",
+ "Internal function MaybeCreateFile()",
+ "returned unexpected value.\n");
+ Feep();
+ break;
+ }
+
+ XeditPrintf(buf);
+}
+
+/*ARGSUSED*/
+void
+DoLoad(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ if (ReallyDoLoad(GetString(filenamewindow), ResolveName(NULL))) {
+ SwitchDirWindow(False);
+ XtSetKeyboardFocus(topwindow, textwindow);
+ }
+}
+
+static Bool
+ReallyDoLoad(char *name, char *filename)
+{
+ Arg args[5];
+ Cardinal num_args = 0;
+ char buf[BUFSIZ];
+ xedit_flist_item *item;
+ Widget source = XawTextGetSource(textwindow);
+
+ if (!filename) {
+ XeditPrintf("Load: Can't resolve pathname.\n");
+ Feep();
+ return (False);
+ }
+ else if (*name == '\0') {
+ XeditPrintf("Load: No file specified.\n");
+ Feep();
+ }
+ if ((item = FindTextSource(NULL, filename)) != NULL) {
+ SwitchTextSource(item);
+ return (True);
+ }
+ else {
+ struct stat st;
+
+ if (stat(filename, &st) == 0 && !S_ISREG(st.st_mode)) {
+ if (S_ISDIR(st.st_mode)) {
+ char path[BUFSIZ + 1];
+
+ strncpy(path, filename, sizeof(path) - 2);
+ path[sizeof(path) - 2] = '\0';
+ if (*path) {
+ if (path[strlen(path) - 1] != '/')
+ strcat(path, "/");
+ }
+ else
+ strcpy(path, "./");
+ XtSetArg(args[0], XtNlabel, "");
+ XtSetValues(dirlabel, args, 1);
+ SwitchDirWindow(True);
+ DirWindowCB(dirwindow, path, NULL);
+ return (False);
+ }
+ }
+ }
+
+ {
+ Boolean exists;
+ int flags;
+ FileAccess file_access;
+
+ switch( file_access = CheckFilePermissions(filename, &exists) ) {
+ case NO_READ:
+ if (exists)
+ XmuSnprintf(buf, sizeof(buf), "File %s, %s", name,
+ "exists, and could not be opened for reading.\n");
+ else
+ XmuSnprintf(buf, sizeof(buf), "File %s %s %s", name,
+ "does not exist, and",
+ "the directory could not be opened for writing.\n");
+
+ XeditPrintf(buf);
+ Feep();
+ return (False);
+ case READ_OK:
+ XtSetArg(args[num_args], XtNeditType, XawtextRead); num_args++;
+ XmuSnprintf(buf, sizeof(buf), "File %s opened READ ONLY.\n",
+ name);
+ break;
+ case WRITE_OK:
+ XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++;
+ XmuSnprintf(buf, sizeof(buf), "File %s opened read - write.\n",
+ name);
+ break;
+ default:
+ XmuSnprintf(buf, sizeof(buf), "%s %s",
+ "Internal function MaybeCreateFile()",
+ "returned unexpected value.\n");
+ XeditPrintf(buf);
+ Feep();
+ return (False);
+ }
+
+ XeditPrintf(buf);
+
+ if (exists) {
+ flags = EXISTS_BIT;
+ XtSetArg(args[num_args], XtNstring, filename); num_args++;
+ }
+ else {
+ flags = 0;
+ XtSetArg(args[num_args], XtNstring, NULL); num_args++;
+ }
+
+ source = XtVaCreateWidget("textSource", international ?
+ multiSrcObjectClass : asciiSrcObjectClass,
+ topwindow,
+ XtNtype, XawAsciiFile,
+ XtNeditType, XawtextEdit,
+ NULL, NULL);
+ XtSetValues(source, args, num_args);
+
+ item = AddTextSource(source, name, filename, flags, file_access);
+ XtAddCallback(item->source, XtNcallback, SourceChanged,
+ (XtPointer)item);
+ if (exists && file_access == WRITE_OK) {
+ struct stat st;
+
+ if (stat(filename, &st) == 0)
+ item->mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ SwitchTextSource(item);
+ ResetSourceChanged(item);
+ }
+
+ return (True);
+}
+
+/* Function Name: SourceChanged
+ * Description: A callback routine called when the source has changed.
+ * Arguments: w - the text source that has changed.
+ * client_data - xedit_flist_item associated with text buffer.
+ * call_data - NULL is unchanged
+ * Returns: none.
+ */
+/*ARGSUSED*/
+void
+SourceChanged(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ xedit_flist_item *item = (xedit_flist_item*)client_data;
+ Bool changed = (Bool)(long)call_data;
+
+ if (changed) {
+ if (item->flags & CHANGED_BIT)
+ return;
+ item->flags |= CHANGED_BIT;
+ }
+ else {
+ if (item->flags & CHANGED_BIT)
+ ResetSourceChanged(item);
+ return;
+ }
+
+ if (flist.pixmap) {
+ Arg args[1];
+ Cardinal num_args;
+ int i;
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNleftBitmap, flist.pixmap); ++num_args;
+ XtSetValues(item->sme, args, num_args);
+
+ for (i = 0; i < 3; i++)
+ if (XawTextGetSource(texts[i]) == item->source)
+ XtSetValues(labels[i], args, num_args);
+ }
+}
+
+/* Function Name: ResetSourceChanged.
+ * Description: Sets the source changed to FALSE, and
+ * registers a callback to set it to TRUE when
+ * the source has changed.
+ * Arguments: item - item with widget to register the callback on.
+ * Returns: none.
+ */
+
+void
+ResetSourceChanged(xedit_flist_item *item)
+{
+ Arg args[1];
+ Cardinal num_args;
+ int i;
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNleftBitmap, None); ++num_args;
+ XtSetValues(item->sme, args, num_args);
+
+ dc_state = 0;
+ double_click = FALSE;
+ for (i = 0; i < 3; i++) {
+ if (XawTextGetSource(texts[i]) == item->source)
+ XtSetValues(labels[i], args, num_args);
+ AddDoubleClickCallback(XawTextGetSource(texts[i]), False);
+ }
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNsourceChanged, False); ++num_args;
+ XtSetValues(item->source, args, num_args);
+
+ item->flags &= ~CHANGED_BIT;
+}
+
+/*ARGSUSED*/
+void
+KillFile(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ xedit_flist_item *item = FindTextSource(XawTextGetSource(textwindow), NULL);
+
+ if (item->source == scratch) {
+ Feep();
+ return;
+ }
+
+ if (item->flags & CHANGED_BIT) {
+ if (!double_click || (dc_state && dc_state != DC_KILL)) {
+ XeditPrintf("Kill: Unsaved changes. Kill again to override.\n");
+ Feep();
+ double_click = TRUE;
+ dc_state = DC_KILL;
+ AddDoubleClickCallback(XawTextGetSource(textwindow), True);
+ return;
+ }
+ double_click = FALSE;
+ dc_state = 0;
+ }
+ KillTextSource(item);
+}
+
+/*ARGSUSED*/
+void
+FindFile(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ char *string = GetString(filenamewindow);
+ char *slash = NULL;
+ XawTextBlock block;
+ XawTextPosition end = XawTextSourceScan(XawTextGetSource(filenamewindow),
+ 0, XawstAll, XawsdRight, 1, True);
+
+ if (string)
+ slash = strrchr(string, '/');
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ block.ptr = string;
+ block.length = slash ? slash - string + 1 : 0;
+
+ if (block.length != end)
+ XawTextReplace(filenamewindow, 0, end, &block);
+ XawTextSetInsertionPoint(filenamewindow, end);
+ XtSetKeyboardFocus(topwindow, filenamewindow);
+ line_edit = False;
+}
+
+/*ARGSUSED*/
+void
+LoadFile(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ if (line_edit)
+ LineEdit(textwindow);
+ else if (ReallyDoLoad(GetString(filenamewindow), ResolveName(NULL))) {
+ SwitchDirWindow(False);
+ XtSetKeyboardFocus(topwindow, textwindow);
+ }
+}
+
+/*ARGSUSED*/
+void
+CancelFindFile(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Arg args[1];
+ xedit_flist_item *item;
+
+ XtSetKeyboardFocus(topwindow, textwindow);
+
+ item = FindTextSource(XawTextGetSource(textwindow), NULL);
+
+ if (item->source != scratch)
+ XtSetArg(args[0], XtNstring, item->name);
+ else
+ XtSetArg(args[0], XtNstring, NULL);
+
+ XtSetValues(filenamewindow, args, 1);
+
+ if (XtIsManaged(XtParent(dirwindow)))
+ SwitchDirWindow(False);
+
+ line_edit = False;
+}
+
+static int
+compar(_Xconst void *a, _Xconst void *b)
+{
+ return (strcmp(*(char **)a, *(char **)b));
+}
+
+/*ARGSUSED*/
+void
+FileCompletion(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ XawTextBlock block;
+ String text;
+ int length;
+ char **matches, *save, *dir_name, *file_name, match[257];
+ unsigned n_matches, len, mlen, buflen;
+ DIR *dir;
+ Bool changed, slash = False, dot = False, has_dot = False;
+#define SM_NEVER 0
+#define SM_HINT 1
+#define SM_ALWAYS 2
+ int show_matches;
+
+ text = GetString(filenamewindow);
+
+ if (!text) {
+ Feep();
+ return;
+ }
+ else if (line_edit) {
+ Feep();
+ line_edit = 0;
+ }
+
+ {
+ XawTextPosition pos = XawTextGetInsertionPoint(w);
+ char *cslash = strchr(&text[pos], '/'), *cdot = strchr(&text[pos], '.');
+
+ if (cslash != NULL || cdot != NULL) {
+ if (cslash != NULL && (cdot == NULL || cdot > cslash)) {
+ length = cslash - text;
+ slash = True;
+ }
+ else {
+ length = cdot - text;
+ has_dot = True;
+ }
+ }
+ else
+ length = strlen(text);
+ }
+
+ if (*num_params == 1 && length == strlen(text)) {
+ switch (params[0][0]) {
+ case 'n': /* Never */
+ case 'N':
+ show_matches = SM_NEVER;
+ break;
+ case 'h': /* Hint */
+ case 'H':
+ show_matches = SM_HINT;
+ break;
+ case 'a': /* Always */
+ case 'A':
+ show_matches = SM_ALWAYS;
+ break;
+ default:
+ show_matches = SM_NEVER;
+ XtAppWarning(XtWidgetToApplicationContext(w),
+ "Bad argument to file-completion, "
+ "must be Never, Hint or Always");
+ break;
+ }
+ }
+ else
+ show_matches = SM_NEVER;
+
+ matches = NULL;
+ n_matches = buflen = 0;
+ save = XtMalloc(length + 1);
+ memmove(save, text, length);
+ save[length] = '\0';
+
+ if (save[0] == '~' && save[1]) {
+ char *slash2 = strchr(save, '/');
+ int nlen;
+
+ if (slash2) {
+ struct passwd *pw;
+ char home[BUFSIZ];
+ char *name;
+ int slen = strlen(save), diff = slash2 - save;
+
+ *slash2 = '\0';
+ name = save + 1;
+ if ((nlen = strlen(name)) != 0)
+ pw = getpwnam(name);
+ else
+ pw = getpwuid(getuid());
+
+ if (pw) {
+ char fname[BUFSIZ];
+ int hlen;
+
+ strncpy(home, pw->pw_dir, sizeof(home) - 1);
+ home[sizeof(home) - 1] = '\0';
+ hlen = strlen(home);
+ strncpy(fname, slash2 + 1, sizeof(fname) - 1);
+ fname[sizeof(fname) - 1] = '\0';
+ save = XtRealloc(save, slen - diff + hlen + 2);
+ (void)memmove(&save[hlen], slash2, slen - diff + 1);
+ (void)memmove(save, home, hlen);
+ save[hlen] = '/';
+ strcpy(&save[hlen + 1], fname);
+
+ /* expand directory */
+ block.length = strlen(save);
+ block.ptr = save;
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ XawTextReplace(filenamewindow, 0, length, &block);
+ XawTextSetInsertionPoint(filenamewindow, length = block.length);
+ }
+ else
+ *slash2 = '/';
+ }
+ }
+
+ if ((file_name = strrchr(save, '/')) != NULL) {
+ *file_name = '\0';
+ ++file_name;
+ dir_name = save;
+ if (!file_name[0])
+ slash = True;
+ if (!dir_name[0])
+ dir_name = "/";
+ }
+ else {
+ dir_name = ".";
+ dot = True;
+ file_name = save;
+ }
+ len = strlen(file_name);
+
+ if ((dir = opendir(dir_name)) != NULL) {
+ char path[BUFSIZ], *pptr;
+ struct dirent *ent;
+ int isdir = 0, first = 1, bytes;
+
+ XmuSnprintf(path, sizeof(path), "%s/", dir_name);
+ pptr = path + strlen(path);
+ bytes = sizeof(path) - (pptr - path) - 1;
+
+ mlen = 0;
+ match[0] = '\0';
+ (void)readdir(dir); /* "." */
+ (void)readdir(dir); /* ".." */
+ while ((ent = readdir(dir)) != NULL) {
+ unsigned d_namlen = strlen(ent->d_name);
+
+ if (d_namlen >= len && strncmp(ent->d_name, file_name, len) == 0) {
+ char *tmp = &(ent->d_name[len]), *mat = match;
+ struct stat st;
+ Bool is_dir = FALSE;
+
+ strncpy(pptr, ent->d_name, bytes);
+ pptr[bytes] = '\0';
+ if (stat(path, &st) != 0)
+ /* Should check errno, may be a broken symbolic link
+ * a directory with r-- permission, etc */
+ continue;
+ else if (first || show_matches != SM_NEVER) {
+ is_dir = S_ISDIR(st.st_mode);
+ }
+
+ if (first) {
+ strncpy(match, tmp, sizeof(match) - 1);
+ match[sizeof(match) - 2] = '\0';
+ mlen = strlen(match);
+ first = 0;
+ isdir = is_dir;
+ }
+ else {
+ while (*tmp && *mat && *tmp++ == *mat)
+ ++mat;
+ if (mlen > mat - match) {
+ mlen = mat - match;
+ match[mlen] = '\0';
+ }
+ }
+ if (show_matches != SM_NEVER) {
+ matches = (char **)XtRealloc((char*)matches, sizeof(char**)
+ * (n_matches + 1));
+ buflen += d_namlen + 1;
+ if (is_dir) {
+ matches[n_matches] = XtMalloc(d_namlen + 2);
+ strcpy(matches[n_matches], ent->d_name);
+ strcat(matches[n_matches], "/");
+ ++buflen;
+ }
+ else
+ matches[n_matches] = XtNewString(ent->d_name);
+ }
+ else if (mlen == 0 && n_matches >= 1) {
+ ++n_matches;
+ break;
+ }
+ ++n_matches;
+ }
+ }
+
+ closedir(dir);
+ changed = mlen != 0;
+
+ if (n_matches) {
+ Bool free_matches = True, add_slash = n_matches == 1 && isdir && !slash;
+
+ if (mlen && has_dot && match[mlen - 1] == '.')
+ --mlen;
+
+ if (mlen || add_slash) {
+ XawTextPosition pos;
+
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ if (mlen) {
+ pos = length;
+ block.length = mlen;
+ block.ptr = match;
+ XawTextReplace(filenamewindow, pos, pos, &block);
+ XawTextSetInsertionPoint(filenamewindow, pos + block.length);
+ }
+ if (add_slash) {
+ XawTextPosition actual = XawTextGetInsertionPoint(w);
+
+ pos = XawTextSourceScan(XawTextGetSource(w), 0, XawstAll,
+ XawsdRight, 1, True);
+ block.length = 1;
+ block.ptr = "/";
+ XawTextReplace(filenamewindow, pos, pos, &block);
+ if (actual == pos)
+ XawTextSetInsertionPoint(filenamewindow, pos + 1);
+ }
+ }
+ else if (n_matches != 1 || isdir) {
+ if (show_matches == SM_NEVER)
+ Feep();
+ }
+
+ if (show_matches != SM_NEVER) {
+ if (show_matches == SM_ALWAYS || (!changed && n_matches != 1)) {
+ char **list = NULL, *label;
+ int n_list;
+ Arg args[2];
+
+ XtSetArg(args[0], XtNlist, &list);
+ XtSetArg(args[1], XtNnumberStrings, &n_list);
+ XtGetValues(dirwindow, args, 2);
+
+ matches = (char **)XtRealloc((char*)matches, sizeof(char**)
+ * (n_matches + 2));
+ matches[n_matches++] = XtNewString("./");
+ matches[n_matches++] = XtNewString("../");
+ qsort(matches, n_matches, sizeof(char*), compar);
+ XtSetArg(args[0], XtNlist, matches);
+ XtSetArg(args[1], XtNnumberStrings, n_matches);
+ XtSetValues(dirwindow, args, 2);
+ if (n_list > 0
+ && (n_list != 1 || list[0] != XtName(dirwindow))) {
+ while (--n_list > -1)
+ XtFree(list[n_list]);
+ XtFree((char*)list);
+ }
+
+ label = ResolveName(dir_name);
+ XtSetArg(args[0], XtNlabel, label);
+ XtSetValues(dirlabel, args, 1);
+ SwitchDirWindow(True);
+ free_matches = False;
+ }
+ }
+ if (free_matches && matches) {
+ while (--n_matches > -1)
+ XtFree(matches[n_matches]);
+ XtFree((char*)matches);
+ }
+ }
+ else
+ Feep();
+ }
+ else
+ Feep();
+
+ XtFree(save);
+}
+
+/*ARGSUSED*/
+void
+DirWindowCB(Widget w, XtPointer user_data, XtPointer call_data)
+{
+ XawListReturnStruct *file_info = (XawListReturnStruct *)call_data;
+ char *dir_name, *string, path[BUFSIZ];
+ Arg args[2];
+
+ if (file_info == NULL)
+ string = (char *)user_data;
+ else
+ string = file_info->string;
+
+ XtSetArg(args[0], XtNlabel, &dir_name);
+ XtGetValues(dirlabel, args, 1);
+ if (*dir_name == '\0') {
+ strncpy(path, string, sizeof(path) - 1);
+ path[sizeof(path) - 1] = '\0';
+ }
+ else if (strcmp(dir_name, "/") == 0)
+ XmuSnprintf(path, sizeof(path), "/%s", string);
+ else
+ XmuSnprintf(path, sizeof(path), "%s/%s", dir_name, string);
+
+ if (*string && string[strlen(string) - 1] == '/') {
+ DIR *dir;
+
+ if ((dir = opendir(path)) != NULL) {
+ struct dirent *ent;
+ struct stat st;
+ unsigned d_namlen;
+ Bool isdir;
+ char **entries = NULL, **list = NULL;
+ int n_entries = 0, n_list = 0;
+ char *label, *pptr = path + strlen(path);
+ int bytes = sizeof(path) - (pptr - path) - 1;
+
+ while ((ent = readdir(dir)) != NULL) {
+ d_namlen = strlen(ent->d_name);
+ strncpy(pptr, ent->d_name, bytes);
+ pptr[bytes] = '\0';
+ if (stat(path, &st) != 0)
+ /* Should check errno, may be a broken symbolic link
+ * a directory with r-- permission, etc */
+ continue;
+ else
+ isdir = S_ISDIR(st.st_mode);
+
+ entries = (char **)XtRealloc((char*)entries, sizeof(char*)
+ * (n_entries + 1));
+ if (isdir) {
+ entries[n_entries] = XtMalloc(d_namlen + 2);
+ strcpy(entries[n_entries], ent->d_name);
+ strcat(entries[n_entries], "/");
+ }
+ else
+ entries[n_entries] = XtNewString(ent->d_name);
+ ++n_entries;
+ }
+ closedir(dir);
+
+ XtSetArg(args[0], XtNlist, &list);
+ XtSetArg(args[1], XtNnumberStrings, &n_list);
+ XtGetValues(dirwindow, args, 2);
+
+ if (n_entries == 0) {
+ entries = (char**)XtMalloc(sizeof(char*) * 2);
+ /* Directory has read but not execute permission? */
+ entries[n_entries++] = XtNewString("./");
+ entries[n_entries++] = XtNewString("../");
+ }
+ qsort(entries, n_entries, sizeof(char*), compar);
+ XtSetArg(args[0], XtNlist, entries);
+ XtSetArg(args[1], XtNnumberStrings, n_entries);
+ XtSetValues(dirwindow, args, 2);
+ if (n_list > 0
+ && (n_list != 1 || list[0] != XtName(dirwindow))) {
+ while (--n_list > -1)
+ XtFree(list[n_list]);
+ XtFree((char*)list);
+ }
+
+ *pptr = '\0';
+ if ((label = ResolveName(path)) == NULL) {
+ Feep();
+ label = path;
+ }
+ XtSetArg(args[0], XtNlabel, label);
+ XtSetValues(dirlabel, args, 1);
+
+ strncpy(path, label, sizeof(path) - 2);
+ if (*path && path[strlen(path) - 1] != '/')
+ strcat(path, "/");
+ XtSetArg(args[0], XtNstring, path);
+ XtSetValues(filenamewindow, args, 1);
+ XtSetKeyboardFocus(topwindow, filenamewindow);
+ XawTextSetInsertionPoint(filenamewindow, strlen(path));
+ }
+ else
+ Feep();
+ }
+ else {
+ (void)ReallyDoLoad(path, path);
+ SwitchDirWindow(False);
+ XtSetKeyboardFocus(topwindow, textwindow);
+ }
+}
diff --git a/hook.c b/hook.c
new file mode 100644
index 0000000..39f393c
--- /dev/null
+++ b/hook.c
@@ -0,0 +1,1234 @@
+/*
+ * Copyright (c) 1999 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/hook.c,v 1.10 2003/01/09 03:36:29 paulo Exp $ */
+
+/*
+ * This file is intended to be used to add all the necessary hooks to xedit
+ * emulate certain features of emacs (and other text editors) that are better
+ * kept only in xedit, to avoid unnecessary code in the Text Widget.
+ *
+ * The code here is not finished, and will probably be changed frequently.
+ */
+
+#include "xedit.h"
+#include "re.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/*
+ * Types
+ */
+typedef struct _ReplaceList {
+ char *word;
+ char *replace;
+ struct _ReplaceList *next;
+} ReplaceList;
+
+typedef enum {
+ SubstituteDisabled,
+ SubstituteAsk,
+ SubstituteNo,
+ SubstituteYes
+} SubstitutionState;
+
+typedef struct _EditInfo {
+ /* Xedit regex data */
+ re_cod regex;
+ re_mat mats[10];
+
+ /* Last command entered */
+ char command[128];
+
+ /* String and flags used to compile regex */
+ char pattern[64];
+ int flags;
+
+ /* Substitution buffer */
+ char subst[64];
+ int soff, slen, sref;
+
+ /* For interactive substitution */
+ int callback;
+ Widget widget;
+ char *text_line;
+ SubstitutionState state;
+ XawTextPosition from, to, start, end, first, last;
+
+ /* Use if need to allocate a buffer to pass the entire line to reexec */
+ char *line;
+ long lsize;
+
+ /* Buffer to prepare replacement, if needs to expand backreferences */
+ char *buffer;
+ long bsize;
+} EditInfo;
+
+/*
+ * Prototypes
+ */
+static void ActionHook(Widget, XtPointer, String, XEvent*, String*, Cardinal*);
+static void AutoReplaceHook(Widget, String, XEvent*);
+static Bool StartAutoReplace(void);
+static char *ReplacedWord(char*, char*);
+static void AutoReplace(Widget, XEvent*);
+static void AutoReplaceCallback(Widget, XtPointer, XtPointer);
+
+static void SubstituteHook(Widget w, String action, XEvent *event);
+static void SubstituteCallback(Widget, XtPointer, XtPointer);
+
+/*
+ * Initialization
+ */
+#define STRTBLSZ 11
+static ReplaceList *replace_list[STRTBLSZ];
+static EditInfo einfo;
+extern Widget scratch;
+
+/*
+ * Implementation
+ */
+Bool
+StartHooks(XtAppContext app)
+{
+ static Bool first_time = True;
+
+ if (first_time) {
+ StartAutoReplace();
+ (void)XtAppAddActionHook(app, ActionHook, NULL);
+ first_time = False;
+
+ return (True);
+ }
+ return (False);
+}
+
+/*ARGSUSED*/
+static void
+ActionHook(Widget w, XtPointer client_data, String action, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ AutoReplaceHook(w, action, event);
+ SubstituteHook(w, action, event);
+}
+
+/*** auto replace ***/
+struct {
+ Widget widget;
+ String text;
+ Cardinal length;
+ XawTextPosition left, right;
+ Bool replace;
+ Bool enabled;
+} auto_replace;
+
+static void
+AutoReplaceHook(Widget w, String action, XEvent *event)
+{
+ static Bool multiply;
+
+ if (w != textwindow || !auto_replace.enabled)
+ return;
+
+ if (auto_replace.widget != textwindow) {
+ if (auto_replace.replace) {
+ auto_replace.replace = False;
+ XtRemoveCallback(auto_replace.widget, XtNpositionCallback,
+ AutoReplaceCallback, NULL);
+ }
+ }
+ else if (strcmp(action, "multiply") == 0) {
+ multiply = True;
+ return;
+ }
+ else if (strcmp(action, "numeric") == 0) {
+ if (multiply)
+ return;
+ }
+ else if (strcmp(action, "insert-char") && strcmp(action, "newline") &&
+ strcmp(action, "newline-and-indent")) {
+ return;
+ }
+ multiply = False;
+
+ AutoReplace(w, event);
+}
+
+static Bool
+StartAutoReplace(void)
+{
+ Bool esc;
+ int len, llen, rlen, count = 0;
+ char ch, *tmp, *left, *right, *replace = app_resources.auto_replace;
+
+ if (!replace || !*replace)
+ return (False);
+
+ left = XtMalloc(llen = 256);
+ right = XtMalloc(rlen = 256);
+ while (*replace) {
+ /* skip white spaces */
+ while (*replace && isspace(*replace))
+ ++replace;
+ if (!*replace)
+ break;
+
+ /* read left */
+ tmp = replace;
+ while (*replace && !isspace(*replace))
+ ++replace;
+ len = replace - tmp;
+ if (len >= llen)
+ left = XtRealloc(left, llen = len + 1);
+ strncpy(left, tmp, len);
+ left[len] = '\0';
+
+ /* skip white spaces */
+ while (*replace && isspace(*replace))
+ ++replace;
+
+ /* read right */
+ len = 0;
+ esc = False;
+ while ((ch = *replace) != '\0') {
+ ++replace;
+ if (len + 2 >= rlen)
+ right = XtRealloc(right, rlen += 256);
+ if (ch == '\\') {
+ if (esc)
+ right[len++] = '\\';
+ esc = !esc;
+ continue;
+ }
+ else if (ch == '\n' && !esc)
+ break;
+ else
+ right[len++] = ch;
+ esc = False;
+ }
+ right[len] = '\0';
+
+ (void)ReplacedWord(left, right);
+ ++count;
+ }
+ XtFree(left);
+ XtFree(right);
+
+ return (auto_replace.enabled = count > 0);
+}
+
+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;
+
+ return (list->replace);
+}
+
+static void
+AutoReplace(Widget w, XEvent *event)
+{
+ static XComposeStatus compose = {NULL, 0};
+ KeySym keysym;
+ XawTextBlock block;
+ XawTextPosition left, right, pos;
+ Widget source;
+ int i, len, size;
+ char *str, buf[32], mb[sizeof(wchar_t)];
+
+ size = XLookupString((XKeyEvent*)event, mb, sizeof(mb), &keysym, &compose);
+
+ if (size != 1 || isalnum(*mb))
+ return;
+
+ source = XawTextGetSource(w);
+ right = XawTextGetInsertionPoint(w);
+ left = XawTextSourceScan(source, right, XawstWhiteSpace,
+ XawsdLeft, 1, False);
+
+ if (left < 0 || left == right)
+ return;
+
+ len = 0;
+ str = buf;
+ size = sizeof(buf);
+ pos = left;
+ while (pos < right) {
+ pos = XawTextSourceRead(source, pos, &block, right - pos);
+ for (i = 0; i < block.length; i++) {
+ if (block.format == FMT8BIT)
+ *mb = block.ptr[i];
+ else
+ wctomb(mb, ((wchar_t*)block.ptr)[i]);
+ str[len++] = *mb;
+ if (len + 2 >= size) {
+ if (str == buf)
+ str = XtMalloc(size += sizeof(buf));
+ else
+ str = XtRealloc(str, size += sizeof(buf));
+ }
+ }
+ }
+ str[len] = '\0';
+ if ((auto_replace.text = ReplacedWord(str, NULL)) != NULL) {
+ auto_replace.length = strlen(auto_replace.text);
+ auto_replace.left = left;
+ auto_replace.right = right;
+ auto_replace.replace = True;
+ XtAddCallback(auto_replace.widget = w, XtNpositionCallback,
+ AutoReplaceCallback, NULL);
+ }
+ if (str != buf)
+ XtFree(str);
+}
+
+/*ARGSUSED*/
+static void
+AutoReplaceCallback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ int i, inc;
+ XawTextBlock block, text;
+ char buffer[1024], mb[sizeof(wchar_t)];
+ XawTextPosition left, right, pos;
+
+ if (!auto_replace.replace || w != auto_replace.widget)
+ return;
+
+ XtRemoveCallback(auto_replace.widget, XtNpositionCallback,
+ AutoReplaceCallback, NULL);
+ auto_replace.replace = False;
+
+ inc = XawTextGetInsertionPoint(w) - auto_replace.right;
+ if (auto_replace.length + inc > sizeof(buffer))
+ block.ptr = XtMalloc(auto_replace.length + inc);
+ else
+ block.ptr = buffer;
+ memcpy(block.ptr, auto_replace.text, auto_replace.length);
+
+ block.length = auto_replace.length;
+ pos = left = auto_replace.right;
+ right = left + inc;
+ while (pos < right) {
+ pos = XawTextSourceRead(XawTextGetSource(w), pos, &text, inc);
+ for (i = 0; i < text.length; i++) {
+ if (text.format == FMT8BIT)
+ *mb = text.ptr[i];
+ else
+ wctomb(mb, ((wchar_t*)text.ptr)[i]);
+ block.ptr[block.length++] = *mb;
+ }
+ }
+
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+
+ if (XawTextReplace(w, auto_replace.left, auto_replace.right + inc,
+ &block) == XawEditDone)
+ XawTextSetInsertionPoint(w, auto_replace.left + block.length);
+
+ if (block.ptr != buffer)
+ XtFree(block.ptr);
+}
+
+/*ARGUSED*/
+void
+LineEditAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ XawTextBlock block;
+
+ if (international)
+ /* XXX FIXME */
+ return;
+
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ block.ptr = einfo.command;
+ block.length = strlen(einfo.command);
+
+ XawTextReplace(filenamewindow, 0,
+ XawTextLastPosition(filenamewindow), &block);
+ XtSetKeyboardFocus(topwindow, filenamewindow);
+ 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)
+{
+ /* Global usage variables */
+ XawTextPosition from, to, first, last, position, length, redisplay;
+ int replace, compile, ecode, nth, flags, count, etype;
+ char *command, *line, buffer[128];
+ XawTextBlock block;
+ Widget source;
+ XawTextScanDirection direction;
+ xedit_flist_item *item;
+
+ /* Variables used while parsing command */
+ int state, action, offset, icase, confirm;
+ long lfrom, lto, lfinc, ltinc, number;
+ char *ptr, *pstart, *pend, *rstart, *rend, *tmp;
+
+ /* Variables used in the search/replace loop */
+ int len;
+ XawTextPosition adjust = 0;
+
+ command = GetString(filenamewindow);
+ length = strlen(command);
+ if (length >= sizeof(einfo.command)) {
+ Feep();
+ return;
+ }
+
+ item = FindTextSource(XawTextGetSource(w), NULL);
+ source = item->source;
+ position = XawTextGetInsertionPoint(w);
+ first = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True);
+ last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True);
+ compile = redisplay = nth = count = confirm = 0;
+ direction = XawsdRight;
+ flags = RE_STARTEND;
+
+ /* Error types */
+#define T_NONE 0
+#define T_OPTION 1
+#define T_ICASE 2
+#define T_COMMAND 3
+#define T_REPLACE 4
+#define T_SEARCH 5
+#define T_BACKSLASH 6
+#define T_DIRECTION 7
+#define T_COMMA 8
+#define T_OFFSET 9
+#define T_INCREMENT 10
+#define T_NUMBER 11
+#define T_UNFINISHED 12
+#define T_RANGE 13
+#define T_BACKREF 14
+#define T_EDIT 15
+ etype = T_NONE;
+
+#define FAIL(code) { etype = code; goto fail; }
+
+ /* Value for the line value, anything else is the line number */
+#define L_FIRST -1
+#define L_CURRENT -2
+#define L_LAST -3
+ lfrom = L_FIRST;
+ lto = L_LAST;
+
+ /* Parsing states */
+#define E_FINC 0
+#define E_FROM 1
+#define E_COMMA 2
+#define E_TINC 3
+#define E_TO 4
+#define E_COMMAND 5
+#define E_REGEX 6
+#define E_SUBST 7
+#define E_OPTIONS 8
+ state = E_FROM; /* Beginning interpretation */
+
+ /* Known commands */
+#define A_SEARCH 0
+#define A_REPLACE 1
+ action = A_SEARCH;
+
+ /* Flag to replace all occurrences */
+#define O_ALL -1
+
+ number = 1;
+ lfinc = ltinc = 0;
+ icase = offset = 0;
+ pstart = pend = rstart = rend = NULL;
+
+ if (einfo.state != SubstituteDisabled) {
+ if (einfo.widget != w || strcmp(einfo.command, command)) {
+ einfo.widget = w;
+ einfo.state = SubstituteAsk;
+ }
+ else {
+ XawTextPosition s_start, s_end;
+
+ XawTextGetSelectionPos(w, &s_start, &s_end);
+ if (s_start != einfo.start || s_end != einfo.end)
+ einfo.state = SubstituteAsk;
+ confirm = replace = 1;
+ from = einfo.from;
+ to = einfo.to;
+ first = einfo.first;
+ last = einfo.last;
+ goto confirm_label;
+ }
+ }
+
+ /* Remember last command */
+ strcpy(einfo.command, command);
+
+ /* Loop parsing command */
+ for (ptr = einfo.command; *ptr;) {
+ switch (*ptr++) {
+ case 'c':
+ if (state != E_OPTIONS &&
+ state != E_COMMAND &&
+ state != E_REGEX)
+ FAIL(T_OPTION)
+ confirm = 1;
+ break;
+ case 'g':
+ if (state != E_OPTIONS &&
+ state != E_COMMAND &&
+ state != E_REGEX)
+ FAIL(T_OPTION)
+ offset = O_ALL;
+ break;
+ case 'i':
+ if (state != E_OPTIONS &&
+ state != E_COMMAND &&
+ state != E_REGEX &&
+ state != E_FROM)
+ FAIL(T_ICASE)
+ icase = 1;
+ break;
+ case 's':
+ if (state == E_FROM)
+ lfrom = lto = L_CURRENT;
+ else if (state == E_COMMA) {
+ lto = L_CURRENT;
+ ltinc = lfinc;
+ }
+ else if (state == E_TO)
+ lto = L_LAST;
+ else if (state == E_FINC) {
+ ltinc = lfinc;
+ lto = L_CURRENT;
+ }
+ else if (state != E_COMMAND && state != E_TINC)
+ FAIL(T_COMMAND)
+ action = A_REPLACE;
+ state = E_REGEX;
+ break;
+ case '?':
+ if (action == A_REPLACE)
+ FAIL(T_REPLACE)
+ case '/':
+ if (state == E_TINC)
+ state = action == A_REPLACE ? E_REGEX : E_FROM;
+ else if (state == E_COMMA || state == E_FINC) {
+ lto = L_LAST;
+ state = E_FROM;
+ }
+ else if (state == E_TO) {
+ if (ltinc == 0)
+ lto = L_LAST;
+ state = E_FROM;
+ }
+ else if (state == E_COMMAND)
+ state = E_FROM;
+ else if (state != E_REGEX &&
+ state != E_SUBST &&
+ state != E_FROM)
+ FAIL(T_SEARCH)
+ if (state != E_SUBST)
+ direction = ptr[-1] == '/' ? XawsdRight : XawsdLeft;
+ for (tmp = ptr; *tmp; tmp++) {
+ if (*tmp == '\\') {
+ if (*++tmp == '\0')
+ FAIL(T_BACKSLASH)
+ }
+ else if (*tmp == ptr[-1])
+ break;
+ }
+ if (state == E_REGEX) {
+ if (*tmp != ptr[-1])
+ FAIL(T_DIRECTION)
+ pstart = ptr;
+ pend = ptr = tmp;
+ state = E_SUBST;
+ }
+ else if (state == E_FROM) {
+ pstart = ptr;
+ pend = ptr = tmp;
+ state = E_OPTIONS;
+ if (*ptr)
+ ++ptr;
+ }
+ else { /* E_SUBST */
+ rstart = ptr;
+ rend = tmp;
+ state = E_OPTIONS;
+ ptr = tmp;
+ if (*ptr)
+ ++ptr;
+ }
+ break;
+ case ',':
+ if (state == E_FROM)
+ lfrom = L_FIRST;
+ else if (state == E_FINC)
+ lfrom = L_CURRENT;
+ else if (state != E_COMMA)
+ FAIL(T_COMMA)
+ state = E_TO;
+ break;
+ case '%':
+ if (state == E_FROM) {
+ lfrom = L_FIRST;
+ lto = L_LAST;
+ state = E_COMMAND;
+ }
+ else
+ FAIL(T_OFFSET)
+ break;
+ case '$':
+ if (state != E_TO)
+ FAIL(T_OFFSET)
+ lto = L_LAST;
+ state = E_COMMAND;
+ break;
+ case '.':
+ if (state == E_FROM) {
+ lfrom = L_CURRENT;
+ state = E_COMMA;
+ }
+ else if (state == E_TO) {
+ lto = L_CURRENT;
+ state = E_COMMAND;
+ }
+ else
+ FAIL(T_OFFSET)
+ break;
+ case '+':
+ if (state == E_FROM) {
+ lfinc = 1;
+ lfrom = L_CURRENT;
+ state = E_FINC;
+ }
+ else if (state == E_TO) {
+ ltinc = 1;
+ lto = L_CURRENT;
+ state = E_TINC;
+ }
+ else
+ FAIL(T_INCREMENT)
+ break;
+ case '-': case '^':
+ if (state == E_FROM) {
+ lfinc = -1;
+ lfrom = L_CURRENT;
+ state = E_FINC;
+ }
+ else if (state == E_TO) {
+ ltinc = -1;
+ lto = L_CURRENT;
+ state = E_TINC;
+ }
+ else
+ FAIL(T_INCREMENT)
+ number = -1;
+ break;
+ case ';':
+ if (state != E_FROM)
+ FAIL(T_OFFSET)
+ lfrom = L_CURRENT;
+ lto = L_LAST;
+ state = E_COMMAND;
+ break;
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ number = number * (ptr[-1] - '0');
+ while (isdigit(*ptr))
+ number = number * 10 + (*ptr++ - '0');
+ if (state == E_FROM) {
+ lfrom = number;
+ state = E_COMMA;
+ }
+ else if (state == E_FINC) {
+ lfinc = number;
+ state = E_COMMA;
+ }
+ else if (state == E_TO) {
+ lto = number;
+ state = E_COMMAND;
+ }
+ else if (state == E_TINC) {
+ ltinc = number;
+ state = E_COMMAND;
+ }
+ else if (state == E_OPTIONS && action == A_REPLACE)
+ offset = number - 1;
+ else
+ FAIL(T_NUMBER)
+ number = 1;
+ break;
+ case '\0':
+ if (state == E_OPTIONS)
+ break;
+ default:
+ FAIL(T_UNFINISHED)
+ }
+ }
+
+ replace = action == A_REPLACE;
+
+ switch (lfrom) {
+ case L_FIRST:
+ from = first;
+ break;
+ case L_LAST:
+ from = LSCAN(last, 1, False);
+ break;
+ case L_CURRENT:
+ if (lfinc <= 0)
+ from = LSCAN(position, -lfinc + 1, False);
+ else {
+ from = RSCAN(position, lfinc + 1, False);
+ from = LSCAN(from, 1, False);
+ }
+ break;
+ default:
+ from = RSCAN(first, lfrom, False);
+ from = LSCAN(from, 1, False);
+ break;
+ }
+ /* Just requesting to go to the numbered line */
+ if (state == E_COMMA || state == E_FINC) {
+ XawTextSetInsertionPoint(w, from);
+ return;
+ }
+
+ length = pend - pstart;
+ if (pstart == NULL || (replace && rstart == NULL) ||
+ length >= sizeof(einfo.pattern) - 1)
+ FAIL(T_UNFINISHED)
+
+ /* Need to (re)compile regular expression pattern? */
+ if ((!!(einfo.flags & RE_ICASE) ^ icase) ||
+ strlen(einfo.pattern) < length ||
+ strncmp(pstart, einfo.pattern, length)) {
+ compile = 1;
+ memcpy(einfo.pattern, pstart, length);
+ einfo.pattern[length] = '\0';
+ einfo.flags = icase ? RE_ICASE : 0;
+ }
+
+ /* Check range of lines to operate on */
+ switch (lto) {
+ case L_FIRST:
+ to = RSCAN(first, 1, True);
+ break;
+ case L_LAST:
+ to = last;
+ break;
+ case L_CURRENT:
+ if (ltinc < 0) {
+ to = LSCAN(position, -ltinc + 1, True);
+ to = RSCAN(to, 2, True);
+ }
+ else
+ to = RSCAN(position, ltinc + 1, True);
+ break;
+ default:
+ to = RSCAN(first, lto, True);
+ break;
+ }
+ if (from >= to)
+ FAIL(T_RANGE)
+
+ /* Set first and last position allowed to search/replace */
+ first = from;
+ last = to;
+
+ /* Check bounds to work on */
+ if (replace) {
+ int i, csubst;
+
+ /* Check number of required match results and remove/parse backslashes */
+ memcpy(einfo.subst, rstart, einfo.slen = rend - rstart);
+ einfo.sref = 0;
+ einfo.soff = offset;
+ for (i = 0; i < einfo.slen - 1; i++) {
+ if (einfo.subst[i] == '\\') {
+ csubst = -1;
+ switch (einfo.subst[i + 1]) {
+ case '0': csubst = '\0'; break;
+ case 'a': csubst = '\b'; break;
+ case 'b': csubst = '\b'; break;
+ case 'f': csubst = '\f'; break;
+ case 'n': csubst = '\n'; break;
+ case 'r': csubst = '\r'; break;
+ case 't': csubst = '\t'; break;
+ case 'v': csubst = '\v'; break;
+ 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';
+ break;
+ default:
+ csubst = einfo.subst[i + 1];
+ break;
+ }
+ if (csubst >= 0) {
+ memmove(einfo.subst + i, einfo.subst + i + 1,
+ einfo.slen - i);
+ einfo.subst[i] = csubst;
+ --einfo.slen;
+ ++i;
+ csubst = -1;
+ }
+ }
+ }
+ }
+ else if (einfo.widget != w) {
+ /* Just a flag for backward search */
+ einfo.from = last;
+ einfo.widget = w;
+ }
+
+ /* Compile pattern if required */
+ if (compile) {
+ refree(&einfo.regex);
+ if ((ecode = recomp(&einfo.regex, einfo.pattern, einfo.flags)) != 0)
+ goto print;
+ }
+
+ if (!replace && position >= first && position <= last) {
+ from = position;
+ /* The backwards repetition currently is only backwards when
+ * changing lines, so remember from where started, to also
+ * search in the first line. */
+ if (LSCAN(from, 1, False) == from) {
+ if (direction == XawsdLeft)
+ einfo.from = from;
+ }
+ else
+ flags |= RE_NOTBOL;
+ }
+ to = RSCAN(from, 1, True);
+
+ if (confirm) {
+ if (!replace)
+ FAIL(T_UNFINISHED)
+ einfo.widget = w;
+ einfo.state = SubstituteAsk;
+ einfo.from = from;
+ einfo.to = to;
+ einfo.first = first;
+ einfo.last = last;
+ }
+ else
+ einfo.state = SubstituteDisabled;
+
+confirm_label:
+ if (replace) {
+ redisplay = 1;
+ XawTextDisableRedisplay(w);
+ }
+
+ for (;;) {
+ if (confirm && einfo.state != SubstituteAsk) {
+ /* Restore state from previous call */
+ ecode = 0;
+ nth = einfo.soff;
+ /* einfo.mats should not have changed */
+ if (einfo.state == SubstituteYes) {
+ einfo.state = SubstituteAsk;
+ line = einfo.text_line;
+ goto substitute_label;
+ }
+ else {
+ ++nth;
+ einfo.state = SubstituteAsk;
+ from = einfo.from = einfo.end;
+ goto no_substitute_label;
+ }
+ }
+
+ /* Read or use a line of text inplace */
+ position = from;
+ length = to - from;
+ XawTextSourceRead(source, position, &block, to - position);
+ if (block.length >= length)
+ line = block.ptr;
+ else {
+ if (length > einfo.lsize) {
+ einfo.line = XtRealloc(einfo.line, to - from);
+ einfo.lsize = to - from;
+ }
+ memcpy(einfo.line, block.ptr, block.length);
+ length = block.length;
+ for (position += length; position < to; position += length) {
+ XawTextSourceRead(source, position, &block, to - position);
+ memcpy(einfo.line + length, block.ptr, block.length);
+ length += block.length;
+ }
+ line = einfo.line;
+ }
+
+ /* Execute expression */
+ einfo.mats[0].rm_so = 0;
+ einfo.mats[0].rm_eo = to - from - !(from == to || to == last);
+ ecode = reexec(&einfo.regex, line,
+ einfo.sref + 1, &einfo.mats[0], flags);
+
+ if (replace && einfo.mats[0].rm_so == einfo.mats[0].rm_eo)
+ /* Ignore empty matches */
+ ecode = RE_NOMATCH;
+
+ if (ecode == 0 && confirm &&
+ (einfo.soff == O_ALL || nth == einfo.soff)) {
+ einfo.end = from + einfo.mats[0].rm_eo;
+ einfo.start = from + einfo.mats[0].rm_so;
+ XawTextSetInsertionPoint(w, einfo.end);
+ XawTextSetSelection(w, einfo.start, einfo.end);
+
+ einfo.state = SubstituteAsk;
+ einfo.from = from;
+ einfo.to = to;
+ einfo.first = first;
+ einfo.last = last;
+ einfo.text_line = line;
+ break;
+ }
+
+substitute_label:
+ if (ecode == 0) {
+ from += einfo.mats[0].rm_so;
+ len = einfo.mats[0].rm_eo - einfo.mats[0].rm_so;
+
+ /* Found match */
+ if (replace) {
+ /* If not replacing all ocurrences, or if not
+ * at the correct offset */
+ if (einfo.soff != O_ALL && nth < einfo.soff) {
+ from += len;
+ ++nth;
+ continue;
+ }
+
+ /* Do the substitution */
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ if (einfo.sref) {
+ /* Hard way */
+ int i, ref, xlen;
+
+ for (i = length = 0; i < einfo.slen; i++) {
+ if (length + 2 >= einfo.bsize) {
+ einfo.bsize = einfo.bsize + 1024;
+ einfo.buffer = XtRealloc(einfo.buffer, einfo.bsize);
+ }
+ if (einfo.subst[i] == '\\') {
+ ++i;
+ if (einfo.subst[i] >= '1' && einfo.subst[i] <= '9') {
+ ref = einfo.subst[i] - '0';
+ xlen = einfo.mats[ref].rm_eo -
+ einfo.mats[ref].rm_so;
+ if (xlen < 0)
+ /* Oops, something went wrong... */
+ FAIL(T_BACKREF)
+ if (length + xlen >= einfo.bsize) {
+ einfo.bsize += xlen + 1024 - (xlen % 1024);
+ einfo.buffer = XtRealloc(einfo.buffer,
+ einfo.bsize);
+ }
+ memcpy(einfo.buffer + length,
+ line + einfo.mats[ref].rm_so, xlen);
+ length += xlen;
+ }
+ else {
+ einfo.buffer[length++] = einfo.subst[i - 1];
+ einfo.buffer[length++] = einfo.subst[i];
+ }
+ }
+ else
+ einfo.buffer[length++] = einfo.subst[i];
+ }
+ block.ptr = einfo.buffer;
+ block.length = length;
+ }
+ else {
+ block.ptr = einfo.subst;
+ block.length = length = einfo.slen;
+ }
+ adjust = length - len;
+ if (XawTextReplace(w, from, from + len, &block) != XawEditDone)
+ FAIL(T_EDIT)
+ last += adjust;
+ to += adjust;
+ from += length;
+
+no_substitute_label:
+ if (einfo.soff != O_ALL) {
+ nth = 0;
+ to = RSCAN(from, 1, True);
+ from = LSCAN(to, 1, False);
+ if (to == last) {
+ XawTextSetInsertionPoint(w, from);
+ break;
+ }
+ }
+ else
+ flags |= RE_NOTBOL;
+ }
+ else {
+ XawTextSetInsertionPoint(w, from + len);
+ XawTextSetSelection(w, from, from + len);
+ break;
+ }
+ }
+ else if (ecode == RE_NOMATCH) {
+ nth = 0;
+
+ /* Try again in the next/previous line */
+ if (direction == XawsdLeft) {
+ from = LSCAN(to - 1, 1 + (from != to), False);
+ if (einfo.from <= first) {
+ Feep();
+ if (++count > 1) {
+ XawTextSetInsertionPoint(w, position);
+ XawTextUnsetSelection(w);
+ break;
+ }
+ from = LSCAN(last, 1, False);
+ }
+ to = RSCAN(from, 1, True);
+ /* Can use einfo.from because replace is only done forward */
+ einfo.from = from;
+ }
+ else {
+ if (to >= last) {
+ Feep();
+ if (replace || ++count > 1) {
+ XawTextSetInsertionPoint(w, position);
+ XawTextUnsetSelection(w);
+ einfo.state = SubstituteDisabled;
+ confirm = 0;
+ break;
+ }
+ to = first;
+ }
+ from = LSCAN(to + 1, 1, False);
+ to = RSCAN(from, 1, True);
+ }
+
+ /* Reset flags now */
+ flags = RE_STARTEND;
+ }
+ else
+ goto print;
+ }
+
+ if (redisplay)
+ XawTextEnableRedisplay(w);
+ /* If replacing not interatively return to the edit window after finished */
+ if (replace && !confirm) {
+ Arg args[1];
+
+ XtSetKeyboardFocus(topwindow, textwindow);
+ if (item->source != scratch)
+ XtSetArg(args[0], XtNstring, item->name);
+ else
+ XtSetArg(args[0], XtNstring, NULL);
+ XtSetValues(filenamewindow, args, 1);
+ }
+ return;
+
+print:
+ if (redisplay)
+ XawTextEnableRedisplay(w);
+
+ strcpy(buffer, "Regex error: ");
+ length = 13;
+ reerror(ecode, &einfo.regex,
+ buffer + length, sizeof(buffer) - length - 2);
+ strcat(buffer, "\n");
+ XeditPrintf(buffer);
+ refree(&einfo.regex);
+ einfo.state = SubstituteDisabled;
+ Feep();
+ return;
+
+
+fail:
+ if (etype != T_NONE) {
+ switch (etype) {
+ case T_OPTION:
+ ptr = "Option needs a command";
+ break;
+ case T_ICASE:
+ ptr = "Icase needs an command defined or none for search";
+ break;
+ case T_COMMAND:
+ ptr = "Command incorrectly specified";
+ break;
+ case T_REPLACE:
+ ptr = "Can only search backwards";
+ break;
+ case T_SEARCH:
+ ptr = "Badly placed search/replace specifier";
+ break;
+ case T_BACKSLASH:
+ ptr = "A single backslash cannot be the last command character";
+ break;
+ case T_DIRECTION:
+ ptr = "Regular expression must be separeted by / or ? not both";
+ break;
+ case T_COMMA:
+ ptr = "Badly placed comma";
+ break;
+ case T_OFFSET:
+ ptr = "Badly placed line offset specifier";
+ break;
+ case T_INCREMENT:
+ ptr = "Badly placed line offset increment specifier";
+ break;
+ case T_NUMBER:
+ ptr = "Numeric argument not expected";
+ break;
+ case T_UNFINISHED:
+ ptr = "Unfinished command";
+ break;
+ case T_RANGE:
+ ptr = "Bad line range";
+ break;
+ case T_BACKREF:
+ /* This may be an internal re error, but most likely the
+ * user asked for something like "s/re0(re1)re2/\2/" */
+ ptr = "Bad backreference";
+ break;
+ case T_EDIT:
+ ptr = "Failed to replace text";
+ break;
+ default:
+ ptr = "Unknown error";
+ break;
+ }
+ XmuSnprintf(buffer, sizeof(buffer), "Error: %s.\n", ptr);
+ XeditPrintf(buffer);
+ }
+ if (redisplay)
+ XawTextEnableRedisplay(w);
+ einfo.state = SubstituteDisabled;
+ Feep();
+}
+
+static void
+SubstituteHook(Widget w, String action, XEvent *event)
+{
+ if (w != filenamewindow)
+ return;
+
+ if (line_edit && einfo.state == SubstituteAsk) {
+ if (strcmp(action, "newline") == 0 ||
+ strcmp(action, "load-file") == 0)
+ einfo.state = SubstituteAsk;
+ else if (strcmp(action, "insert-char") == 0) {
+ static XComposeStatus compose = {NULL, 0};
+ KeySym keysym;
+ char mb[sizeof(wchar_t)];
+
+ if (XLookupString((XKeyEvent*)event, mb, sizeof(mb),
+ &keysym, &compose) == 1) {
+ if (*mb == 'y' || *mb == 'Y')
+ einfo.state = SubstituteYes;
+ else if (*mb == 'n' || *mb == 'N')
+ einfo.state = SubstituteNo;
+ else
+ einfo.state = SubstituteDisabled;
+
+ if (einfo.state != SubstituteDisabled) {
+ einfo.callback = 1;
+ XtAddCallback(filenamewindow, XtNpositionCallback,
+ SubstituteCallback, NULL);
+ }
+ }
+ }
+ else if (strcmp(action, "cancel-find-file") == 0)
+ einfo.state = SubstituteDisabled;
+ }
+ if (einfo.state == SubstituteDisabled && einfo.callback) {
+ einfo.callback = 0;
+ XtRemoveCallback(filenamewindow, XtNpositionCallback,
+ SubstituteCallback, NULL);
+ }
+}
+
+/*ARGSUSED*/
+static void
+SubstituteCallback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ XawTextBlock block;
+
+ einfo.callback = 0;
+ XtRemoveCallback(filenamewindow, XtNpositionCallback,
+ SubstituteCallback, NULL);
+
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ block.ptr = einfo.command;
+ block.length = strlen(einfo.command);
+
+ XawTextReplace(filenamewindow, 0,
+ XawTextLastPosition(filenamewindow), &block);
+
+ LineEdit(einfo.widget);
+}
diff --git a/ispell.c b/ispell.c
new file mode 100644
index 0000000..1066cb6
--- /dev/null
+++ b/ispell.c
@@ -0,0 +1,2217 @@
+/*
+ * Copyright (c) 1999 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/ispell.c,v 1.19 2002/10/19 20:04:20 herrb Exp $ */
+
+#include "xedit.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SimpleMenu.h>
+
+#define RECEIVE 1
+#define SEND 2
+
+#define CHECK 0
+#define ADD 1
+#define REMOVE 2
+
+#define ASIS 1
+#define UNCAP 2
+
+/*
+ * Types
+ */
+#define UNDO_DEPTH 16
+typedef struct _ispell_undo {
+ char *undo_str;
+ int undo_count;
+ XawTextPosition undo_pos;
+ Boolean repeat; /* two (misspelled?) words together */
+ Boolean terse;
+ int format; /* remember text formatting style */
+ struct _ispell_undo *next, *prev;
+} ispell_undo;
+
+typedef struct _ispell_dict {
+ Widget sme;
+ char *wchars;
+ struct _ispell_dict *next;
+} ispell_dict;
+
+#define TEXT 0
+#define HTML 1
+struct _ispell_format {
+ char *name;
+ int value;
+ Widget sme;
+};
+
+static struct _ispell_format ispell_format[] = {
+ {"text", TEXT},
+ {"html", HTML},
+};
+
+struct _ispell {
+ Widget shell, form, mispelled, repeated, word, replacement, text,
+ suggestions, viewport, list, commands, replace, status,
+ replaceAll, undo, ignore, ignoreAll, add, addUncap, suspend,
+ cancel, check, look, terse, options, dict, dictMenu,
+ format, formatMenu;
+
+ Widget ascii, source;
+ XtInputId id;
+ int pid, ifd[2], ofd[2];
+ XawTextPosition left, right;
+ char *item;
+ Bool lock;
+ Bool repeat;
+ Bool checkit;
+ int stat;
+ char *buf;
+ int bufsiz;
+ int buflen;
+ char sendbuf[1024];
+ char sentbuf[1024];
+
+ int undo_depth;
+ ispell_undo *undo_head, *undo_base;
+ char *undo_for;
+
+ char *wchars;
+ char *cmd;
+ char *skip;
+ char *command;
+ Boolean terse_mode, undo_terse_mode;
+ char *guess_label, *miss_label, *root_label, *none_label, *eof_label,
+ *compound_label, *ok_label, *repeat_label, *working_label, *look_label;
+ char *look_cmd;
+ char *words_file;
+
+ char *dictionary;
+ char *dict_list;
+ ispell_dict *dict_info;
+
+ int format_mode; /* to undo correctly */
+ char *formatting;
+ struct _ispell_format *format_info;
+};
+
+typedef struct _ReplaceList {
+ char *word;
+ char *replace;
+ struct _ReplaceList *next;
+} ReplaceList;
+
+typedef struct _IgnoreList {
+ char *word;
+ int add;
+ struct _IgnoreList *next;
+} IgnoreList;
+
+/*
+ * Prototypes
+ */
+static void AddIspell(Widget, XtPointer, XtPointer);
+static void ChangeDictionaryIspell(Widget, XtPointer, XtPointer);
+static void ChangeFormatIspell(Widget, XtPointer, XtPointer);
+static void CheckIspell(Widget, XtPointer, XtPointer);
+static void IgnoreIspell(Widget, XtPointer, XtPointer);
+static Bool InitIspell(void);
+static void IspellCheckUndo(void);
+static int IspellConvertHtmlAmp(char*);
+static Bool IspellDoIgnoredWord(char*, int, int);
+static Bool IspellIgnoredWord(char*, int, int);
+static void IspellInputCallback(XtPointer, int*, XtInputId*);
+static void IspellKillUndoBuffer(void);
+static Bool IspellReceive(void);
+static char *IspellReplacedWord(char*, char*);
+static int IspellSend(void);
+static void IspellSetSelection(XawTextPosition, XawTextPosition);
+static void IspellSetRepeated(Bool);
+static void IspellSetSensitive(Bool);
+static void IspellSetStatus(char*);
+static void IspellSetTerseMode(Bool);
+static Bool IspellStartProcess(void);
+static Bool IspellEndProcess(Bool, Bool);
+static void LookIspell(Widget, XtPointer, XtPointer);
+static void PopdownIspell(Widget, XtPointer, XtPointer);
+static void ReplaceIspell(Widget, XtPointer, XtPointer);
+static void RevertIspell(Widget, XtPointer, XtPointer);
+static void SelectIspell(Widget, XtPointer, XtPointer);
+static void ToggleTerseIspell(Widget, XtPointer, XtPointer);
+#ifndef SIGNALRETURNSINT
+static void timeout_signal(int);
+static void (*old_timeout)(int);
+#else
+static int timeout_signal(int);
+static int (*old_timeout)(int);
+#endif
+static void UndoIspell(Widget, XtPointer, XtPointer);
+
+Bool _XawTextSrcUndo(TextSrcObject, XawTextPosition*);
+
+/*
+ * Initialization
+ */
+static struct _ispell ispell;
+
+#define RSTRTBLSZ 23
+#define ISTRTBLSZ 71
+static ReplaceList *replace_list[RSTRTBLSZ];
+static IgnoreList *ignore_list[ISTRTBLSZ];
+
+#ifndef XtCStatus
+#define XtCStatus "Status"
+#endif
+
+#define Offset(field) XtOffsetOf(struct _ispell, field)
+static XtResource resources[] = {
+ {"wordChars", "Chars", XtRString, sizeof(char*),
+ Offset(wchars), XtRString, ""},
+ {"ispellCommand", "CommandLine", XtRString, sizeof(char*),
+ Offset(cmd), XtRString, "/usr/local/bin/ispell"},
+ {"terseMode", "Terse", XtRBoolean, sizeof(Boolean),
+ Offset(terse_mode), XtRImmediate, (XtPointer)False},
+ {"guessLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(guess_label), XtRString, "Guess"},
+ {"missLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(miss_label), XtRString, "Miss"},
+ {"rootLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(root_label), XtRString, "Root:"},
+ {"noneLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(none_label), XtRString, "None"},
+ {"compoundLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(compound_label), XtRString, "Compound"},
+ {"okLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(ok_label), XtRString, "Ok"},
+ {"eofLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(eof_label), XtRString, "End Of File"},
+ {"repeatLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(repeat_label), XtRString, "Repeat"},
+ {"workingLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(working_label), XtRString, "..."},
+ {"lookLabel", XtCStatus, XtRString, sizeof(String),
+ Offset(look_label), XtRString, "Look"},
+ {"lookCommand", "CommandLine", XtRString, sizeof(char*),
+ Offset(look_cmd), XtRString, "/usr/bin/egrep -i"},
+ {"wordsFile", "Words", XtRString, sizeof(char*),
+ Offset(words_file), XtRString, "/usr/share/dict/words"},
+ {"dictionary", "Dictionary", XtRString, sizeof(char*),
+ Offset(dictionary), XtRString, "american"},
+ {"dictionaries", "Dictionary", XtRString, sizeof(char*),
+ Offset(dict_list), XtRString, "american americanmed+ english"},
+ {"formatting", "TextFormat", XtRString, sizeof(char*),
+ Offset(formatting), XtRString, "text"},
+};
+#undef Offset
+
+#ifdef NO_LIBC_I18N
+static int
+ToLower(int ch)
+{
+ char buf[2];
+
+ *buf = ch;
+ XmuNCopyISOLatin1Lowered(buf, buf, sizeof(buf));
+
+ return (*buf);
+}
+
+static int
+ToUpper(int ch)
+{
+ char buf[2];
+
+ *buf = ch;
+ XmuNCopyISOLatin1Uppered(buf, buf, sizeof(buf));
+
+ return (*buf);
+}
+
+static int
+IsLower(int ch)
+{
+ char upbuf[2];
+ char lobuf[2];
+
+ *upbuf = *lobuf = ch;
+ XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
+ XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));
+
+ return (*lobuf != *upbuf && ch == *lobuf);
+}
+
+static int
+IsUpper(int ch)
+{
+ char upbuf[2];
+ char lobuf[2];
+
+ *upbuf = *lobuf = ch;
+ XmuNCopyISOLatin1Lowered(lobuf, lobuf, sizeof(lobuf));
+ XmuNCopyISOLatin1Uppered(upbuf, upbuf, sizeof(upbuf));
+
+ return (*lobuf != *upbuf && ch == *upbuf);
+}
+#else
+#define ToLower tolower
+#define ToUpper toupper
+#define IsLower islower
+#define IsUpper isupper
+#endif
+
+/*
+ * Implementation
+ */
+#ifdef STDERR_FILENO
+# define WRITES(s) write(STDERR_FILENO, s, strlen(s))
+#else
+# define WRITES(s) write(fileno(stderr), s, strlen(s))
+#endif
+
+/*ARGSUSED*/
+#ifndef SIGNALRETURNSINT
+static void
+timeout_signal(int unused)
+{
+ int olderrno = errno;
+
+ WRITES("Warning: Timeout waiting ispell process to die.\n");
+ kill(ispell.pid, SIGTERM);
+ errno = olderrno;
+}
+#else
+static int
+timeout_signal(int unused)
+{
+ int olderrno = errno;
+
+ WRITES("Warning: Timeout waiting ispell process to die.\n");
+ kill(ispell.pid, SIGTERM);
+
+ errno = olderrno;
+ return (0);
+}
+#endif
+
+static void
+IspellSetSelection(XawTextPosition left, XawTextPosition right)
+{
+ /* Try to make sure the selected word is completely visible */
+ XawTextSetInsertionPoint(ispell.ascii, right);
+ XawTextSetInsertionPoint(ispell.ascii, left);
+ XawTextSetSelection(ispell.ascii, left, right);
+}
+
+static void
+IspellSetStatus(char *label)
+{
+ Arg args[1];
+
+ XtSetArg(args[0], XtNlabel, label);
+ XtSetValues(ispell.status, args, 1);
+}
+
+static void
+IspellSetRepeated(Bool state)
+{
+ static char *mispelled, *repeated;
+ Arg args[1];
+
+ if (mispelled == NULL) {
+ XtSetArg(args[0], XtNlabel, &mispelled);
+ XtGetValues(ispell.mispelled, args, 1);
+ mispelled = XtNewString(mispelled);
+ }
+ if (repeated == NULL) {
+ XtSetArg(args[0], XtNlabel, &repeated);
+ XtGetValues(ispell.repeated, args, 1);
+ repeated = XtNewString(repeated);
+ }
+ XtSetSensitive(ispell.replaceAll, !state);
+ XtSetSensitive(ispell.ignoreAll, !state);
+ XtSetSensitive(ispell.add, !state);
+ XtSetSensitive(ispell.addUncap, !state);
+ if (!state) {
+ XtSetArg(args[0], XtNlabel, mispelled);
+ XtSetValues(ispell.mispelled, args, 1);
+ }
+ else {
+ XtSetArg(args[0], XtNlabel, repeated);
+ XtSetValues(ispell.mispelled, args, 1);
+ }
+}
+
+static void
+IspellSetSensitive(Bool state)
+{
+ XtSetSensitive(ispell.replace, state);
+ XtSetSensitive(ispell.replaceAll, state);
+ XtSetSensitive(ispell.ignore, state);
+ XtSetSensitive(ispell.ignoreAll, state);
+ XtSetSensitive(ispell.add, state);
+ XtSetSensitive(ispell.addUncap, state);
+}
+
+static void
+IspellSetTerseMode(Bool mode)
+{
+ Arg args[1];
+
+ XtSetArg(args[0], XtNstate, ispell.terse_mode = mode);
+ XtSetValues(ispell.terse, args, 1);
+ write(ispell.ofd[1], mode ? "!\n" : "%\n", 2);
+}
+
+static void
+IspellCheckUndo(void)
+{
+ ispell_undo *undo = XtNew(ispell_undo);
+
+ if (ispell.undo_for && strcmp(ispell.undo_for, ispell.dictionary)) {
+ XeditPrintf("Undo: Dictionary changed. Previous undo information lost.\n");
+ IspellKillUndoBuffer();
+ Feep();
+ }
+
+ undo->next = NULL;
+ undo->repeat = False;
+ undo->terse = ispell.undo_terse_mode;
+ undo->format = ispell.format_mode;
+ if ((undo->prev = ispell.undo_head) != NULL)
+ undo->prev->next = undo;
+ else
+ undo->prev = NULL;
+ ++ispell.undo_depth;
+ if (!ispell.undo_base) {
+ ispell.undo_base = undo;
+ XtSetSensitive(ispell.undo, True);
+ }
+ else if (ispell.undo_depth > UNDO_DEPTH) {
+ ispell_undo *tmp;
+
+ if (ispell.undo_base->undo_str)
+ XtFree(ispell.undo_base->undo_str);
+ tmp = ispell.undo_base->next;
+ XtFree((char*)ispell.undo_base);
+ tmp->prev = NULL;
+ ispell.undo_base = tmp;
+ ispell.undo_depth = UNDO_DEPTH;
+ }
+ ispell.undo_head = undo;
+}
+
+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);
+
+ list = XtNew(ReplaceList);
+ list->word = XtNewString(word);
+ list->replace = XtNewString(replace);
+ list->next = replace_list[ii];
+ replace_list[ii] = list;
+
+ return (list->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);
+ }
+
+ if (cmd != ADD)
+ return (False);
+
+ list = XtNew(IgnoreList);
+ list->word = XtNewString(word);
+ list->add = add;
+ list->next = ignore_list[ii];
+ ignore_list[ii] = list;
+
+ return (True);
+}
+
+static Bool
+IspellIgnoredWord(char *word, int cmd, int add)
+{
+ if (add != UNCAP && IspellDoIgnoredWord(word, cmd, add))
+ return (True);
+
+ /* add/remove uncapped word to/of list,
+ * or cheks for correct capitalization */
+ if (add == UNCAP || cmd == CHECK) {
+ unsigned char *str = (unsigned char*)word;
+ unsigned char string[1024];
+ Bool upper, status;
+ int i;
+
+ status = True;
+ upper = IsUpper(*str);
+ *string = upper ? ToLower(*str) : *str;
+ if (*str)
+ str++;
+ if (IsLower(*str))
+ upper = False;
+ for (i = 1; *str && i < sizeof(string) - 1; i++, str++) {
+ if (upper && IsLower(*str))
+ status = False;
+ else if (!upper && IsUpper(*str))
+ status = False;
+ string[i] = ToLower(*str);
+ }
+ string[i] = '\0';
+
+ if ((cmd != CHECK || status) &&
+ IspellDoIgnoredWord((char*)string, cmd, add))
+ return (True);
+ }
+
+ return (False);
+}
+
+/*ARGSUSED*/
+static Bool
+IspellReceive(void)
+{
+ int i, len, old_len;
+ Arg args[2];
+ char *str, *end, **list, **old_list;
+ char *tmp, word[1024];
+ int j;
+
+ if (ispell.lock || ispell.stat != RECEIVE)
+ return (False);
+
+ while (1) { /* read the entire line */
+ if (ispell.buflen >= ispell.bufsiz - 1)
+ ispell.buf = XtRealloc(ispell.buf, ispell.bufsiz += BUFSIZ);
+ if ((len = read(ispell.ifd[0], &ispell.buf[ispell.buflen],
+ ispell.bufsiz - ispell.buflen - 1)) <= 0)
+ break;
+ ispell.buflen += len;
+ }
+ if (ispell.buflen <= 0)
+ return (False);
+ len = 0;
+ i = ispell.buflen - 1;
+ while (i >= 0 && ispell.buf[i] == '\n') {
+ ++len;
+ --i;
+ }
+ if (len < 2 - ((ispell.terse_mode && i == -1) || ispell.buf[0] == '@'))
+ return (False);
+ ispell.buf[ispell.buflen - len] = '\0';
+ ispell.buflen = 0;
+
+ if ((tmp = strchr(ispell.sendbuf, '\n')) != NULL)
+ *tmp = '\0';
+
+ switch (ispell.buf[0]) {
+ case '&': /* MISS */
+ case '?': /* GUESS */
+ str = strchr(&ispell.buf[2], ' ');
+ if (!ispell.checkit) {
+ *str = '\0';
+ XtSetArg(args[0], XtNlabel, &ispell.buf[2]);
+ XtSetValues(ispell.word, args, 1);
+ }
+ ++str;
+ list = NULL;
+ str = strchr(str, ':') + 1;
+ for (i = 0; ; i++) {
+ end = strchr(str, ',');
+ if (end) *end = '\0';
+ if ((i % 16) == 0)
+ list = (char**)XtRealloc((char*)list, (i + 16) * sizeof(char*));
+ tmp = word;
+ for (j = 1; j < sizeof(word) && str[j]; j++) {
+ if (str[j] == '+')
+ continue;
+ else if (str[j] == '-' && str[j+1] != '-' && str[j-1] != '-') {
+ char *p, string[256];
+ int k, l;
+
+ for (l = 0, k = j + 1; str[k] != '+' && str[k] != '-'
+ && str[k] && l < sizeof(string) - 1; k++, l++)
+ string[l] = str[k];
+ string[l] = '\0';
+ *tmp = '\0';
+ if (l && (p = strstr(word, string)) != NULL) {
+ char *sav = p;
+
+ while ((p = strstr(p + l, string)) != NULL)
+ sav = p;
+ p = sav;
+ if (strcmp(p, string) == 0) {
+ tmp = p;
+ j = k - 1;
+ }
+ else
+ *tmp++ = '-';
+ }
+ else
+ *tmp++ = '-';
+ }
+ else
+ *tmp++ = str[j];
+ }
+ *tmp = '\0';
+ list[i] = XtNewString(word);
+
+ if (end) str = end + 1;
+ else break;
+ }
+ len = i + 1;
+
+ XtSetArg(args[0], XtNlist, &old_list);
+ XtSetArg(args[1], XtNnumberStrings, &old_len);
+ XtGetValues(ispell.list, args, 2);
+
+ ispell.item = NULL;
+ if ((str = IspellReplacedWord(&ispell.buf[2], NULL)) != NULL)
+ for (i = 0; i < len; i++) {
+ if (strcmp(list[i], str) == 0) {
+ ispell.item = list[i];
+ break;
+ }
+ }
+ else
+ ispell.item = list[i = 0];
+ if (!ispell.item) {
+ list = (char**)XtRealloc((char*)list, (len + 1) * sizeof(char*));
+ ispell.item = list[i] = XtNewString(str);
+ ++len;
+ }
+
+ XtSetArg(args[0], XtNlist, list);
+ XtSetArg(args[1], XtNnumberStrings, len);
+ XtSetValues(ispell.list, args, 2);
+
+ XtSetSensitive(ispell.list, True);
+ if (!ispell.checkit)
+ XawListHighlight(ispell.list, i);
+
+ if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
+ while (--old_len > -1)
+ XtFree(old_list[old_len]);
+ XtFree((char*)old_list);
+ }
+
+ if (!ispell.checkit) {
+ XtSetArg(args[0], XtNstring, ispell.item);
+ XtSetValues(ispell.text, args, 1);
+ IspellSetSelection(ispell.left, ispell.right);
+ if (ispell.repeat)
+ IspellSetRepeated(ispell.repeat = False);
+ }
+
+ IspellSetStatus(ispell.buf[0] == '?' ?
+ ispell.guess_label : ispell.miss_label);
+ ispell.undo_terse_mode = ispell.terse_mode;
+ ispell.format_mode = ispell.format_info->value;
+ ispell.lock = True;
+ break;
+ case '#': /* NONE */
+ case '-': /* COMPOUND */
+ case '+': /* ROOT */
+ check_label:
+ str = &ispell.sendbuf[1];
+ if (!ispell.checkit) {
+ XtSetArg(args[0], XtNlabel, str);
+ XtSetValues(ispell.word, args, 1);
+ }
+
+ XtSetArg(args[0], XtNlist, &old_list);
+ XtSetArg(args[1], XtNnumberStrings, &old_len);
+ XtGetValues(ispell.list, args, 2);
+ ispell.item = NULL;
+
+ list = (char**)XtMalloc(sizeof(char**));
+ if ((tmp = IspellReplacedWord(str, NULL)) != NULL)
+ str = tmp;
+ if (tmp == NULL && ispell.buf[0] == '#')
+ list[0] = XtNewString("");
+ else
+ list[0] = XtNewString(str);
+
+ XtSetArg(args[0], XtNlist, list);
+ XtSetArg(args[1], XtNnumberStrings, 1);
+ XtSetValues(ispell.list, args, 2);
+
+ if (tmp == NULL && ispell.buf[0] == '#') {
+ XawListUnhighlight(ispell.list);
+ XtSetSensitive(ispell.list, False);
+ }
+ else {
+ XtSetSensitive(ispell.list, True);
+ if (!ispell.checkit)
+ XawListHighlight(ispell.list, 0);
+ }
+ if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
+ while (--old_len > -1)
+ XtFree(old_list[old_len]);
+ XtFree((char*)old_list);
+ }
+
+ if (!ispell.checkit) {
+ XtSetArg(args[0], XtNstring, str);
+ XtSetValues(ispell.text, args, 1);
+ IspellSetSelection(ispell.left, ispell.right);
+ if (ispell.repeat)
+ IspellSetRepeated(ispell.repeat = False);
+ }
+
+ ispell.undo_terse_mode = ispell.terse_mode;
+ ispell.format_mode = ispell.format_info->value;
+ ispell.lock = True;
+ if (ispell.buf[0] == '+') {
+ if ((tmp = strchr(&ispell.buf[2], '\n')) != NULL)
+ *tmp = '\0';
+ XmuSnprintf(word, sizeof(word), "%s %s",
+ ispell.root_label, &ispell.buf[2]);
+ IspellSetStatus(word);
+ }
+ else
+ IspellSetStatus(ispell.buf[0] == '#' ? ispell.none_label :
+ ispell.buf[0] == '-' ? ispell.compound_label :
+ ispell.ok_label);
+ break;
+ case '*': /* OK */
+ case '\0': /* when running in terse mode */
+ if (!ispell.checkit)
+ (void)IspellIgnoredWord(&ispell.sendbuf[1], ADD, 0);
+ else
+ goto check_label;
+ ispell.lock = False;
+ break;
+ case '@': /* Ispell banner */
+ /* it only happens when the dictionary is changed */
+ if (!ispell.repeat) {
+ XawTextPosition left, right;
+
+ ispell.stat = SEND;
+ while (IspellSend() == 0)
+ ;
+ /* word chars may have changed */
+ XawTextGetSelectionPos(ispell.ascii, &left, &right);
+ if (left != ispell.left || right != ispell.right) {
+ XtSetArg(args[0], XtNstring, &ispell.sendbuf[1]);
+ XtSetValues(ispell.text, args, 1);
+ IspellSetSelection(ispell.left, ispell.right);
+ }
+ ispell.checkit = True;
+ }
+ else {
+ IspellSetStatus(ispell.repeat_label);
+ ispell.undo_terse_mode = ispell.terse_mode;
+ ispell.format_mode = ispell.format_info->value;
+ ispell.lock = True;
+ return (True);
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown ispell command '%c'\n", ispell.buf[0]);
+ return (False);
+ }
+
+ if (!ispell.lock && !ispell.checkit) {
+ ispell.stat = SEND;
+ while (IspellSend() == 0)
+ ;
+ }
+
+ return (True);
+}
+
+static int
+IspellConvertHtmlAmp(char *buf)
+{
+ int len, ch = '?';
+
+ /* this function is static, so I can do it */
+ *strchr(++buf, ';') = '\0';
+
+ len = strlen(buf);
+ if (len == 0)
+ return ('&');
+ if (len > 1) {
+ if (strcasecmp(&buf[1], "lt") == 0)
+ ch = '<';
+ else if (strcasecmp(&buf[1], "gt") == 0)
+ ch = '>';
+ else if (strcasecmp(&buf[1], "nbsp") == 0)
+ ch = ' ';
+ else if (strcasecmp(&buf[1], "amp") == 0)
+ ch = '&';
+ else if (strcasecmp(&buf[1], "quot") == 0)
+ ch = '"';
+ else if (*buf == '#') {
+ char *tmp;
+
+ if (len == 1);
+ return ('?');
+ ch = strtol(&buf[1], &tmp, 10);
+ if (*tmp)
+ fprintf(stderr, "Warning: bad html interpreting '&#' mark.\n");
+ }
+ else if (strcmp(&buf[1], "acute") == 0) {
+ switch (*buf) {
+ case 'a': ch = 'á'; break;
+ case 'e': ch = 'é'; break;
+ case 'i': ch = 'í'; break;
+ case 'o': ch = 'ó'; break;
+ case 'u': ch = 'ú'; break;
+ case 'A': ch = 'Á'; break;
+ case 'E': ch = 'É'; break;
+ case 'I': ch = 'Í'; break;
+ case 'O': ch = 'Ó'; break;
+ case 'U': ch = 'Ú'; break;
+ }
+ }
+ else if (strcmp(&buf[1], "grave") == 0) {
+ switch (*buf) {
+ case 'a': ch = 'à'; break;
+ case 'e': ch = 'è'; break;
+ case 'i': ch = 'ì'; break;
+ case 'o': ch = 'ò'; break;
+ case 'u': ch = 'ù'; break;
+ case 'A': ch = 'À'; break;
+ case 'E': ch = 'È'; break;
+ case 'I': ch = 'Ì'; break;
+ case 'O': ch = 'Ò'; break;
+ case 'U': ch = 'Ù'; break;
+ }
+ }
+ else if (strcmp(&buf[1], "tilde") == 0) {
+ switch (*buf) {
+ case 'a': ch = 'ã'; break;
+ case 'o': ch = 'õ'; break;
+ case 'n': ch = 'ñ'; break;
+ case 'A': ch = 'ã'; break;
+ case 'O': ch = 'Õ'; break;
+ case 'N': ch = 'Ñ'; break;
+ }
+ }
+ else if (strcmp(&buf[1], "circ") == 0) {
+ switch (*buf) {
+ case 'a': ch = 'â'; break;
+ case 'e': ch = 'ê'; break;
+ case 'i': ch = 'î'; break;
+ case 'o': ch = 'ô'; break;
+ case 'u': ch = 'û'; break;
+ case 'A': ch = 'Â'; break;
+ case 'E': ch = 'Ê'; break;
+ case 'I': ch = 'Î'; break;
+ case 'O': ch = 'Ô'; break;
+ case 'U': ch = 'Û'; break;
+ }
+ }
+ else if (strcmp(&buf[1], "cedil") == 0) {
+ switch (*buf) {
+ case 'c': ch = 'ç'; break;
+ case 'C': ch = 'Ç'; break;
+ }
+ }
+ /* add more cases here */
+ }
+
+ return (ch);
+}
+
+/*ARGSUSED*/
+static int
+IspellSend(void)
+{
+ XawTextPosition position, old_left, pos;
+ XawTextBlock block;
+ int i, len, spaces, nls;
+ Bool nl, html, inside_html;
+ char ampbuf[32];
+ int amplen;
+
+ if (ispell.lock || ispell.stat != SEND)
+ return (-1);
+
+ len = 1;
+ ispell.sendbuf[0] = '^'; /* don't evaluate following characters as commands */
+
+ spaces = nls = 0;
+
+ html = ispell.format_info->value == HTML;
+ inside_html = False;
+ amplen = 0;
+
+ /* skip non word characters */
+ pos = position = ispell.right;
+ nl = False;
+ while (1) {
+ Bool done = False;
+ char mb[sizeof(wchar_t)];
+
+ retry_html_space:
+ position = XawTextSourceRead(ispell.source, position,
+ &block, BUFSIZ);
+ if (block.length == 0) { /* end of file */
+ ispell.stat = 0;
+ ispell.lock = True;
+ XawTextSetInsertionPoint(ispell.ascii, ispell.right);
+ XawTextUnsetSelection(ispell.ascii);
+ IspellSetSensitive(False);
+ IspellSetStatus(ispell.eof_label);
+ return (-1);
+ }
+ for (i = 0; i < block.length; i++) {
+ if (international)
+ wctomb(mb, ((wchar_t*)block.ptr)[i]);
+ else
+ *mb = block.ptr[i];
+ if (amplen) {
+ if (amplen + 2 >= sizeof(ampbuf)) {
+ if (!ispell.terse_mode)
+ fprintf(stderr, "Warning: error interpreting '&' mark.\n");
+ amplen = 0;
+ position = pos + 1;
+ goto retry_html_space;
+ }
+ else if ((ampbuf[amplen++] = *mb) == ';') {
+ int ch;
+
+ ampbuf[amplen] = '\0';
+ ch = IspellConvertHtmlAmp(ampbuf);
+ amplen = 0;
+ if (isalpha(ch) ||
+ (ch && strchr(ispell.wchars, ch))) {
+ /* interpret it again */
+ ispell.right = pos;
+ i = 0;
+ done = True;
+ break;
+ }
+ else if ((ch == '\n' || isspace(ch)) && spaces >= 0)
+ ++spaces;
+ else
+ spaces = -1;
+ }
+ }
+ else if (html && *mb == '&') {
+ ampbuf[amplen++] = *mb;
+ pos = block.firstPos + i;
+ continue;
+ }
+ else if ((!html || !inside_html) && (isalpha(*mb) ||
+ (*mb && strchr(ispell.wchars, *mb)))) {
+ done = True;
+ break;
+ }
+ else if (!html && *mb == '\n') {
+ nl = True;
+ if (++nls > 1 && (!html || !inside_html))
+ spaces = -1;
+ else if (spaces >= 0)
+ ++spaces;
+ }
+ else if (nl) {
+ nl = False;
+ if (*mb && strchr(ispell.skip, *mb)) {
+ position = ispell.right =
+ XawTextSourceScan(ispell.source, ispell.right + i,
+ XawstEOL, XawsdRight, 1, False);
+ i = 0;
+ break;
+ }
+ else if (spaces >= 0 && isspace(*mb))
+ ++spaces;
+ else
+ spaces = -1;
+ }
+ else if (html && inside_html) {
+ if (*mb == '>')
+ inside_html = False;
+ }
+ else if (html && *mb == '<')
+ inside_html = True;
+ else if (spaces >= 0 && (isspace(*mb) || (html && *mb == '\n')))
+ ++spaces;
+ else
+ spaces = -1;
+ }
+
+ ispell.right += i;
+ if (done)
+ break;
+ }
+
+ old_left = ispell.left;
+
+ /* read a word */
+ position = ispell.left = ispell.right;
+ while (1) {
+ Bool done = False;
+ char mb[sizeof(wchar_t)];
+
+ retry_html_word:
+ position = XawTextSourceRead(ispell.source, position,
+ &block, BUFSIZ);
+ if (block.length == 0 && len == 1) { /* end of file */
+ ispell.stat = 0;
+ ispell.lock = True;
+ XawTextSetInsertionPoint(ispell.ascii, ispell.right);
+ XawTextUnsetSelection(ispell.ascii);
+ IspellSetSensitive(False);
+ IspellSetStatus(ispell.eof_label);
+ return (-1);
+ }
+ for (i = 0; i < block.length; i++) {
+ if (international)
+ wctomb(mb, ((wchar_t*)block.ptr)[i]);
+ else
+ *mb = block.ptr[i];
+ if (amplen) {
+ if (amplen + 2 >= sizeof(ampbuf)) {
+ if (!ispell.terse_mode)
+ fprintf(stderr, "Warning: error interpreting '&' mark.\n");
+ amplen = 0;
+ position = pos + 1;
+ if (strchr(ispell.wchars, '&')) {
+ if (len + 1 >= sizeof(ispell.sendbuf) - 1) {
+ done = True;
+ fprintf(stderr, "Warning: word is too large!\n");
+ break;
+ }
+ ispell.sendbuf[len++] = '&';
+ goto retry_html_word;
+ }
+ else {
+ ispell.right = position;
+ i = 0;
+ done = True;
+ break;
+ }
+ }
+ else if ((ampbuf[amplen++] = *mb) == ';') {
+ int ch;
+
+ ampbuf[amplen] = '\0';
+ ch = IspellConvertHtmlAmp(ampbuf);
+ amplen = 0;
+ if (!isalpha(ch) &&
+ (!ch || !strchr(ispell.wchars, ch))) {
+ ispell.right = pos;
+ i = 0;
+ done = True;
+ break;
+ }
+ *mb = ch;
+ }
+ else
+ continue;
+ }
+ else if (html && *mb == '&') {
+ ampbuf[amplen++] = *mb;
+ pos = block.firstPos + i;
+ continue;
+ }
+ else if (!isalpha(*mb) && (!*mb || !strchr(ispell.wchars, *mb))) {
+ done = True;
+ break;
+ }
+ ispell.sendbuf[len] = *mb;
+ if (++len >= sizeof(ispell.sendbuf) - 1) {
+ done = True;
+ fprintf(stderr, "Warning: word is too large!\n");
+ break;
+ }
+ }
+ ispell.right += i;
+ if (done || block.length == 0)
+ break;
+ }
+
+ ispell.sendbuf[len] = '\0';
+
+ if (spaces > 0 && spaces <= 32 && strcmp(ispell.sendbuf, ispell.sentbuf) == 0) {
+ Arg args[2];
+ int old_len;
+ char **list, **old_list;
+ char label[sizeof(ispell.sendbuf) + sizeof(ispell.sentbuf) + 32];
+
+ strcpy(label, &ispell.sendbuf[1]);
+ for (i = 0; i < spaces; i++)
+ label[len + i - 1] = ' ';
+ strcpy(&label[len + i - 1], &ispell.sendbuf[1]);
+ XtSetArg(args[0], XtNlabel, label);
+ XtSetValues(ispell.word, args, 1);
+
+ XtSetArg(args[0], XtNstring, &ispell.sendbuf[1]);
+ XtSetValues(ispell.text, args, 1);
+
+ XtSetArg(args[0], XtNlist, &old_list);
+ XtSetArg(args[1], XtNnumberStrings, &old_len);
+ XtGetValues(ispell.list, args, 2);
+ list = (char**)XtMalloc(sizeof(char**));
+ list[0] = XtNewString(&ispell.sendbuf[1]);
+ XtSetArg(args[0], XtNlist, list);
+ XtSetArg(args[1], XtNnumberStrings, 1);
+ XtSetValues(ispell.list, args, 2);
+ XtSetSensitive(ispell.list, True);
+ XawListHighlight(ispell.list, 0);
+ if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
+ while (--old_len > -1)
+ XtFree(old_list[old_len]);
+ XtFree((char*)old_list);
+ }
+
+ IspellSetRepeated(True);
+ IspellSetSelection(old_left, ispell.right);
+ IspellSetStatus(ispell.repeat_label);
+ ispell.repeat = ispell.lock = True;
+
+ return (1);
+ }
+ strcpy(ispell.sentbuf, ispell.sendbuf);
+
+ if (len <= 2 || IspellIgnoredWord(&ispell.sendbuf[1], CHECK, 0))
+ return (0);
+
+ ispell.sendbuf[len++] = '\n';
+
+ write(ispell.ofd[1], ispell.sendbuf, len);
+
+ ispell.stat = RECEIVE;
+
+ return (1);
+}
+
+/*ARGSUSED*/
+static void
+IspellInputCallback(XtPointer closure, int *source, XtInputId *id)
+{
+ if (ispell.right < 0) {
+ int len;
+ char buf[1024];
+
+ ispell.right = XawTextGetInsertionPoint(ispell.ascii);
+ ispell.right = XawTextSourceScan(ispell.source, ispell.right,
+ XawstEOL, XawsdLeft, 1, True);
+ len = read(ispell.ifd[0], buf, sizeof(buf));
+ if (strncmp(buf, "@(#)", 4) == 0) {
+ Arg args[1];
+
+ buf[len - 1] = '\0';
+ XtSetArg(args[0], XtNtitle, &buf[5]);
+ XtSetValues(ispell.shell, args, 1);
+ }
+ else
+ fprintf(stderr, "Error: is ispell talking with me?\n");
+ IspellSetTerseMode(ispell.terse_mode);
+ while (IspellSend() == 0)
+ ;
+ }
+ else if (ispell.source)
+ IspellReceive();
+}
+
+/*ARGSUSED*/
+void
+IspellCallback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ Cardinal zero = 0;
+
+ IspellAction(textwindow, NULL, NULL, &zero);
+}
+
+/*ARGSUSED*/
+void
+IspellAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Arg args[3];
+ Cardinal num_args;
+ char **strs, **list;
+ int n_strs;
+ Bool first_time = InitIspell();
+
+ if (*num_params == 1 && (params[0][0] == 'e' || params[0][0] == 'E')) {
+ PopdownIspell(w, (XtPointer)True, NULL);
+ return;
+ }
+
+ if (!XtIsSubclass(w, textWidgetClass) || ispell.source) {
+ Feep();
+ return;
+ }
+
+ ispell.source = XawTextGetSource(ispell.ascii = w);
+
+ if (first_time) {
+ /* let the user choose the better position for the ispell window */
+ Dimension width, height, b_width;
+ Position x, y, max_x, max_y;
+
+ x = y = -1;
+ if (event) {
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ x = event->xbutton.x_root;
+ y = event->xbutton.y_root;
+ break;
+ case KeyPress:
+ case KeyRelease:
+ x = event->xkey.x_root;
+ y = event->xkey.y_root;
+ break;
+ }
+ }
+ if (x < 0 || y < 0) {
+ Window r, c;
+ int rx, ry, wx, wy;
+ unsigned mask;
+
+ XQueryPointer(XtDisplay(ispell.shell), XtWindow(ispell.shell),
+ &r, &c, &rx, &ry, &wx, &wy, &mask);
+ x = rx;
+ y = ry;
+ }
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNwidth, &width); num_args++;
+ XtSetArg(args[num_args], XtNheight, &height); num_args++;
+ XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
+ XtGetValues(ispell.shell, args, num_args);
+
+ width += b_width << 1;
+ height += b_width << 1;
+
+ x -= (Position)(width >> 1);
+ if (x < 0)
+ x = 0;
+ if (x > (max_x = (Position)(XtScreen(w)->width - width)))
+ x = max_x;
+
+ y -= (Position)(height >> 1);
+ if (y < 0)
+ y = 0;
+ if (y > (max_y = (Position)(XtScreen(w)->height - height)))
+ y = max_y;
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNx, x); num_args++;
+ XtSetArg(args[num_args], XtNy, y); num_args++;
+ XtSetValues(ispell.shell, args, num_args);
+ }
+
+ if (ispell.repeat)
+ IspellSetRepeated(False);
+ ispell.lock = ispell.repeat = ispell.checkit = False;
+ ispell.stat = SEND;
+
+ IspellSetSensitive(True);
+ XtSetSensitive(ispell.undo, False);
+
+ XtSetArg(args[0], XtNlabel, "");
+ XtSetValues(ispell.word, args, 1);
+
+ XtSetArg(args[0], XtNstring, "");
+ XtSetValues(ispell.text, args, 1);
+
+ XtSetArg(args[0], XtNlist, &strs);
+ XtSetArg(args[1], XtNnumberStrings, &n_strs);
+ XtGetValues(ispell.list, args, 2);
+
+ list = (char**)XtMalloc(sizeof(char**));
+ list[0] = XtNewString("");
+ XtSetArg(args[0], XtNlist, list);
+ XtSetArg(args[1], XtNnumberStrings, 1);
+ XtSetValues(ispell.list, args, 2);
+
+ if (n_strs > 1 || (XtName(ispell.list) != strs[0])) {
+ while (--n_strs > -1)
+ XtFree(strs[n_strs]);
+ XtFree((char*)strs);
+ }
+
+ IspellSetStatus(ispell.working_label);
+
+ if (!ispell.pid)
+ (void)IspellStartProcess();
+ else {
+ ispell.right = XawTextGetInsertionPoint(ispell.ascii);
+ ispell.right = XawTextSourceScan(ispell.source, ispell.right,
+ XawstEOL, XawsdLeft, 1, True);
+ while (IspellSend() == 0)
+ ;
+ }
+
+ XtPopup(ispell.shell, XtGrabExclusive);
+ XtSetKeyboardFocus(ispell.shell, ispell.text);
+}
+
+static Bool
+IspellStartProcess(void)
+{
+ if (!ispell.pid) {
+ int len;
+ char *command;
+
+ ispell.source = XawTextGetSource(ispell.ascii);
+
+ len = strlen(ispell.cmd) + strlen(ispell.dictionary) +
+ strlen(ispell.wchars) + 16;
+ command = XtMalloc(len);
+ XmuSnprintf(command, len, "%s -a -d '%s' -w '%s'",
+ ispell.cmd, ispell.dictionary, ispell.wchars);
+
+ pipe(ispell.ifd);
+ pipe(ispell.ofd);
+ if ((ispell.pid = fork()) == 0) {
+ close(0);
+ close(1);
+ dup2(ispell.ofd[0], 0);
+ dup2(ispell.ifd[1], 1);
+ close(ispell.ofd[0]);
+ close(ispell.ofd[1]);
+ close(ispell.ifd[0]);
+ close(ispell.ifd[1]);
+ execl("/bin/sh", "sh", "-c", command, (void *)NULL);
+ exit(-127);
+ }
+ else if (ispell.pid < 0) {
+ fprintf(stderr, "Cannot fork\n");
+ exit(1);
+ }
+ ispell.buf = XtMalloc(ispell.bufsiz = BUFSIZ);
+ ispell.right = -1;
+ ispell.id = XtAppAddInput(XtWidgetToApplicationContext(ispell.shell),
+ ispell.ifd[0], (XtPointer)XtInputReadMask,
+ IspellInputCallback, NULL);
+ fcntl(ispell.ifd[0], F_SETFL, O_NONBLOCK);
+ }
+ else
+ return (False);
+
+ return (True);
+}
+
+/*ARGSUSED*/
+static void
+PopdownIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ (void)IspellEndProcess((Bool)(long)client_data, True);
+ XtPopdown(ispell.shell);
+ *ispell.sentbuf = '\0';
+}
+
+static Bool
+IspellEndProcess(Bool killit, Bool killundo)
+{
+ ispell.source = NULL;
+
+ if (ispell.pid) {
+ IgnoreList *il, *pil, *nil;
+ int i;
+
+ /* 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;
+ }
+ else
+ il = il->next;
+ pil = il;
+ }
+ }
+ write(ispell.ofd[1], "#\n", 2); /* save dictionary */
+
+ if (killit) {
+ ReplaceList *rl, *prl;
+
+ XtRemoveInput(ispell.id);
+
+ close(ispell.ofd[0]);
+ close(ispell.ofd[1]);
+ close(ispell.ifd[0]);
+ close(ispell.ifd[1]);
+
+ /* if something goes wrong, we don't want to block here forever */
+ old_timeout = signal(SIGALRM, timeout_signal);
+ alarm(10);
+ waitpid(ispell.pid, NULL, 0);
+ alarm(0);
+ signal(SIGALRM, old_timeout);
+
+ ispell.pid = 0;
+ if (ispell.buf)
+ 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;
+ }
+ }
+
+ if (killundo)
+ IspellKillUndoBuffer();
+ }
+ else
+ return (False);
+
+ return (True);
+}
+
+static void
+IspellKillUndoBuffer(void)
+{
+ ispell_undo *undo, *pundo;
+
+ undo = pundo = ispell.undo_base;
+ while (undo) {
+ undo = undo->next;
+ if (pundo->undo_str)
+ XtFree(pundo->undo_str);
+ XtFree((char*)pundo);
+ pundo = undo;
+ }
+ ispell.undo_base = ispell.undo_head = NULL;
+ ispell.undo_for = NULL;
+ ispell.undo_depth = 0;
+ XtSetSensitive(ispell.undo, False);
+}
+
+/*ARGSUSED*/
+static void
+RevertIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ Arg args[1];
+ char *string, *repstr = NULL;
+
+ XtSetArg(args[0], XtNlabel, &string);
+ XtGetValues(ispell.word, args, 1);
+ if ((repstr = strchr(string, ' ')) != NULL) {
+ string = repstr = XtNewString(string);
+ *strchr(repstr, ' ') = '\0';
+ }
+ XtSetArg(args[0], XtNstring, string);
+ XtSetValues(ispell.text, args, 1);
+ if (repstr)
+ XtFree(repstr);
+}
+
+/*ARGSUSED*/
+static void
+SelectIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ XawListReturnStruct *info = (XawListReturnStruct *)call_data;
+ Arg args[1];
+
+ XtSetArg(args[0], XtNstring, ispell.item = info->string);
+ XtSetValues(ispell.text, args, 1);
+}
+
+/*ARGSUSED*/
+void
+ReplaceIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ XawTextPosition pos = XawTextGetInsertionPoint(ispell.ascii);
+ XawTextBlock check, search, replace;
+ Arg args[1];
+ char *text;
+
+ if (!ispell.lock)
+ return;
+
+ XtSetArg(args[0], XtNlabel, &text);
+ XtGetValues(ispell.word, args, 1);
+ search.ptr = text;
+ search.format = XawFmt8Bit;
+ search.firstPos = 0;
+ search.length = ispell.right - pos;
+
+ XtSetArg(args[0], XtNstring, &text);
+ XtGetValues(ispell.text, args, 1);
+ replace.ptr = text;
+ replace.format = XawFmt8Bit;
+ replace.firstPos = 0;
+ replace.length = strlen(text);
+
+ if (strcmp(search.ptr, replace.ptr) != 0 &&
+ XawTextReplace(ispell.ascii, pos, pos + search.length,
+ &replace) == XawEditDone) {
+ ispell.right += replace.length - search.length;
+ IspellCheckUndo();
+ ispell.undo_head->undo_str = NULL;
+ ispell.undo_head->undo_pos = pos;
+ ispell.undo_head->undo_count = 1;
+
+ if (ispell.repeat) {
+ ispell.undo_head->repeat = 2; /* To recognize later it was replaced */
+ ispell.undo_head->undo_count = ispell.right;
+ ispell.undo_head->undo_str = XtNewString(search.ptr);
+ }
+ if (client_data && !ispell.repeat) {
+ XawTextDisableRedisplay(ispell.ascii);
+ pos = ispell.right;
+ while ((pos = XawTextSourceSearch(ispell.source, pos, XawsdRight, &search))
+ != XawTextSearchError) {
+ Bool do_replace = True;
+ char mb[sizeof(wchar_t)];
+
+ if (XawTextSourceRead(ispell.source, pos - 1, &check, 1) > 0) {
+ if (international)
+ wctomb(mb, *(wchar_t*)check.ptr);
+ else
+ *mb = *check.ptr;
+ do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb);
+ }
+ if (do_replace &&
+ XawTextSourceRead(ispell.source, pos + search.length, &check, 1) > 0) {
+ if (international)
+ wctomb(mb, *(wchar_t*)check.ptr);
+ else
+ *mb = *check.ptr;
+ do_replace = !isalpha(*mb) && *mb && !strchr(ispell.wchars, *mb);
+ }
+ if (do_replace) {
+ XawTextReplace(ispell.ascii, pos, pos + search.length, &replace);
+ ++ispell.undo_head->undo_count;
+ }
+ pos += search.length;
+ }
+ XawTextEnableRedisplay(ispell.ascii);
+ }
+ (void)IspellReplacedWord(search.ptr, replace.ptr);
+
+ strncpy(&ispell.sentbuf[1], replace.ptr, sizeof(ispell.sentbuf) - 2);
+ ispell.sentbuf[sizeof(ispell.sentbuf) - 1] = '\0';
+ }
+ else
+ Feep();
+
+ if (ispell.repeat)
+ ispell.right = ispell.left = XawTextGetInsertionPoint(ispell.ascii);
+ else if (!ispell.terse_mode || !ispell.item ||
+ strcmp(ispell.item, replace.ptr))
+ ispell.right = ispell.left; /* check it again! */
+
+ ispell.lock = ispell.checkit = False;
+
+ ispell.stat = SEND;
+ IspellSetStatus(ispell.working_label);
+ while (IspellSend() == 0)
+ ;
+}
+
+/*ARGSUSED*/
+void
+IgnoreIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ Arg args[1];
+ char *text;
+
+ if (!ispell.lock)
+ return;
+
+ XtSetArg(args[0], XtNlabel, &text);
+ XtGetValues(ispell.word, args, 1);
+
+ IspellCheckUndo();
+
+ if ((ispell.undo_head->repeat = ispell.repeat) != False) {
+ ispell.undo_head->undo_count = ispell.right;
+ ispell.undo_head->undo_str = XtNewString(text);
+ }
+ else
+ ispell.undo_head->undo_count = 0;
+
+ ispell.undo_head->undo_pos = XawTextGetInsertionPoint(ispell.ascii);
+
+ if (!ispell.repeat) {
+ if (client_data) {
+ IspellIgnoredWord(text, ADD, 0);
+ ispell.undo_head->undo_str = XtNewString(text);
+ }
+ else
+ ispell.undo_head->undo_str = NULL;
+ }
+
+ ispell.lock = ispell.checkit = False;
+
+ ispell.stat = SEND;
+ IspellSetStatus(ispell.working_label);
+ while (IspellSend() == 0)
+ ;
+}
+
+/*ARGSUSED*/
+void
+AddIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ Arg args[1];
+ char *text;
+ int cmd = (long)client_data;
+
+ if (!ispell.lock || ispell.repeat)
+ return;
+
+ XtSetArg(args[0], XtNlabel, &text);
+ XtGetValues(ispell.word, args, 1);
+
+ IspellCheckUndo();
+ ispell.undo_head->undo_str = XtNewString(text);
+ ispell.undo_head->undo_pos = XawTextGetInsertionPoint(ispell.ascii);
+ ispell.undo_head->undo_count = -cmd;
+
+ (void)IspellIgnoredWord(text, ADD, cmd);
+
+ ispell.lock = ispell.checkit = False;
+ ispell.stat = SEND;
+ IspellSetStatus(ispell.working_label);
+ while (IspellSend() == 0)
+ ;
+}
+
+/*ARGSUSED*/
+static void
+UndoIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ Bool enable_redisplay = False;
+ ispell_undo *undo = ispell.undo_head;
+
+ if ((!ispell.lock && ispell.stat) || !undo)
+ return;
+
+ if (ispell.undo_for && strcmp(ispell.undo_for, ispell.dictionary)) {
+ XeditPrintf("Undo: Dictionary changed. Undo information was lost.\n");
+ IspellKillUndoBuffer();
+ Feep();
+ return;
+ }
+
+ if (undo->terse != ispell.terse_mode)
+ IspellSetTerseMode(undo->terse);
+
+ if (undo->format != ispell.format_info->value) {
+ struct _ispell_format *fmt = &ispell_format[undo->format];
+ ChangeFormatIspell(fmt->sme, (XtPointer)fmt, NULL);
+ }
+
+ if (undo->undo_count > 0 && !undo->repeat) {
+ XawTextPosition tmp;
+
+ enable_redisplay = undo->undo_count > 1;
+ if (enable_redisplay)
+ XawTextDisableRedisplay(ispell.ascii);
+ while (undo->undo_count--)
+ if (!_XawTextSrcUndo((TextSrcObject)ispell.source, &tmp)) {
+ Feep();
+ break;
+ }
+ }
+ else if (undo->undo_count < 0) {
+ if (undo->undo_str)
+ (void)IspellIgnoredWord(undo->undo_str, REMOVE, -undo->undo_count);
+ }
+ else if (undo->undo_str) {
+ if (!undo->repeat)
+ IspellIgnoredWord(undo->undo_str, REMOVE, 0);
+ }
+
+ XawTextSetInsertionPoint(ispell.ascii,
+ ispell.right = ispell.left = undo->undo_pos);
+ if (enable_redisplay)
+ XawTextEnableRedisplay(ispell.ascii);
+
+ /* need to do it because may be two misspelled words together */
+ if (undo->repeat) {
+ char **list, **old_list;
+ int old_len;
+ Arg args[2];
+
+ if (undo->repeat > 1) {
+ XawTextDisableRedisplay(ispell.ascii);
+ if (!_XawTextSrcUndo((TextSrcObject)ispell.source, &ispell.right))
+ Feep();
+ XawTextEnableRedisplay(ispell.ascii);
+ }
+ else
+ ispell.right = (XawTextPosition)undo->undo_count;
+ IspellSetRepeated(ispell.repeat = True);
+ XtSetArg(args[0], XtNlabel, undo->undo_str);
+ XtSetValues(ispell.word, args, 1);
+ XmuSnprintf(ispell.sentbuf, sizeof(ispell.sentbuf), "^%s",
+ strrchr(undo->undo_str, ' ') + 1);
+ strcpy(ispell.sendbuf, ispell.sentbuf);
+ XtSetArg(args[0], XtNstring, &ispell.sentbuf[1]);
+ XtSetValues(ispell.text, args, 1);
+
+ XtSetArg(args[0], XtNlist, &old_list);
+ XtSetArg(args[1], XtNnumberStrings, &old_len);
+ XtGetValues(ispell.list, args, 2);
+
+ list = (char **)XtMalloc(sizeof(char*));
+ list[0] = XtNewString(&ispell.sentbuf[1]);
+ XtSetArg(args[0], XtNlist, list);
+ XtSetArg(args[1], XtNnumberStrings, 1);
+ XtSetValues(ispell.list, args, 2);
+ XtSetSensitive(ispell.list, True);
+ XawListHighlight(ispell.list, 0);
+
+ if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
+ while (--old_len > -1)
+ XtFree(old_list[old_len]);
+ XtFree((char*)old_list);
+ }
+
+ IspellSetSelection(ispell.left, ispell.right);
+ IspellSetStatus(ispell.repeat_label);
+ ispell.lock = True;
+ ispell.checkit = False;
+ }
+ else if (ispell.repeat) {
+ *ispell.sentbuf = '\0';
+ IspellSetRepeated(ispell.repeat = False);
+ }
+
+ if (undo->prev)
+ undo->prev->next = NULL;
+ ispell.undo_head = undo->prev;
+ if (undo == ispell.undo_base) {
+ ispell.undo_base = NULL;
+ ispell.undo_for = NULL;
+ XtSetSensitive(ispell.undo, False);
+ }
+ if (undo->undo_str)
+ XtFree(undo->undo_str);
+ XtFree((char*)undo);
+ --ispell.undo_depth;
+
+ if (!ispell.stat || ispell.checkit)
+ IspellSetSensitive(True);
+
+ if (!ispell.repeat) {
+ ispell.lock = ispell.checkit = False;
+ ispell.stat = SEND;
+ IspellSetStatus(ispell.working_label);
+ while (IspellSend() == 0)
+ ;
+ }
+}
+
+/*ARGSUSED*/
+static void
+CheckIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ Arg args[1];
+ char *text, *str, string[1024];
+ int i, len;
+
+ if (!ispell.lock)
+ return;
+
+ XtSetArg(args[0], XtNstring, &text);
+ XtGetValues(ispell.text, args, 1);
+
+ /* Check only a word at a time */
+ len = 0;
+ str = text;
+ while (*str) {
+ if (isalpha(*str) || strchr(ispell.wchars, *str))
+ break;
+ ++str;
+ ++len;
+ }
+ i = 0;
+ while (*str) {
+ if (isalpha(*str) || strchr(ispell.wchars, *str))
+ string[i++] = *str++;
+ else
+ break;
+ }
+ string[i] = '\0';
+
+ if (strcmp(text, string)) {
+ XawTextPosition pos = XawTextGetInsertionPoint(ispell.text) - len;
+
+ XtSetArg(args[0], XtNstring, string);
+ XtSetValues(ispell.text, args, 1);
+ XawTextSetInsertionPoint(ispell.text, pos);
+ Feep();
+ }
+
+ if (i == 0) {
+ Feep();
+ return;
+ }
+
+ len = XmuSnprintf(ispell.sendbuf, sizeof(ispell.sendbuf), "^%s\n", string);
+
+ ispell.sendbuf[sizeof(ispell.sendbuf) - 1] = '\n';
+
+ write(ispell.ofd[1], ispell.sendbuf, len);
+
+ ispell.lock = False;
+ ispell.checkit = True;
+ ispell.stat = RECEIVE;
+}
+
+/*ARGSUSED*/
+static void
+LookIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ int len, old_len;
+ FILE *fd;
+ Arg args[2];
+ char *text, *str, **list, **old_list, command[1024], buffer[1024];
+ Bool sensitive = True;
+
+ if (!ispell.lock)
+ return;
+
+ XtSetArg(args[0], XtNstring, &text);
+ XtGetValues(ispell.text, args, 1);
+
+ if (!*text) {
+ Feep();
+ return;
+ }
+
+ if (strlen(ispell.look_cmd) + strlen(text) + strlen(ispell.words_file) + 8
+ > sizeof(command) - 1) {
+ fprintf(stderr, "Command line too large\n");
+ return;
+ }
+
+ XmuSnprintf(command, sizeof(command), "%s '^%s.*$' %s",
+ ispell.look_cmd, text, ispell.words_file);
+
+ if ((fd = popen(command, "r")) == NULL) {
+ fprintf(stderr, "Cannot popen '%s'\n", ispell.look_cmd);
+ return;
+ }
+
+ list = NULL;
+ len = 0;
+
+#define MAX_LOOK_RESULTS 256
+ while (fgets(buffer, sizeof(buffer), fd) != NULL) {
+ if ((str = strchr(buffer, '\n')) == NULL) {
+ fprintf(stderr, "String is too large\n");
+ break;
+ }
+ *str = '\0';
+ if ((len % 16) == 0)
+ list = (char**)XtRealloc((char*)list, sizeof(char*) * (len + 16));
+ list[len] = XtNewString(buffer);
+ if (++len >= MAX_LOOK_RESULTS) {
+ Feep();
+ break;
+ }
+ }
+#undef MAX_LOOK_RESULTS
+
+ XtSetArg(args[0], XtNlist, &old_list);
+ XtSetArg(args[1], XtNnumberStrings, &old_len);
+ XtGetValues(ispell.list, args, 2);
+
+ if (len == 0) {
+ list = (char**)XtMalloc(sizeof(char*));
+ list[0] = XtNewString("");
+ len = 1;
+ sensitive = False;
+ }
+
+ XtSetArg(args[0], XtNlist, list);
+ XtSetArg(args[1], XtNnumberStrings, len);
+ XtSetValues(ispell.list, args, 2);
+
+ XtSetSensitive(ispell.list, sensitive);
+ IspellSetStatus(sensitive ? ispell.look_label : ispell.none_label);
+
+ if (old_len > 1 || (XtName(ispell.list) != old_list[0])) {
+ while (--old_len > -1)
+ XtFree(old_list[old_len]);
+ XtFree((char*)old_list);
+ }
+
+ pclose(fd);
+}
+
+/*ARGSUSED*/
+static void
+ToggleTerseIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ if (!ispell.lock)
+ return;
+
+ ispell.terse_mode = !ispell.terse_mode;
+ write(ispell.ofd[1], ispell.terse_mode ? "!\n" : "%\n", 2);
+}
+
+/*ARGSUSED*/
+static void
+ChangeDictionaryIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ ispell_dict *tmp, *dic = (ispell_dict*)client_data;
+ XawTextPosition pos = XawTextGetInsertionPoint(ispell.ascii);
+ XawTextPosition right = ispell.right;
+ Arg args[1];
+
+ if (strcmp(XtName(dic->sme), ispell.dictionary) == 0)
+ return;
+
+ if (!ispell.lock) {
+ Feep();
+ return;
+ }
+
+ for (tmp = ispell.dict_info; tmp; tmp = tmp->next)
+ if (strcmp(XtName(tmp->sme), ispell.dictionary) == 0) {
+ XtSetArg(args[0], XtNleftBitmap, None);
+ XtSetValues(tmp->sme, args, 1);
+ }
+
+ if (ispell.undo_base && !ispell.undo_for)
+ ispell.undo_for = ispell.dictionary;
+
+ XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
+ XtSetValues(dic->sme, args, 1);
+ ispell.dictionary = XtName(dic->sme);
+ ispell.wchars = dic->wchars;
+ XtSetArg(args[0], XtNlabel, XtName(dic->sme));
+ XtSetValues(ispell.dict, args, 1);
+
+ IspellSetStatus(ispell.working_label);
+
+ (void)IspellEndProcess(True, False);
+ ispell.lock = ispell.checkit = False;
+ (void)IspellStartProcess();
+
+ ispell.stat = RECEIVE;
+
+ /* restart at the same selected word */
+ if (ispell.repeat == False)
+ ispell.left = ispell.right = pos;
+ else
+ ispell.right = right;
+}
+
+/*ARGSUSED*/
+static void
+ChangeFormatIspell(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ struct _ispell_format *fmt = (struct _ispell_format*)client_data;
+ Arg args[1];
+
+ if (strcmp(fmt->name, ispell.formatting) == 0)
+ return;
+
+ if (!ispell.lock) {
+ Feep();
+ return;
+ }
+
+ XtSetArg(args[0], XtNleftBitmap, None);
+ XtSetValues(ispell.format_info->sme, args, 1);
+
+ XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
+ XtSetValues(fmt->sme, args, 1);
+ ispell.formatting = fmt->name;
+ ispell.format_info = fmt;
+ XtSetArg(args[0], XtNlabel, fmt->name);
+ XtSetValues(ispell.format, args, 1);
+}
+
+static Bool
+InitIspell(void)
+{
+ Atom delete_window;
+ char *str, *list;
+ XtResource dict_res;
+ int i;
+ static XtResource text_res[] = {
+ {"skipLines", "Skip", XtRString, sizeof(char*),
+ XtOffsetOf(struct _ispell, skip), XtRString, "#"},
+ };
+
+ if (ispell.shell)
+ return (False);
+
+ ispell.shell = XtCreatePopupShell("ispell", transientShellWidgetClass,
+ topwindow, NULL, 0);
+
+ XtGetApplicationResources(ispell.shell, (XtPointer)&ispell, resources,
+ XtNumber(resources), NULL, 0);
+
+ ispell.form = XtCreateManagedWidget("form", formWidgetClass,
+ ispell.shell, NULL, 0);
+ ispell.mispelled = XtCreateManagedWidget("mispelled", labelWidgetClass,
+ ispell.form, NULL, 0);
+ ispell.repeated = XtCreateWidget("repeated", labelWidgetClass,
+ ispell.form, NULL, 0);
+ ispell.word = XtCreateManagedWidget("word", commandWidgetClass,
+ ispell.form, NULL, 0);
+ XtAddCallback(ispell.word, XtNcallback, RevertIspell, NULL);
+ ispell.replacement = XtCreateManagedWidget("replacement", labelWidgetClass,
+ ispell.form, NULL, 0);
+ ispell.text = XtVaCreateManagedWidget("text", asciiTextWidgetClass,
+ ispell.form,
+ XtNeditType, XawtextEdit,
+ NULL, 0);
+ ispell.suggestions = XtCreateManagedWidget("suggestions", labelWidgetClass,
+ ispell.form, NULL, 0);
+ ispell.viewport = XtCreateManagedWidget("viewport", viewportWidgetClass,
+ ispell.form, NULL, 0);
+ ispell.list = XtCreateManagedWidget("list", listWidgetClass,
+ ispell.viewport, NULL, 0);
+ XtAddCallback(ispell.list, XtNcallback, SelectIspell, NULL);
+ ispell.commands = XtCreateManagedWidget("commands", formWidgetClass,
+ ispell.form, NULL, 0);
+ ispell.check = XtCreateManagedWidget("check", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.check, XtNcallback, CheckIspell, NULL);
+ ispell.look = XtCreateManagedWidget("look", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.look, XtNcallback, LookIspell, NULL);
+ ispell.undo = XtCreateManagedWidget("undo", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.undo, XtNcallback, UndoIspell, NULL);
+ ispell.replace = XtCreateManagedWidget("replace", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.replace, XtNcallback, ReplaceIspell, (XtPointer)False);
+ ispell.replaceAll = XtCreateManagedWidget("replaceAll", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.replaceAll, XtNcallback, ReplaceIspell, (XtPointer)True);
+ ispell.ignore = XtCreateManagedWidget("ignore", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.ignore, XtNcallback, IgnoreIspell, (XtPointer)False);
+ ispell.ignoreAll = XtCreateManagedWidget("ignoreAll", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.ignoreAll, XtNcallback, IgnoreIspell, (XtPointer)True);
+ ispell.add = XtCreateManagedWidget("add", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.add, XtNcallback, AddIspell, (XtPointer)ASIS);
+ ispell.addUncap = XtCreateManagedWidget("addUncap", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.addUncap, XtNcallback, AddIspell, (XtPointer)UNCAP);
+ ispell.suspend = XtCreateManagedWidget("suspend", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.suspend, XtNcallback, PopdownIspell, (XtPointer)False);
+ ispell.cancel = XtCreateManagedWidget("cancel", commandWidgetClass,
+ ispell.commands, NULL, 0);
+ XtAddCallback(ispell.cancel, XtNcallback, PopdownIspell, (XtPointer)True);
+ ispell.terse = XtVaCreateManagedWidget("terse", toggleWidgetClass,
+ ispell.commands,
+ XtNstate, ispell.terse_mode,
+ NULL, 0);
+ XtAddCallback(ispell.terse, XtNcallback, ToggleTerseIspell, NULL);
+ ispell.status = XtCreateManagedWidget("status", labelWidgetClass,
+ ispell.form, NULL, 0);
+ ispell.options = XtCreateManagedWidget("options", formWidgetClass,
+ ispell.form, NULL, 0);
+ ispell.dict = XtVaCreateManagedWidget("dict", menuButtonWidgetClass,
+ ispell.options,
+ XtNmenuName, "dictionaries",
+ NULL, 0);
+ ispell.dictMenu = XtCreatePopupShell("dictionaries", simpleMenuWidgetClass,
+ ispell.options, NULL, 0);
+ XtRealizeWidget(ispell.dictMenu);
+
+ ispell.format = XtVaCreateManagedWidget("format", menuButtonWidgetClass,
+ ispell.options,
+ XtNmenuName, "formats",
+ NULL, 0);
+ ispell.formatMenu = XtCreatePopupShell("formats", simpleMenuWidgetClass,
+ ispell.options, NULL, 0);
+ XtRealizeWidget(ispell.formatMenu);
+
+ XtRealizeWidget(ispell.shell);
+
+ for (i = 0; i < sizeof(ispell_format) / sizeof(ispell_format[0]); i++) {
+ struct _ispell_format *fmt = &ispell_format[i];
+
+ fmt->sme = XtCreateManagedWidget(fmt->name, smeBSBObjectClass,
+ ispell.formatMenu, NULL, 0);
+ XtAddCallback(fmt->sme, XtNcallback, ChangeFormatIspell, (XtPointer)fmt);
+
+ if (strcmp(fmt->name, ispell.formatting) == 0) {
+ Arg args[1];
+
+ XtSetArg(args[0], XtNlabel, ispell.formatting);
+ XtSetValues(ispell.format, args, 1);
+ XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
+ XtSetValues(fmt->sme, args, 1);
+ ispell.format_info = fmt;
+ }
+ }
+ if (ispell.format_info == NULL) {
+ Arg args[1];
+ char msg[256];
+
+ ispell.format_info = &ispell_format[TEXT];
+
+ XmuSnprintf(msg, sizeof(msg),
+ "Unrecognized formatting type \"%s\", will use \"%s\"",
+ ispell.formatting, ispell.format_info->name);
+ XtAppWarning(XtWidgetToApplicationContext(ispell.shell), msg);
+ ispell.formatting = ispell.format_info->name;
+
+ XtSetArg(args[0], XtNlabel, ispell.format_info->name);
+ XtSetValues(ispell.format, args, 1);
+ XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
+ XtSetValues(ispell.format_info->sme, args, 1);
+ }
+ XtGetApplicationResources(ispell_format[TEXT].sme, (XtPointer)&ispell,
+ text_res, XtNumber(text_res), NULL, 0);
+
+ dict_res.resource_name = "wordChars";
+ dict_res.resource_class = "Chars";
+ dict_res.resource_type = XtRString;
+ dict_res.resource_size = sizeof(char*);
+ dict_res.resource_offset = XtOffsetOf(ispell_dict, wchars);
+ dict_res.default_type = XtRString;
+ 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,
+ 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) {
+ Arg args[1];
+
+ XtSetArg(args[0], XtNleftBitmap, flist.pixmap);
+ XtSetValues(dic->sme, args, 1);
+ XtSetArg(args[0], XtNlabel, str);
+ XtSetValues(ispell.dict, args, 1);
+ ispell.wchars = dic->wchars;
+ }
+ }
+ XtFree(list);
+
+ delete_window = XInternAtom(XtDisplay(ispell.shell), "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(XtDisplay(ispell.shell), XtWindow(ispell.shell), &delete_window, 1);
+
+ return (True);
+}
diff --git a/lisp.c b/lisp.c
new file mode 100644
index 0000000..ceaf41c
--- /dev/null
+++ b/lisp.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp.c,v 1.20 2002/12/04 05:27:56 paulo Exp $ */
+
+#include "xedit.h"
+#include "lisp/lisp.h"
+#include "lisp/xedit.h"
+#include <unistd.h>
+#include <locale.h>
+#include <ctype.h>
+
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SimpleMenu.h>
+
+/*
+ * Prototypes
+ */
+static void XeditDoLispEval(Widget);
+static void EditModeCallback(Widget, XtPointer, XtPointer);
+
+/*
+ * Initialization
+ */
+static int lisp_initialized;
+extern Widget scratch;
+static Widget edit_mode_menu, edit_mode_entry, edit_mode_none;
+
+/*
+ * Implementation
+ */
+void
+XeditLispInitialize(void)
+{
+ setlocale(LC_NUMERIC, "C");
+ lisp_initialized = 1;
+ LispBegin();
+ LispXeditInitialize();
+}
+
+void
+XeditLispCleanUp(void)
+{
+ LispEnd();
+}
+
+void
+XeditLispEval(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ XeditDoLispEval(messwidget);
+}
+
+void
+XeditPrintLispEval(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ if (XawTextGetSource(w) == scratch) {
+ XtCallActionProc(w, "newline", event, params, *num_params);
+ XeditDoLispEval(w);
+ }
+ else
+ XtCallActionProc(w, "newline-and-indent", event, params, *num_params);
+}
+
+void
+XeditKeyboardReset(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ XtCallActionProc(w, "keyboard-reset", event, params, *num_params);
+}
+
+void
+SetTextProperties(xedit_flist_item *item)
+{
+ if (lisp_initialized) {
+ Widget source = XawTextGetSource(textwindow);
+ XawTextPosition top = XawTextTopPosition(textwindow);
+
+ if (source != item->source)
+ XawTextSetSource(textwindow, item->source, 0);
+ XeditLispSetEditMode(item, NULL);
+ if (source != item->source)
+ XawTextSetSource(textwindow, source, top);
+ }
+}
+
+void
+UnsetTextProperties(xedit_flist_item *item)
+{
+ XeditLispUnsetEditMode(item);
+}
+
+static void
+XeditDoLispEval(Widget output)
+{
+ Widget src;
+ XawTextBlock block;
+ XawTextPosition position, end;
+
+ /* get lisp expression */
+ src = XawTextGetSource(textwindow);
+ position = XawTextGetInsertionPoint(textwindow);
+ --position;
+ while (position >= 0) {
+ (void)XawTextSourceRead(src, position, &block, 1);
+ if (!isspace(block.ptr[0]))
+ break;
+ --position;
+ }
+ end = position + 1;
+
+ if (block.ptr[0] != ')') {
+ while (position >= 0) {
+ (void)XawTextSourceRead(src, position, &block, 1);
+ if (isspace(block.ptr[0]) ||
+ block.ptr[0] == '(' ||
+ block.ptr[0] == ')' ||
+ block.ptr[0] == '"' ||
+ block.ptr[0] == '|')
+ break;
+ --position;
+ }
+ if (!isspace(block.ptr[0]))
+ ++position;
+ }
+ else {
+ /* XXX note that embedded '(' and ')' will confuse this code */
+ XawTextPosition last, tmp;
+ int level = 0;
+ char ptr[2];
+
+ last = position;
+ ptr[1] = '\0';
+ block.ptr = ptr;
+ do {
+ block.ptr[0] = '(';
+ position = XawTextSourceSearch(src, last, XawsdLeft, &block);
+ if (position == XawTextSearchError) {
+ Feep();
+ return;
+ }
+ block.ptr[0] = ')';
+ tmp = position;
+ do {
+ tmp = XawTextSourceSearch(src, tmp, XawsdRight, &block);
+ if (tmp == XawTextSearchError) {
+ Feep();
+ return;
+ }
+ if (tmp <= last)
+ ++level;
+ } while (++tmp <= last);
+ --level;
+ last = position;
+ } while (level);
+ /* check for extra characters */
+ while (position > 0) {
+ (void)XawTextSourceRead(src, position - 1, &block, 1);
+ if (block.length != 1 ||
+ isspace(block.ptr[0]) ||
+ block.ptr[0] == ')' ||
+ block.ptr[0] == '"' ||
+ block.ptr[0] == '|')
+ break;
+ --position;
+ }
+ }
+
+ if (position < 0 || position >= end)
+ Feep();
+ else
+ XeditLispExecute(output, position, end);
+}
+
+void
+CreateEditModePopup(Widget parent)
+{
+ int i;
+ Widget sme;
+ static char *editModes = "editModes";
+
+ XtVaCreateManagedWidget("modeMenuItem", smeBSBObjectClass, parent,
+ XtNmenuName, editModes, NULL);
+ edit_mode_menu = XtCreatePopupShell(editModes, simpleMenuWidgetClass,
+ parent, NULL, 0);
+ XtRealizeWidget(edit_mode_menu);
+
+ edit_mode_none = XtCreateManagedWidget("none", smeBSBObjectClass,
+ edit_mode_menu, NULL, 0);
+ XtAddCallback(edit_mode_none, XtNcallback, EditModeCallback, NULL);
+
+ for (i = 0; i < num_mode_infos; i++) {
+ sme = XtVaCreateManagedWidget("mode", smeBSBObjectClass, edit_mode_menu,
+ XtNlabel, mode_infos[i].desc, NULL);
+ XtAddCallback(sme, XtNcallback, EditModeCallback,
+ (XtPointer)(mode_infos + i));
+ mode_infos[i].sme = sme;
+ }
+}
+
+void
+SetEditModeMenu(void)
+{
+ int i;
+ Widget old_entry = edit_mode_entry, new_entry = edit_mode_none;
+ xedit_flist_item *item = FindTextSource(XawTextGetSource(textwindow), NULL);
+
+ for (i = 0; i < num_mode_infos; i++) {
+ if (item->xldata && item->xldata->syntax &&
+ mode_infos[i].syntax == item->xldata->syntax) {
+ new_entry = mode_infos[i].sme;
+ break;
+ }
+ }
+
+ if (old_entry != new_entry) {
+ if (old_entry)
+ XtVaSetValues(old_entry, XtNleftBitmap, None, NULL);
+ XtVaSetValues(new_entry, XtNleftBitmap, flist.pixmap, NULL);
+ edit_mode_entry = new_entry;
+ }
+}
+
+static void
+EditModeCallback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ Widget source = XawTextGetSource(textwindow);
+ EditModeInfo *info = (EditModeInfo*)client_data;
+ xedit_flist_item *item = FindTextSource(source, NULL);
+
+ /* If no edit mode selected and selecting the plain/none mode */
+ if ((info == NULL &&
+ (item->xldata == NULL || item->xldata->syntax == NULL)) ||
+ /* if selecting the current mode */
+ (info && item && item->xldata && info->syntax &&
+ info->syntax == item->xldata->syntax))
+ return;
+
+ XawTextSourceClearEntities(source,
+ XawTextSourceScan(source, 0, XawsdLeft,
+ XawstAll, 1, True),
+ XawTextSourceScan(source, 0, XawsdRight,
+ XawstAll, 1, True));
+ XeditLispUnsetEditMode(item);
+ if (info)
+ XeditLispSetEditMode(item, info->symbol);
+ else
+ item->properties = NULL;
+ UpdateTextProperties(1);
+}
diff --git a/lisp/README b/lisp/README
new file mode 100644
index 0000000..b375d86
--- /dev/null
+++ b/lisp/README
@@ -0,0 +1,195 @@
+$XFree86: xc/programs/xedit/lisp/README,v 1.13 2002/12/16 03:59:27 paulo Exp $
+
+LAST UPDATED: $Date$
+
+
+ SUMMARY
+
+ This is a small lisp interpreter for xedit. It implements a subset of
+Common Lisp and the xedit package implements several of the basic Emacs
+lisp functions.
+
+(shared modules not broken, but needs a redesign for better performance,
+ but won't be made available in the default build probably for a long time,
+ it would be really better to generate the interface dinamically, and/or just
+ link agains't the required libraries and use a ffi interface)
++------------------------------------------------------------------------
+| It has a very simple method for loading shared modules, slightly based on
+| the XFree86 loader code, that is currently disabled by default. To enable it,
+| edit lisp.cf and change BuildSharedLispModules to YES.
+|
+| Assuming you have built it with BuildSharedLispModules enabled, you can build
+| a small test application can be built in this directory running "make lsp".
+| Two lisp programs are available in the test directory. To test the programs
+| run "./lsp test/hello.lsp" or "./lsp test/widgets.lsp".
++------------------------------------------------------------------------
+
+ Currently, it should be used as an helper and/or a small calculator embedded
+in xedit. For the future it should be possible to write entire interfaces
+in the xedit text buffers.
+
+
+ USAGE SUMMARY
+
+ To evaluate lisp expressions, put the text cursor just after the
+lisp expression and press:
+C-x,C-e - will evaluate it, and print the result to the message window
+C-j - will evaluate it, and print the result to the edit window, any
+ errors are printed to the message window.
+C-g - will send an SIGINT to the lisp process, and that process will
+ stop whatever it was processing and jump to the toplevel,
+ to wait for more input.
+
+Note that C-j will only work in the *scratch* buffer.
+
+
+ NOTES
+
+ The improvements to xedit including the several possibilites to extend
+the editor using Lisp are expected to allow making of xedit a versatile
+text editor for programming, but there is code being (slowly) developed
+that should also make it useable as a small word processor, for things
+like WYSIWYG html, etc.
+ The xedit development is being done very slowly, maybe it will get
+somewhere someday, but it is a pet/hobby project, there is no intention
+of making of it an end user editor (the idea is to make it an useful
+development tool).
+ In some aspects the development is trying to mimic several Emacs
+features, but there is no intention of competition (if xedit ever get
+something better than Emacs, I hope that it serves as a motivation to
+make of Emacs an even better editor), actually it is expected to explore
+different areas and use alternate solutions for the implementation.
+ Most work in a computer is done in a text editor and the more the editor
+can help the user the better.
+
+
+(debugger is broken and very slow, no prevision for fixing it, but is
+ expected to work correctly for interpreted only code)
++------------------------------------------------------------------------
+| DEBUGGER
+|
+| There is a, currently, very simple debugger implement in the interpreter.
+| The debugger is now optional, and off by default. To make it available,
+| you need to recompile with -DDEBUGGER.
+| To use the debugger, run the lsp sample program as "./lsp -d", and optionally
+| pass a second parameter, for the file to be interpreted. Once the debugger
+| prompt is visible, type "help" for a summary of options. To leave the debugger
+| type "continue".
+| Note that the debugger is still very simple, it won't work from xedit, and
+| won't drop to the debugger on "fatal errors". It allows adding breakpoints to
+| functions and watchpoints to variables. Support for changing data and going to
+| the debugger on fatal errors should be added at some time.
++------------------------------------------------------------------------
+
+
+ COMPILER
+
+ Now there is a very simple bytecode compiler. It is far from finished, but
+for simple code can show significant better performance.
+ There is not yet an interface to compile entire files and no interface to
+store the generated bytecode in disk. There is an interface to bytecode
+compile toplevel forms as a LAMBDA NIL, but it is not yet exported.
+ If your code needs to call GO/RETURN/RETURN-FROM as the result of an EVAL,
+it must jump to code in the interpreter, after compiling all calls to
+GO/RETURN/RETURN-FROM are just stack adjusting and jumps in the bytecode.
+CATCH/THROW and UNWIND-PROTECT are running as interpreted code for now, so it
+is safe to use these, but code in such blocks is not compiled/optimized
+(not even macro expansion is done, as it understands that while not compiled,
+everything is candidate to redefinition at any time).
+ To compile the code, just write a function, and compile it, example:
+
+ (defun fact (n)
+ (if (< n 2)
+ 1
+ (* n (fact (1- n)))
+ )
+ )
+ FACT
+
+ (compile 'fact)
+ FACT
+ NIL
+ NIL
+
+ (disassemble 'fact)
+ Function FACT:
+ 1 required argument: N
+ 0 optional arguments
+ 0 keyword parameters
+ No rest argument
+
+ Bytecode header:
+ 1 element used in the stack
+ 2 elements used in the builtin stack
+ 0 elements used in the protected stack
+ Constant 0 = 1
+ Constant 1 = (2)
+ Symbol 0 = N
+ Builtin 0 = *
+ Builtin 1 = 1-
+ Builtin 2 = <
+
+ Initial stack:
+ 0 = N
+
+ Bytecode stream:
+ 0 LOAD&PUSH (0)
+ 2 LOADCON&PUSH [1] ; (2)
+ 4 CALL 2 [2] ; <
+ 7 JUMPNIL 8
+ 10 LOADCON [0] ; 1
+ 12 NOOP
+ 13 JUMP 19
+ 16 LOAD&PUSH (0)
+ 18 LOAD&PUSH (0)
+ 20 CALL 1 [1] ; 1-
+ 23 LET* [0] ; N
+ 25 LETREC 1
+ 27 UNLET 1
+ 29 BCONS1
+ 30 CALL 1 [0] ; *
+ 33 RETURN
+ FACT
+
+
+ There are several optimizations that should be done at some time, I don't
+think adding NOOP opcodes will help everywhere to make aligned memory reads
+of shorts and ints.
+ It should have explicitly visible registers, not the abstraction of "the
+current value", so the code generator can choose register allocation for
+loop control variables, commonly used variables, etc, for example. Jumps
+should have 3 types: byte relative, 2 bytes relative and 4 bytes relative.
+For now there is only 2 byte relative jumps, byte relative jumps
+can show a significant performance increase, but they are disable until
+it is decided how inlined functions will work, if it just updates the bytecode
+header and cut&past the bytecode, jumps must be updated, and some jumps
+may not fit anymore in a byte.
+
+
+ OPTIMIZATION
+
+ There are plenty of possibilities to make the interpreter run faster. Some
+optimizations that can make it run quite faster in certain cases are:
+ o Better object memory layout and gc. The current memory allocation code
+ is very bad, it try to keep 3 times more free objects than the currently
+ used number, this can consume a lot of memory. The reason is to reduce
+ the gc time cost so that it will in average miss only one in every 4
+ collect tries.
+ o Implement real vectors, currently they are just a list, so it cannot
+ just deference a given index, and gc time is very long also.
+ o Most lists are never changed once created, it could somehow add an index
+ field in the cons cell, so that NTH/NTHCDR/LENGTH like code could just
+ deference the correct object, instead of traversing the CDR of every
+ cons. This would probably require implementing lists as vectors, while
+ making it easy to deference would make life harder when deleting/inserting
+ sublists in a list. It should also better be done in a way that does
+ not require a lot of objects allocated linearly.
+
+
+ HELPING
+
+ Send comments and code to me (paulo@XFree86.Org) or to the XFree86
+mailing/patch lists.
+
+--
+Paulo
diff --git a/lisp/TODO b/lisp/TODO
new file mode 100644
index 0000000..01babd6
--- /dev/null
+++ b/lisp/TODO
@@ -0,0 +1,81 @@
+$XFree86: xc/programs/xedit/lisp/TODO,v 1.9 2002/12/16 03:59:27 paulo Exp $
+
+LAST UPDATED: $Date$
+
+ Small todo list
+
+o Change function/macro body to know if a &key or &optional argument was not
+ provided, and initialize to the default value in the function, for
+ interpreted and builtin functions it is better done before the function is
+ called, but for bytecode it is better in the function.
+o Following the previous idea, change function definitions in the format:
+ (defun afun (a &aux b (c (some-code))) ...)
+ to
+ (defun afun (a) (let* ((b (c (some-code)))) ...))
+ This can significatively reduce bytecode size, and also simplify function
+ calls.
+o Optimize text redisplay in Xaw, instead of allocating a copy of the buffer
+ for the paint-list, should use the text in place, and only allocate small
+ buffers when required, i.e. displaying control characters, etc.
+o Add an interface to create new object types dinamically.
+o Add a special string object to simplify and avoid too many copies of
+ portions of the text buffers. This special string should be read-only
+ and not gc-collected.
+o Make the bytecode compiler smarter to detect some constructs like:
+ (builtin-or-bytecode-function-call arg1 arg2 (return))
+ this will not properly restore the internal stacks.
+
+o When an Init function is present in every file, call LispAddBuiltin from
+ that initialization function.
+o Cleanup the code, make it optional to build some uncommon features (as well
+ as the entire interpreter?). Implement more functions directly in lisp.
+o Finish an "specification" for loadable modules. Write a FFI interface.
+ Without the possibility of using already existing libraries, the interpreter
+ won't be too much useful due to a poor library. It is very desirable to
+ "auto-generate" directly from C header files the interface to the lisp
+ code, and from that, dlload a shared library. In some cases, it is required
+ to link statically with a new interpreter binary, make it easy.
+o Implement a better string type. That should support characters larger than
+ 8 bits, and that should allow embeded nuls.
+o Implement a richer set of math functions. This, if properly done can be
+ made a loadable module.
+o Optmize mathimp.c, comparing a double with a bignum should never cause an
+ error. Implement mp?_initsetXXX functions?
+o Finish missing features in read.c, and simplify it.
+o (close) probably should not send a signal when closing a pipe.
+o Implement "real" vectors, they exist to make access to field elements
+ at constant time, implementing vectors as lists may be very slow.
+o Use float and double as floating points formats.
+o Implement support for vectors of "atomic" types. Vectors of floats would
+ be useful for example if a OpenGL binding is done.
+o Implement a multiple precision floating point format. Either a 128 bits
+ (or configurable length?) IEEE 754 like number, or some format using
+ ratios, a epsilon for rouding, rounding modes, exact/inexact flag, a good
+ amount of guard digits, etc.
+o Write more functions and optimization for bignums. Try to make the code
+ as reusable as possible, either by other software or at least by the
+ different number types.
+o Instead of using mathimp.c for a large amount of functions, implement a
+ "generic number" type and implement the calculations directly in the
+ mp library.
+o Add more missing Common Lisp features, like &allow-other-keys for function
+ definitions, the missing structure features, CLOS etc.
+o Fix the Postgresql module, make it compile everywhere.
+o Add support for multi-threaded applications?
+o Make it possible to know if a object has only one reference, this is
+ required to make "inplace" changes of variables. Very useful for things
+ like (incf) and (decf), and also when dealing with bignums.
+o Maybe have a freelist for objects depending on the type. Bignums can be
+ reused, and having some large ones expecting to be freed by the gc can
+ consume a lot of memory.
+o Internationalization. Support ',' in floats? Correctly treat characters
+ for {up,down}-casing.
+o Synch the Xaw text code for supporting tables, text alignment/paragraphs,
+ etc, and add bindings to the interpreter. Add support for incremental
+ changes of those properties, currently it is "read-only".
+o Write some type of "hyperlinks", this is the only feature missing to even
+ allow writting a web browser inside xedit.
+o Write some "demos" (maybe a file manager or a simple mail reader) using the
+ Xt/Xaw bindings (needs modules working in all systems).
+o Remove all calls to the macros GCDisable and GCEnable. This is unsafe
+ and should be removed.
diff --git a/lisp/bytecode.c b/lisp/bytecode.c
new file mode 100644
index 0000000..39667b0
--- /dev/null
+++ b/lisp/bytecode.c
@@ -0,0 +1,3707 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/bytecode.c,v 1.15 2003/01/29 03:05:53 paulo Exp $ */
+
+
+/*
+somethings TODO:
+
+ o Write code for allowing storing the bytecode on disk. Basically
+ write a section of the bytecode with the package name of the symbol
+ pointers, and after that, the symbols used. At load time just put
+ the pointers in the bytecode. Constants can be stored as the string
+ representation. Probably just storing the gc protected code as a
+ string is enough to rebuild it.
+
+ o Write code to store tags of BLOCK/CATCH and setjump buffer stacks,
+ and, only keep track of this if non byte-compiled code is called,
+ as after byte-compilation RETURN and THROW are just jumps.
+
+ o Remove not reliable "optmization code" code from Com_XXX functions
+ and do it later, removing dead code, tests with a constant argument,
+ etc, in the "link time". Frequently joining sequential opcodes to a
+ compound version.
+
+ o Write an optimizer to do code transformation.
+
+ o Write code to know when variables can be changed in place, this
+ can save a huge time in loop counters.
+
+ o Write code for fast garbage collection of objects that can be
+ safely collected.
+
+ o Cleanup of interpreted code. Having bytecode mean that the interpreter
+ now is better having a clean and small code. If speed is important,
+ it should be byte compiled.
+
+ o Limit the stacks length. So that instead of using an index, use the
+ pointer where an object value should be read/stored as the stack address
+ would not change during the program execution.
+
+ o Optimize jump to jump. Common in code like:
+ (IF test
+ (GO somewhere)
+ (GO elsewhere)
+ )
+ (GO there)
+ that generates a bytecode like:
+ <code to evaluate test>
+ JUMPNIL :NIL-RESULT
+ :T-RESULT
+ JUMP :SOMEWHERE
+ JUMP :END-OF-IF ;; <- this is not required, or even
+ :NIL-RESULT ;; better, notice the jump after
+ JUMP :ELSEWHERE ;; the if and transform it into
+ :END-OF-IF ;; a JUMP :THERE (assuming there
+ JUMP :THERE ;; (was no jump in the T code).
+
+ o Optimize variables that are known to not change it's value, i.e. pseudo
+ constants. Loading the value of a constant should be faster than loading
+ the current value of a variable; the constant table could fit in the
+ processor cache line and needs less calculation to find the object address.
+
+ o Fix some known problems, like when calling return or return-from while
+ building the argument list to a builtin function, or inline of recursive
+ functions.
+ */
+
+
+#include "bytecode.h"
+#include "write.h"
+
+#define SYMBOL_KEYWORD -1 /* A keyword, load as constant */
+#define SYMBOL_CONSTANT -2 /* Defined as constant at compile time */
+#define SYMBOL_UNBOUND -3 /* Not a local variable */
+
+#define NEW_TREE(type) CompileNewTree(com, type)
+
+/* If in tagbody, ignore anything that is not code */
+#define IN_TAGBODY() (com->block->type == LispBlockBody && \
+ com->level == com->tagbody)
+#define FORM_ENTER() ++com->level
+#define FORM_LEAVE() --com->level
+
+#define COMPILE_FAILURE(message) \
+ LispMessage("COMPILE: %s", message); \
+ longjmp(com->jmp, 1)
+
+/*
+ * Types
+ */
+typedef struct _CodeTree CodeTree;
+typedef struct _CodeBlock CodeBlock;
+
+typedef enum {
+ CodeTreeBytecode,
+ CodeTreeLabel,
+ CodeTreeGo,
+ CodeTreeJump,
+ CodeTreeJumpIf,
+ CodeTreeCond,
+ CodeTreeBlock,
+ CodeTreeReturn
+} CodeTreeType;
+
+struct _CodeTree {
+ CodeTreeType type;
+
+ /* Resolved when linking, may be adjusted while optimizing */
+ long offset;
+
+ LispByteOpcode code;
+
+ union {
+ signed char signed_char;
+ signed short signed_short;
+ signed int signed_int;
+ LispAtom *atom;
+ LispObj *object;
+ CodeTree *tree;
+ CodeBlock *block;
+ struct {
+ unsigned char num_arguments;
+ LispBuiltin *builtin;
+ signed short offset; /* Used if opcode is XBC_CALL_SET */
+ } builtin;
+ struct {
+ unsigned char num_arguments;
+ LispObj *name;
+ LispObj *lambda;
+ } call;
+ struct {
+ unsigned char num_arguments;
+ LispObj *code;
+ } bytecall;
+ struct {
+ short offset;
+ LispAtom *name;
+ } let;
+ struct {
+ LispAtom *symbol;
+ LispAtom *name;
+ } let_sym;
+ struct {
+ LispObj *object;
+ LispAtom *name;
+ } let_con;
+ struct {
+ signed short load;
+ signed short set;
+ } load_set;
+ struct {
+ LispObj *object;
+ signed short offset;
+ } load_con_set;
+ struct {
+ LispObj *car;
+ LispObj *cdr;
+ } cons;
+ struct {
+ short offset;
+ LispObj *definition;
+ } struc;
+ } data;
+
+ CodeTree *next;
+ CodeTree *group;
+ CodeBlock *block;
+};
+
+struct _CodeBlock {
+ LispBlockType type;
+ LispObj *tag;
+
+ struct {
+ LispObj **labels;
+ CodeTree **codes; /* Filled at link time */
+ int length;
+ int space;
+ } tagbody;
+
+ struct {
+ LispAtom **symbols; /* Identifiers of variables in a block */
+ int *flags; /* Information about usage of the variable */
+ int length;
+ } variables;
+
+ int bind; /* Used in case of RETURN from LET */
+ int level; /* Nesting level block was created */
+
+ CodeTree *tree, *tail;
+ CodeBlock *prev; /* Linked list as a stack */
+ CodeTree *parent; /* Back reference */
+};
+
+struct _LispCom {
+ unsigned char *bytecode; /* Bytecode generated so far */
+ long length;
+
+ CodeBlock *block, *toplevel;
+
+ int tagbody; /* Inside a tagbody block? */
+ int level; /* Nesting level */
+ int macro; /* Expanding a macro? */
+
+ int lex;
+
+ int warnings;
+
+ LispObj *form, *plist;
+
+ jmp_buf jmp; /* Used if compilation cannot be finished */
+
+ struct {
+ int cstack; /* Current number of objects in forms evaluation */
+ int cbstack;
+ int cpstack;
+ int stack; /* max number of objects will be loaded in stack */
+ int bstack;
+ int pstack;
+ } stack;
+
+ struct {
+ /* Constant table */
+ LispObj **constants;
+ int num_constants;
+ /* Symbol table */
+ LispAtom **symbols;
+ int num_symbols;
+ /* Builtin table */
+ LispBuiltin **builtins;
+ int num_builtins;
+ /* Bytecode table */
+ LispObj **bytecodes;
+ int num_bytecodes;
+ } table;
+};
+
+/*
+ * Prototypes
+ */
+static LispObj *MakeBytecodeObject(LispCom*, LispObj*, LispObj*);
+
+static CodeTree *CompileNewTree(LispCom*, CodeTreeType);
+static void CompileFreeState(LispCom*);
+static void CompileFreeBlock(CodeBlock*);
+static void CompileFreeTree(CodeTree*);
+
+static void CompileIniBlock(LispCom*, LispBlockType, LispObj*);
+static void CompileFiniBlock(LispCom*);
+
+static void com_BytecodeChar(LispCom*, LispByteOpcode, char);
+static void com_BytecodeShort(LispCom*, LispByteOpcode, short);
+static void com_BytecodeObject(LispCom*, LispByteOpcode, LispObj*);
+static void com_BytecodeCons(LispCom*, LispByteOpcode, LispObj*, LispObj*);
+
+static void com_BytecodeAtom(LispCom*, LispByteOpcode, LispAtom*);
+
+static void com_Bytecode(LispCom*, LispByteOpcode);
+
+static void com_Load(LispCom*, short);
+static void com_LoadLet(LispCom*, short, LispAtom*);
+static void com_LoadPush(LispCom*, short);
+
+static void com_Let(LispCom*, LispAtom*);
+
+static void com_Bind(LispCom*, short);
+static void com_Unbind(LispCom*, short);
+
+static void com_LoadSym(LispCom*, LispAtom*);
+static void com_LoadSymLet(LispCom*, LispAtom*, LispAtom*);
+static void com_LoadSymPush(LispCom*, LispAtom*);
+
+static void com_LoadCon(LispCom*, LispObj*);
+static void com_LoadConLet(LispCom*, LispObj*, LispAtom*);
+static void com_LoadConPush(LispCom*, LispObj*);
+
+static void com_Set(LispCom*, short);
+static void com_SetSym(LispCom*, LispAtom*);
+
+static void com_Struct(LispCom*, short, LispObj*);
+static void com_Structp(LispCom*, LispObj*);
+
+static void com_Call(LispCom*, unsigned char, LispBuiltin*);
+static void com_Bytecall(LispCom*, unsigned char, LispObj*);
+static void com_Funcall(LispCom*, LispObj*, LispObj*);
+
+static void CompileStackEnter(LispCom*, int, int);
+static void CompileStackLeave(LispCom*, int, int);
+
+static void LinkBytecode(LispCom*);
+
+static LispObj *ExecuteBytecode(unsigned char*);
+
+
+/* Defined in lisp.c */
+void LispMoreStack(void);
+void LispMoreEnvironment(void);
+void LispMoreGlobals(LispPackage*);
+LispObj *LispEvalBackquote(LispObj*, int);
+void LispSetAtomObjectProperty(LispAtom*, LispObj*);
+
+/*
+ * Initialization
+ */
+extern int pagesize;
+
+LispObj x_cons[8];
+static LispObj *cons, *cons1, *cons2, *cons3, *cons4, *cons5, *cons6, *cons7;
+
+/*
+ * Implementation
+ */
+#include "compile.c"
+
+void
+LispBytecodeInit(void)
+{
+ cons = &x_cons[7];
+ cons->type = LispCons_t;
+ CDR(cons) = NIL;
+ cons1 = &x_cons[6];
+ cons1->type = LispCons_t;
+ CDR(cons1) = cons;
+ cons2 = &x_cons[5];
+ cons2->type = LispCons_t;
+ CDR(cons2) = cons1;
+ cons3 = &x_cons[4];
+ cons3->type = LispCons_t;
+ CDR(cons3) = cons2;
+ cons4 = &x_cons[3];
+ cons4->type = LispCons_t;
+ CDR(cons4) = cons3;
+ cons5 = &x_cons[2];
+ cons5->type = LispCons_t;
+ CDR(cons5) = cons4;
+ cons6 = &x_cons[1];
+ cons6->type = LispCons_t;
+ CDR(cons6) = cons5;
+ cons7 = &x_cons[0];
+ cons7->type = LispCons_t;
+ CDR(cons7) = cons6;
+}
+
+LispObj *
+Lisp_Compile(LispBuiltin *builtin)
+/*
+ compile name &optional definition
+ */
+{
+ GC_ENTER();
+ LispObj *result, *warnings_p, *failure_p;
+
+ LispObj *name, *definition;
+
+ definition = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ result = name;
+ warnings_p = NIL;
+ failure_p = T;
+
+ if (name != NIL) {
+ LispAtom *atom;
+
+ CHECK_SYMBOL(name);
+ atom = name->data.atom;
+ if (atom->a_builtin || atom->a_compiled)
+ goto finished_compilation;
+ else if (atom->a_function) {
+ LispCom com;
+ int failed, *pfailed;
+ int lex, base, *plex, *pbase;
+ LispArgList *alist;
+ LispObj *lambda, *form, *arguments, **parguments;
+ LispObj **presult, **pwarnings_p, **pfailure_p, **pform;
+
+ lambda = atom->property->fun.function;
+ if (definition != UNSPEC || lambda->funtype != LispFunction)
+ /* XXX TODO replace definition etc. */
+ goto finished_compilation;
+ alist = atom->property->alist;
+
+ memset(&com, 0, sizeof(LispCom));
+ com.toplevel = com.block = LispCalloc(1, sizeof(CodeBlock));
+ com.block->type = LispBlockClosure;
+ com.block->tag = name;
+
+ /* Create a fake argument list to avoid yet another flag
+ * for ComCall. The value does not matter, just the fact
+ * that the symbol will be bound or not in the implicit
+ * PROGN of the function body. */
+ base = alist->num_arguments - alist->auxs.num_symbols;
+ if (base) {
+ LispObj *argument;
+ int i, sforms;
+
+ for (i = sforms = 0; i < alist->optionals.num_symbols; i++)
+ if (alist->optionals.sforms[i])
+ ++sforms;
+
+ arguments = form = NIL;
+ i = sforms +
+ alist->normals.num_symbols + alist->optionals.num_symbols;
+
+ if (i) {
+ arguments = form = CONS(NIL, NIL);
+ GC_PROTECT(arguments);
+ for (--i; i > 0; i--) {
+ RPLACD(form, CONS(NIL, NIL));
+ form = CDR(form);
+ }
+ }
+
+ for (i = 0; i < alist->keys.num_symbols; i++) {
+ /* key symbol */
+ if (alist->keys.keys[i])
+ argument = QUOTE(alist->keys.keys[i]);
+ else
+ argument = alist->keys.symbols[i];
+
+ /* add key */
+ if (arguments == NIL) {
+ arguments = form = CONS(argument, NIL);
+ GC_PROTECT(arguments);
+ }
+ else {
+ RPLACD(form, CONS(argument, NIL));
+ form = CDR(form);
+ }
+
+ /* add value */
+ RPLACD(form, CONS(NIL, NIL));
+ form = CDR(form);
+
+ if (alist->keys.sforms[i]) {
+ RPLACD(form, CONS(NIL, NIL));
+ form = CDR(form);
+ }
+ }
+
+ if (alist->rest) {
+ if (arguments == NIL) {
+ arguments = form = CONS(NIL, NIL);
+ GC_PROTECT(arguments);
+ }
+ else {
+ RPLACD(form, CONS(NIL, NIL));
+ form = CDR(form);
+ }
+ }
+ }
+ else
+ arguments = NIL;
+
+ form = CONS(lambda->data.lambda.code, NIL);
+ GC_PROTECT(form);
+ com.form = form;
+ com.plist = CONS(NIL, NIL);
+ GC_PROTECT(com.plist);
+
+ pfailed = &failed;
+ plex = &lex;
+ pbase = &base;
+ pform = &form;
+ presult = &result;
+ pwarnings_p = &warnings_p;
+ pfailure_p = &failure_p;
+ parguments = &arguments;
+ failed = 1;
+ if (setjmp(com.jmp) == 0) {
+ /* Save interpreter state */
+ lex = com.lex = lisp__data.env.lex;
+ base = ComCall(&com, alist, name, arguments, 1, 0, 1);
+
+ /* Generate code tree */
+ lisp__data.env.lex = base;
+ ComProgn(&com, CAR(form));
+ failed = 0;
+ }
+
+ /* Restore interpreter state */
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = base;
+
+ if (!failed) {
+ failure_p = NIL;
+ result = MakeBytecodeObject(&com, name,
+ lambda->data.lambda.data);
+ LispSetAtomCompiledProperty(atom, result);
+ result = name;
+ }
+ if (com.warnings)
+ warnings_p = FIXNUM(com.warnings);
+ goto finished_compilation;
+ }
+ else
+ goto undefined_function;
+ }
+
+undefined_function:
+ LispDestroy("%s: the function %s is undefined",
+ STRFUN(builtin), STROBJ(name));
+
+finished_compilation:
+ RETURN(0) = warnings_p;
+ RETURN(1) = failure_p;
+ RETURN_COUNT = 2;
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Disassemble(LispBuiltin *builtin)
+/*
+ disassemble function
+ */
+{
+ int macro;
+ char buffer[128];
+ LispAtom *atom;
+ LispArgList *alist;
+ LispBuiltin *xbuiltin;
+ LispObj *name, *lambda, *bytecode;
+
+ LispObj *function;
+
+ function = ARGUMENT(0);
+
+ macro = 0;
+ alist = NULL;
+ xbuiltin = NULL;
+ name = bytecode = NULL;
+
+ switch (OBJECT_TYPE(function)) {
+ case LispAtom_t:
+ name = function;
+ atom = function->data.atom;
+ alist = atom->property->alist;
+ if (atom->a_builtin) {
+ xbuiltin = atom->property->fun.builtin;
+ macro = xbuiltin->type == LispMacro;
+ }
+ else if (atom->a_compiled)
+ bytecode = atom->property->fun.function;
+ else if (atom->a_function) {
+ lambda = atom->property->fun.function;
+ macro = lambda->funtype == LispMacro;
+ }
+ else if (atom->a_defstruct &&
+ atom->property->structure.function != STRUCT_NAME) {
+ if (atom->property->structure.function == STRUCT_CONSTRUCTOR)
+ atom = Omake_struct->data.atom;
+ else if (atom->property->structure.function == STRUCT_CHECK)
+ atom = Ostruct_type->data.atom;
+ else
+ atom = Ostruct_access->data.atom;
+ xbuiltin = atom->property->fun.builtin;
+ }
+ else
+ LispDestroy("%s: the function %s is not defined",
+ STRFUN(builtin), STROBJ(function));
+ break;
+ case LispBytecode_t:
+ name = Olambda;
+ bytecode = function;
+ break;
+ case LispLambda_t:
+ name = Olambda;
+ alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
+ break;
+ case LispCons_t:
+ if (CAR(function) == Olambda) {
+ function = EVAL(function);
+ if (OBJECT_TYPE(function) == LispLambda_t) {
+ name = Olambda;
+ alist = (LispArgList*)
+ function->data.lambda.name->data.opaque.data;
+ break;
+ }
+ }
+ default:
+ LispDestroy("%s: %s is not a function",
+ STRFUN(builtin), STROBJ(function));
+ break;
+ }
+
+ if (xbuiltin) {
+ LispWriteStr(NIL, "Builtin ", 8);
+ if (macro)
+ LispWriteStr(NIL, "macro ", 6);
+ else
+ LispWriteStr(NIL, "function ", 9);
+ }
+ else if (macro)
+ LispWriteStr(NIL, "Macro ", 6);
+ else
+ LispWriteStr(NIL, "Function ", 9);
+ LispWriteObject(NIL, name);
+ LispWriteStr(NIL, ":\n", 2);
+
+ if (alist) {
+ int i;
+
+ sprintf(buffer, "%d required argument%s",
+ alist->normals.num_symbols,
+ alist->normals.num_symbols != 1 ? "s" : "");
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ 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])));
+ }
+ LispWriteChar(NIL, '\n');
+
+ sprintf(buffer, "%d optional argument%s",
+ alist->optionals.num_symbols,
+ alist->optionals.num_symbols != 1 ? "s" : "");
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ 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])));
+ }
+ LispWriteChar(NIL, '\n');
+
+ sprintf(buffer, "%d keyword parameter%s",
+ alist->keys.num_symbols,
+ alist->keys.num_symbols != 1 ? "s" : "");
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ for (i = 0; i < alist->keys.num_symbols; i++) {
+ LispWriteChar(NIL, i ? ',' : ':');
+ LispWriteChar(NIL, ' ');
+ LispWriteObject(NIL, alist->keys.symbols[i]);
+ }
+ LispWriteChar(NIL, '\n');
+
+ if (alist->rest) {
+ LispWriteStr(NIL, "Rest argument: ", 15);
+ LispWriteStr(NIL, ATOMID(alist->rest),
+ strlen(ATOMID(alist->rest)));
+ LispWriteChar(NIL, '\n');
+ }
+ else
+ LispWriteStr(NIL, "No rest argument\n", 17);
+ }
+
+ if (bytecode) {
+ char *ptr;
+ int *offsets[4];
+ int i, done, j, sym0, sym1, con0, con1, bui0, byt0, strd, strf;
+ LispObj **constants;
+ LispAtom **symbols;
+ LispBuiltin **builtins;
+ unsigned char **codes;
+ LispObj **names;
+ short stack, num_constants, num_symbols, num_builtins, num_bytecodes;
+ unsigned char *base, *stream = bytecode->data.bytecode.bytecode->code;
+
+ LispWriteStr(NIL, "\nBytecode header:\n", 18);
+
+ /* Header information */
+ stack = *(short*)stream;
+ stream += sizeof(short);
+ sprintf(buffer, "%d element%s used in the stack\n",
+ stack, stack != 1 ? "s" : "");
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ stack = *(short*)stream;
+ stream += sizeof(short);
+ sprintf(buffer, "%d element%s used in the builtin stack\n",
+ stack, stack != 1 ? "s" : "");
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ stack = *(short*)stream;
+ stream += sizeof(short);
+ sprintf(buffer, "%d element%s used in the protected stack\n",
+ stack, stack != 1 ? "s" : "");
+ LispWriteStr(NIL, buffer, strlen(buffer));
+
+ num_constants = *(short*)stream;
+ stream += sizeof(short);
+ num_symbols = *(short*)stream;
+ stream += sizeof(short);
+ num_builtins = *(short*)stream;
+ stream += sizeof(short);
+ num_bytecodes = *(short*)stream;
+ stream += sizeof(short);
+
+ constants = (LispObj**)stream;
+ stream += num_constants * sizeof(LispObj*);
+ symbols = (LispAtom**)stream;
+ stream += num_symbols * sizeof(LispAtom*);
+ builtins = (LispBuiltin**)stream;
+ stream += num_builtins * sizeof(LispBuiltin*);
+ codes = (unsigned char**)stream;
+ stream += num_bytecodes * sizeof(unsigned char*);
+ names = (LispObj**)stream;
+ stream += num_bytecodes * sizeof(LispObj*);
+
+ for (i = 0; i < num_constants; i++) {
+ sprintf(buffer, "Constant %d = %s\n", i, STROBJ(constants[i]));
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ }
+
+/* Macro XSTRING avoids some noisy in the output, if it were defined as
+ * #define XSTRING(object) object ? STROBJ(object) : #<UNBOUND>
+ * 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 : "#<UNBOUND>"
+
+ for (i = 0; i < num_symbols; i++) {
+ sprintf(buffer, "Symbol %d = %s\n",
+ i, XSTRING(symbols[i]->string));
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ }
+ for (i = 0; i < num_builtins; i++) {
+ sprintf(buffer, "Builtin %d = %s\n",
+ i, STROBJ(builtins[i]->symbol));
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ }
+ for (i = 0; i < num_bytecodes; i++) {
+ sprintf(buffer, "Bytecode %d = %s\n",
+ i, STROBJ(names[i]));
+ LispWriteStr(NIL, buffer, strlen(buffer));
+ }
+
+ /* Make readability slightly easier printing the names of local
+ * variables where it's offset is known, i.e. function arguments. */
+ if (alist) {
+ if (alist->num_arguments == 0)
+ LispWriteStr(NIL, "\nNo initial stack\n", 18);
+ else {
+ int len1, len2;
+
+ j = 0;
+ LispWriteStr(NIL, "\nInitial stack:\n", 16);
+
+ 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));
+ 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));
+ 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);
+ LispWriteChars(NIL, ' ', 28 - (len1 + len2));
+ LispWriteStr(NIL, "; sform\n", 9);
+ j++;
+ }
+ }
+
+ for (i = 0; i < alist->keys.num_symbols; i++, j++) {
+ sprintf(buffer, "%d = ", j);
+ 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));
+ LispWriteChars(NIL, ' ', 28 - (len1 + len2));
+ LispWriteStr(NIL, "; special key", 14);
+ }
+ else {
+ ptr = alist->keys.symbols[i]->data.atom->string;
+ LispWriteStr(NIL, ptr, strlen(ptr));
+ }
+ 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);
+ LispWriteChars(NIL, ' ', 28 - (len1 + len2));
+ LispWriteStr(NIL, "; sform\n", 9);
+ j++;
+ }
+ }
+
+ if (alist->rest) {
+ sprintf(buffer, "%d = ", j);
+ len1 = strlen(buffer);
+ LispWriteStr(NIL, buffer, len1);
+ ptr = alist->rest->data.atom->string;
+ len2 = strlen(ptr);
+ LispWriteStr(NIL, ptr, len2);
+ LispWriteChar(NIL, '\n');
+ j++;
+ }
+
+ for (i = 0; i < alist->auxs.num_symbols; i++, j++) {
+ 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);
+ LispWriteChars(NIL, ' ', 28 - (len1 + len2));
+ LispWriteStr(NIL, "; aux\n", 7);
+ }
+ }
+ }
+
+ LispWriteStr(NIL, "\nBytecode stream:\n", 18);
+
+ base = stream;
+ for (done = j = 0; !done; j = 0) {
+ sym0 = sym1 = con0 = con1 = bui0 = byt0 = strd = strf = -1;
+ sprintf(buffer, "%4ld ", (long)(stream - base));
+ ptr = buffer + strlen(buffer);
+ switch (*stream++) {
+ case XBC_NOOP: strcpy(ptr, "NOOP"); break;
+ case XBC_PRED:
+ strcpy(ptr, "PRED:");
+ ptr += strlen(ptr);
+ goto predicate;
+ case XBC_INV: strcpy(ptr, "INV"); break;
+ case XBC_NIL: strcpy(ptr, "NIL"); break;
+ case XBC_T: strcpy(ptr, "T"); break;
+ case XBC_CAR: strcpy(ptr, "CAR"); break;
+ case XBC_CDR: strcpy(ptr, "CDR"); break;
+ case XBC_RPLACA:strcpy(ptr, "RPLACA"); break;
+ case XBC_RPLACD:strcpy(ptr, "RPLACD"); break;
+ case XBC_EQ: strcpy(ptr, "EQ"); break;
+ case XBC_EQL: strcpy(ptr, "EQL"); break;
+ case XBC_EQUAL: strcpy(ptr, "EQUAL"); break;
+ case XBC_EQUALP:strcpy(ptr, "EQUALP"); break;
+ case XBC_LENGTH:strcpy(ptr, "LENGTH"); break;
+ case XBC_LAST: strcpy(ptr, "LAST"); break;
+ case XBC_NTHCDR:strcpy(ptr, "NTHCDR"); break;
+ case XBC_PUSH: strcpy(ptr, "PUSH"); break;
+ case XBC_CAR_PUSH:
+ strcpy(ptr, "CAR&PUSH");
+ break;
+ case XBC_CDR_PUSH:
+ strcpy(ptr, "CDR&PUSH");
+ break;
+ case XBC_PUSH_NIL:
+ strcpy(ptr, "PUSH NIL");
+ break;
+ case XBC_PUSH_UNSPEC:
+ strcpy(ptr, "PUSH #<UNSPEC>");
+ break;
+ case XBC_PUSH_T:
+ strcpy(ptr, "PUSH T");
+ break;
+ case XBC_PUSH_NIL_N:
+ strcpy(ptr, "PUSH NIL ");
+ ptr += strlen(ptr);
+ sprintf(ptr, "%d", (int)(*stream++));
+ break;
+ case XBC_PUSH_UNSPEC_N:
+ strcpy(ptr, "PUSH #<UNSPEC> ");
+ ptr += strlen(ptr);
+ sprintf(ptr, "%d", (int)(*stream++));
+ break;
+ case XBC_LET:
+ strcpy(ptr, "LET");
+/* update sym0 */
+symbol:
+ offsets[j++] = &sym0;
+/* update <offsets> - print [byte] */
+offset:
+ ptr += strlen(ptr);
+ i = *stream++;
+ *(offsets[j - 1]) = i;
+ sprintf(ptr, " [%d]", i);
+ break;
+ case XBC_LETX:
+ strcpy(ptr, "LET*");
+ goto symbol;
+ case XBC_LET_NIL:
+ strcpy(ptr, "LET NIL");
+ goto symbol;
+ case XBC_LETX_NIL:
+ strcpy(ptr, "LET* NIL");
+ goto symbol;
+ case XBC_LETBIND:
+ strcpy(ptr, "LETBIND");
+/* print byte */
+value:
+ ptr += strlen(ptr);
+ sprintf(ptr, " %d", (int)(*stream++));
+ break;
+ case XBC_UNLET:strcpy(ptr, "UNLET"); goto value;
+ case XBC_LOAD:
+ strcpy(ptr, "LOAD");
+/* print (byte) */
+reference:
+ ptr += strlen(ptr);
+ i = *stream++;
+ sprintf(ptr, " (%d)", i);
+ break;
+ case XBC_LOAD_CAR:
+ strcpy(ptr, "LOAD&CAR");
+ goto reference;
+ case XBC_LOAD_CDR:
+ strcpy(ptr, "LOAD&CDR");
+ goto reference;
+ case XBC_LOAD_CAR_STORE:
+ strcpy(ptr, "LOAD&CAR&STORE");
+ goto reference;
+ case XBC_LOAD_CDR_STORE:
+ strcpy(ptr, "LOAD&CDR&STORE");
+ goto reference;
+ case XBC_LOAD_LET:
+ strcpy(ptr, "LOAD&LET");
+load_let:
+ offsets[j++] = &sym0;
+ i = *stream++;
+ ptr += strlen(ptr);
+ sprintf(ptr, " (%d)", i);
+ goto offset;
+ case XBC_LOAD_LETX:
+ strcpy(ptr, "LOAD&LET*");
+ goto load_let;
+ case XBC_STRUCT:
+ strcpy(ptr, "STRUCT");
+ offsets[j++] = &strf;
+ offsets[j++] = &strd;
+/* update <offsets> - print [byte] - update <offsets> - print [byte] */
+offset_offset:
+ ptr += strlen(ptr);
+ i = *stream++;
+ *(offsets[j - 2]) = i;
+ sprintf(ptr, " [%d]", i);
+ goto offset;
+ case XBC_LOAD_PUSH:
+ strcpy(ptr, "LOAD&PUSH");
+ goto reference;
+ case XBC_LOADCON:
+ strcpy(ptr, "LOADCON");
+constant:
+ offsets[j++] = &con0;
+ goto offset;
+ case XBC_LOADCON_SET:
+ strcpy(ptr, "LOADCON&SET");
+ offsets[j++] = &con0;
+/* update <offsets> - print [byte] - print (byte) */
+offset_reference:
+ i = *stream++;
+ *(offsets[j - 1]) = i;
+ ptr += strlen(ptr);
+ sprintf(ptr, " [%d]", i);
+ goto reference;
+ case XBC_STRUCTP:
+ strcpy(ptr, "STRUCTP");
+ offsets[j++] = &strd;
+ goto offset;
+ case XBC_LOADCON_LET:
+ strcpy(ptr, "LOADCON&LET");
+loadcon_let:
+ offsets[j++] = &con0;
+ offsets[j++] = &sym0;
+ goto offset_offset;
+ case XBC_LOADCON_LETX:
+ strcpy(ptr, "LOADCON&LET*");
+ goto loadcon_let;
+ case XBC_LOADCON_PUSH:
+ strcpy(ptr, "LOADCON&PUSH");
+ goto constant;
+ case XBC_LOADSYM:
+ strcpy(ptr, "LOADSYM");
+ goto symbol;
+ case XBC_LOADSYM_LET:
+ strcpy(ptr, "LOADSYM&LET");
+loadsym_let:
+ offsets[j++] = &sym0;
+ offsets[j++] = &sym1;
+ goto offset_offset;
+ case XBC_LOADSYM_LETX:
+ strcpy(ptr, "LOADSYM&LET*");
+ goto loadsym_let;
+ case XBC_LOADSYM_PUSH:
+ strcpy(ptr, "LOADSYM&PUSH");
+ goto symbol;
+ case XBC_LOAD_SET:
+ strcpy(ptr, "LOAD&SET");
+/* print (byte) - print (byte) */
+reference_reference:
+ ptr += strlen(ptr);
+ i = *stream++;
+ sprintf(ptr, " (%d)", i);
+ goto reference;
+ case XBC_LOAD_CAR_SET:
+ strcpy(ptr, "LOAD&CAR&SET");
+ goto reference_reference;
+ case XBC_LOAD_CDR_SET:
+ strcpy(ptr, "LOAD&CDR&SET");
+ goto reference_reference;
+ case XBC_CAR_SET:
+ strcpy(ptr, "CAR&SET");
+ goto reference;
+ case XBC_CDR_SET:
+ strcpy(ptr, "CDR&SET");
+ goto reference;
+ case XBC_SET:
+ strcpy(ptr, "SET");
+ goto reference;
+ case XBC_SETSYM:
+ strcpy(ptr, "SETSYM");
+ goto symbol;
+ case XBC_SET_NIL:
+ strcpy(ptr, "SET NIL");
+ goto reference;
+ case XBC_CALL:
+ strcpy(ptr, "CALL");
+ ptr += strlen(ptr);
+ sprintf(ptr, " %d", (int)(*stream++));
+ offsets[j++] = &bui0;
+ goto offset;
+ case XBC_CALL_SET:
+ strcpy(ptr, "CALL&SET");
+ ptr += strlen(ptr);
+ sprintf(ptr, " %d", (int)(*stream++));
+ offsets[j++] = &bui0;
+ goto offset_reference;
+ case XBC_BYTECALL:
+ strcpy(ptr, "BYTECALL");
+ ptr += strlen(ptr);
+ sprintf(ptr, " %d", (int)(*stream++));
+ offsets[j++] = &byt0;
+ goto offset;
+ case XBC_FUNCALL:
+ strcpy(ptr, "FUNCALL");
+constant_constant:
+ offsets[j++] = &con0;
+ offsets[j++] = &con1;
+ goto offset_offset;
+ case XBC_CCONS:
+ strcpy(ptr, "CCONS");
+ goto constant_constant;
+ case XBC_CSTAR: strcpy(ptr, "CSTAR"); break;
+ case XBC_CFINI: strcpy(ptr, "CFINI"); break;
+ case XBC_LSTAR: strcpy(ptr, "LSTAR"); break;
+ case XBC_LCONS: strcpy(ptr, "LCONS"); break;
+ case XBC_LFINI: strcpy(ptr, "LFINI"); break;
+ case XBC_BCONS: strcpy(ptr, "BCONS"); break;
+ case XBC_BCONS1: case XBC_BCONS2: case XBC_BCONS3:
+ case XBC_BCONS4: case XBC_BCONS5: case XBC_BCONS6:
+ case XBC_BCONS7:
+ strcpy(ptr, "BCONS");
+ ptr += strlen(ptr);
+ sprintf(ptr, "%d", (int)(stream[-1] - XBC_BCONS));
+ break;
+ case XBC_JUMP:
+ strcpy(ptr, "JUMP");
+integer:
+ ptr += strlen(ptr);
+ sprintf(ptr, " %d", *(signed short*)stream);
+ stream += sizeof(short);
+ break;
+ case XBC_JUMPT:
+ strcpy(ptr, "JUMPT");
+ goto integer;
+ case XBC_JUMPNIL:
+ strcpy(ptr, "JUMPNIL");
+ goto integer;
+ case XBC_LETREC:
+ strcpy(ptr, "LETREC");
+ ptr += strlen(ptr);
+ sprintf(ptr, " %d", (int)*stream++);
+ break;
+ case XBC_RETURN:
+ strcpy(ptr, "RETURN");
+ done = 1;
+ break;
+ }
+ i = ptr - buffer + strlen(ptr);
+ LispWriteStr(NIL, buffer, i);
+ if (j) {
+
+ /* Pad */
+ LispWriteChars(NIL, ' ', 28 - i);
+ LispWriteChar(NIL, ';');
+
+ ptr = buffer;
+
+ /* Structure */
+ if (strf >= 0) {
+ /* strd is valid if strf set */
+ LispObj *fields = constants[strd];
+
+ for (; strf >= 0; strf--)
+ fields = CDR(fields);
+ strcpy(ptr, " "); ptr += 2;
+ strcpy(ptr, CAR(fields)->data.atom->string);
+ ptr += strlen(ptr);
+ }
+ if (strd >= 0) {
+ strcpy(ptr, " "); ptr += 2;
+ strcpy(ptr, STROBJ(CAR(constants[strd])));
+ ptr += strlen(ptr);
+ }
+
+ /* Constants */
+ if (con0 >= 0) {
+ strcpy(ptr, " "); ptr += 2;
+ strcpy(ptr, STROBJ(constants[con0]));
+ ptr += strlen(ptr);
+ if (con1 >= 0) {
+ strcpy(ptr, " "); ptr += 2;
+ strcpy(ptr, STROBJ(constants[con1]));
+ ptr += strlen(ptr);
+ }
+ }
+
+ /* Builtin */
+ if (bui0 >= 0) {
+ strcpy(ptr, " "); ptr += 2;
+ strcpy(ptr, STROBJ(builtins[bui0]->symbol));
+ ptr += strlen(ptr);
+ }
+
+ /* Bytecode */
+ if (byt0 >= 0) {
+ strcpy(ptr, " "); ptr += 2;
+ strcpy(ptr, STROBJ(names[byt0]));
+ ptr += strlen(ptr);
+ }
+
+ /* Symbols */
+ if (sym0 >= 0) {
+ strcpy(ptr, " "); ptr += 2;
+ strcpy(ptr, XSTRING(symbols[sym0]->string));
+ ptr += strlen(ptr);
+ if (sym1 >= 0) {
+ strcpy(ptr, " "); ptr += 2;
+ strcpy(ptr, XSTRING(symbols[sym1]->string));
+ ptr += strlen(ptr);
+ }
+ }
+
+ i = ptr - buffer;
+ LispWriteStr(NIL, buffer, i);
+ }
+ LispWriteChar(NIL, '\n');
+ continue;
+predicate:
+ switch (*stream++) {
+ case XBP_CONSP: strcpy(ptr, "CONSP"); break;
+ case XBP_LISTP: strcpy(ptr, "LISTP"); break;
+ case XBP_NUMBERP: strcpy(ptr, "NUMBERP"); break;
+ }
+ LispWriteStr(NIL, buffer, ptr - buffer + strlen(ptr));
+ LispWriteChar(NIL, '\n');
+ }
+#undef XSTRING
+ }
+
+ return (function);
+}
+
+
+
+LispObj *
+LispCompileForm(LispObj *form)
+{
+ GC_ENTER();
+ int failed, *pfailed;
+ LispCom com;
+ LispObj *code, **pform;
+
+ if (!CONSP(form))
+ /* Incorrect call or NIL */
+ return (form);
+
+ memset(&com, 0, sizeof(LispCom));
+
+ com.toplevel = com.block = LispCalloc(1, sizeof(CodeBlock));
+ com.block->type = LispBlockNone;
+ com.lex = lisp__data.env.lex;
+
+ com.plist = CONS(NIL, NIL);
+ GC_PROTECT(com.plist);
+
+ pfailed = &failed;
+ pform = &form;
+ failed = 1;
+ if (setjmp(com.jmp) == 0) {
+ for (code = form; CONSP(form); form = CDR(form)) {
+ com.form = form;
+ ComEval(&com, CAR(form));
+ }
+ failed = 0;
+ }
+ GC_LEAVE();
+
+ return (failed ? NIL : MakeBytecodeObject(&com, NIL, NIL));
+}
+
+LispObj *
+LispExecuteBytecode(LispObj *object)
+{
+ if (!BYTECODEP(object))
+ return (EVAL(object));
+
+ return (ExecuteBytecode(object->data.bytecode.bytecode->code));
+}
+
+static LispObj *
+MakeBytecodeObject(LispCom *com, LispObj *name, LispObj *plist)
+{
+ LispObj *object;
+ LispBytecode *bytecode;
+
+ GC_ENTER();
+ unsigned char *stream;
+ short i, num_constants;
+ LispObj **constants, *code, *cons, *prev;
+
+ /* Resolve dependencies, optimize and create byte stream */
+ LinkBytecode(com);
+
+ object = LispNew(NIL, NIL);
+ GC_PROTECT(object);
+ bytecode = LispMalloc(sizeof(LispBytecode));
+ bytecode->code = com->bytecode;
+ bytecode->length = com->length;
+
+
+ stream = bytecode->code;
+
+ /* Skip stack information */
+ stream += sizeof(short) * 3;
+
+ /* Get information */
+ num_constants = *(short*)stream;
+ stream += sizeof(short) * 4;
+ constants = (LispObj**)stream;
+
+ GC_PROTECT(plist);
+ code = cons = prev = NIL;
+ for (i = 0; i < num_constants; i++) {
+ if (POINTERP(constants[i]) && !XSYMBOLP(constants[i])) {
+ if (code == NIL) {
+ code = cons = prev = CONS(constants[i], NIL);
+ GC_PROTECT(code);
+ }
+ else {
+ RPLACD(cons, CONS(constants[i], NIL));
+ prev = cons;
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ /* Protect this in case the function is redefined */
+ for (i = 0; i < com->table.num_bytecodes; i++) {
+ if (code == NIL) {
+ code = cons = prev = CONS(com->table.bytecodes[i], NIL);
+ GC_PROTECT(code);
+ }
+ else {
+ RPLACD(cons, CONS(com->table.bytecodes[i], NIL));
+ prev = cons;
+ cons = CDR(cons);
+ }
+ }
+
+ /* Free everything, but the LispCom structure and the generated bytecode */
+ CompileFreeState(com);
+
+ /* Allocate the minimum required number of cons cells to protect objects */
+ if (!CONSP(code))
+ code = plist;
+ else if (CONSP(plist)) {
+ if (code == cons)
+ RPLACD(code, plist);
+ else
+ RPLACD(cons, plist);
+ }
+ else {
+ if (code == cons)
+ code = CAR(code);
+ else
+ CDR(prev) = CAR(cons);
+ }
+
+ object->data.bytecode.bytecode = bytecode;
+ /* Byte code references this object, so it cannot be garbage collected */
+ object->data.bytecode.code = code;
+ object->data.bytecode.name = name;
+ object->type = LispBytecode_t;
+
+ LispMused(bytecode);
+ LispMused(bytecode->code);
+ GC_LEAVE();
+
+ return (object);
+}
+
+static void
+CompileFreeTree(CodeTree *tree)
+{
+ if (tree->type == CodeTreeBlock)
+ CompileFreeBlock(tree->data.block);
+ LispFree(tree);
+}
+
+static void
+CompileFreeBlock(CodeBlock *block)
+{
+ CodeTree *tree = block->tree, *next;
+
+ while (tree) {
+ next = tree->next;
+ CompileFreeTree(tree);
+ tree = next;
+ }
+ if (block->type == LispBlockBody) {
+ LispFree(block->tagbody.labels);
+ LispFree(block->tagbody.codes);
+ }
+ LispFree(block->variables.symbols);
+ LispFree(block->variables.flags);
+ LispFree(block);
+}
+
+static void
+CompileFreeState(LispCom *com)
+{
+ CompileFreeBlock(com->block);
+ LispFree(com->table.constants);
+ LispFree(com->table.symbols);
+ LispFree(com->table.builtins);
+ LispFree(com->table.bytecodes);
+}
+
+/* XXX Put a breakpoint here when changing the macro expansion code.
+ * No opcodes should be generated during macro expansion. */
+static CodeTree *
+CompileNewTree(LispCom *com, CodeTreeType type)
+{
+ CodeTree *tree = LispMalloc(sizeof(CodeTree));
+
+ tree->type = type;
+ tree->next = NULL;
+ tree->block = com->block;
+ if (com->block->tree == NULL)
+ com->block->tree = tree;
+ else
+ com->block->tail->next = tree;
+ com->block->tail = tree;
+
+ return (tree);
+}
+
+static void
+CompileIniBlock(LispCom *com, LispBlockType type, LispObj *tag)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBlock);
+ CodeBlock *block = LispCalloc(1, sizeof(CodeBlock));
+
+ tree->data.block = block;
+
+ block->type = type;
+ block->tag = tag;
+ block->prev = com->block;
+ block->parent = tree;
+ block->level = com->level;
+ com->block = block;
+
+ if (type == LispBlockBody)
+ com->tagbody = com->level;
+}
+
+static void
+CompileFiniBlock(LispCom *com)
+{
+ com->block = com->block->prev;
+ if (com->block && com->block->type == LispBlockBody)
+ com->tagbody = com->block->level;
+}
+
+static void
+com_BytecodeChar(LispCom *com, LispByteOpcode code, char value)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = code;
+ tree->data.signed_char = value;
+}
+
+static void
+com_BytecodeShort(LispCom *com, LispByteOpcode code, short value)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = code;
+ tree->data.signed_short = value;
+}
+
+static void
+com_BytecodeAtom(LispCom *com, LispByteOpcode code, LispAtom *atom)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = code;
+ tree->data.atom = atom;
+}
+
+static void
+com_BytecodeObject(LispCom *com, LispByteOpcode code, LispObj *object)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = code;
+ tree->data.object = object;
+}
+
+static void
+com_BytecodeCons(LispCom *com, LispByteOpcode code, LispObj *car, LispObj *cdr)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = code;
+ tree->data.cons.car = car;
+ tree->data.cons.cdr = cdr;
+}
+
+static void
+com_Bytecode(LispCom *com, LispByteOpcode code)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = code;
+}
+
+static void
+com_Load(LispCom *com, short offset)
+{
+ com_BytecodeShort(com, XBC_LOAD, offset);
+}
+
+static void
+com_LoadLet(LispCom *com, short offset, LispAtom *name)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = XBC_LOAD_LET;
+ tree->data.let.offset = offset;
+ tree->data.let.name = name;
+}
+
+static void
+com_LoadPush(LispCom *com, short offset)
+{
+ com_BytecodeShort(com, XBC_LOAD_PUSH, offset);
+}
+
+static void
+com_Let(LispCom *com, LispAtom *name)
+{
+ com_BytecodeAtom(com, XBC_LET, name);
+}
+
+static void
+com_Bind(LispCom *com, short count)
+{
+ if (count)
+ com_BytecodeShort(com, XBC_LETBIND, count);
+}
+
+static void
+com_Unbind(LispCom *com, short count)
+{
+ if (count)
+ com_BytecodeShort(com, XBC_UNLET, count);
+}
+
+static void
+com_LoadSym(LispCom *com, LispAtom *atom)
+{
+ com_BytecodeAtom(com, XBC_LOADSYM, atom);
+}
+
+static void
+com_LoadSymLet(LispCom *com, LispAtom *symbol, LispAtom *name)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = XBC_LOADSYM_LET;
+ tree->data.let_sym.symbol = symbol;
+ tree->data.let_sym.name = name;
+}
+
+static void
+com_LoadSymPush(LispCom *com, LispAtom *name)
+{
+ com_BytecodeAtom(com, XBC_LOADSYM_PUSH, name);
+}
+
+static void
+com_LoadCon(LispCom *com, LispObj *constant)
+{
+ if (constant == NIL)
+ com_Bytecode(com, XBC_NIL);
+ else if (constant == T)
+ com_Bytecode(com, XBC_T);
+ else if (constant == UNSPEC) {
+ COMPILE_FAILURE("internal error: loading #<UNSPEC>");
+ }
+ else
+ com_BytecodeObject(com, XBC_LOADCON, constant);
+}
+
+static void
+com_LoadConLet(LispCom *com, LispObj *constant, LispAtom *name)
+{
+ if (constant == NIL)
+ com_BytecodeAtom(com, XBC_LET_NIL, name);
+ else {
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = XBC_LOADCON_LET;
+ tree->data.let_con.object = constant;
+ tree->data.let_con.name = name;
+ }
+}
+
+static void
+com_LoadConPush(LispCom *com, LispObj *constant)
+{
+ if (constant == NIL)
+ com_Bytecode(com, XBC_PUSH_NIL);
+ else if (constant == T)
+ com_Bytecode(com, XBC_PUSH_T);
+ else if (constant == UNSPEC)
+ com_Bytecode(com, XBC_PUSH_UNSPEC);
+ else
+ com_BytecodeObject(com, XBC_LOADCON_PUSH, constant);
+}
+
+static void
+com_Set(LispCom *com, short offset)
+{
+ com_BytecodeShort(com, XBC_SET, offset);
+}
+
+static void
+com_SetSym(LispCom *com, LispAtom *symbol)
+{
+ com_BytecodeAtom(com, XBC_SETSYM, symbol);
+}
+
+static void
+com_Struct(LispCom *com, short offset, LispObj *definition)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = XBC_STRUCT;
+ tree->data.struc.offset = offset;
+ tree->data.struc.definition = definition;
+}
+
+static void
+com_Structp(LispCom *com, LispObj *definition)
+{
+ com_BytecodeObject(com, XBC_STRUCTP, definition);
+}
+
+static void
+com_Call(LispCom *com, unsigned char num_arguments, LispBuiltin *builtin)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = XBC_CALL;
+ tree->data.builtin.num_arguments = num_arguments;
+ tree->data.builtin.builtin = builtin;
+}
+
+static void
+com_Bytecall(LispCom *com, unsigned char num_arguments, LispObj *code)
+{
+ CodeTree *tree = NEW_TREE(CodeTreeBytecode);
+
+ tree->code = XBC_BYTECALL;
+ tree->data.bytecall.num_arguments = num_arguments;
+ tree->data.bytecall.code = code;
+}
+
+static void
+com_Funcall(LispCom *com, LispObj *function, LispObj *arguments)
+{
+ com_BytecodeCons(com, XBC_FUNCALL, function, arguments);
+}
+
+static void
+CompileStackEnter(LispCom *com, int count, int builtin)
+{
+ if (!com->macro) {
+ if (builtin) {
+ com->stack.cbstack += count;
+ if (com->stack.bstack < com->stack.cbstack)
+ com->stack.bstack = com->stack.cbstack;
+ }
+ else {
+ com->stack.cstack += count;
+ if (com->stack.stack < com->stack.cstack)
+ com->stack.stack = com->stack.cstack;
+ }
+ }
+}
+
+static void
+CompileStackLeave(LispCom *com, int count, int builtin)
+{
+ if (!com->macro) {
+ if (builtin)
+ com->stack.cbstack -= count;
+ else
+ com->stack.cstack -= count;
+ }
+}
+
+static void
+LinkWarnUnused(LispCom *com, CodeBlock *block)
+{
+ int i;
+ CodeTree *tree;
+
+ for (tree = block->tree; tree; tree = tree->next) {
+ if (tree->type == CodeTreeBlock)
+ LinkWarnUnused(com, tree->data.block);
+ }
+
+ for (i = 0; i < block->variables.length; i++)
+ if (!(block->variables.flags[i] & (VARIABLE_USED | VARIABLE_ARGUMENT))) {
+ ++com->warnings;
+ LispWarning("the variable %s is unused",
+ block->variables.symbols[i]->string);
+ }
+}
+
+#define INTERNAL_ERROR_STRING "COMPILE: internal error #%d"
+#define INTERNAL_ERROR(value) LispDestroy(INTERNAL_ERROR_STRING, value)
+static long
+LinkBuildOffsets(LispCom *com, CodeTree *tree, long offset)
+{
+ for (; tree; tree = tree->next) {
+ tree->offset = offset;
+ switch (tree->type) {
+ case CodeTreeBytecode:
+ switch (tree->code) {
+ case XBC_NOOP:
+ INTERNAL_ERROR(__LINE__);
+ break;
+
+ /* byte */
+ case XBC_BCONS:
+ case XBC_BCONS1:
+ case XBC_BCONS2:
+ case XBC_BCONS3:
+ case XBC_BCONS4:
+ case XBC_BCONS5:
+ case XBC_BCONS6:
+ case XBC_BCONS7:
+ case XBC_INV:
+ case XBC_NIL:
+ case XBC_T:
+ case XBC_PUSH:
+ case XBC_CAR_PUSH:
+ case XBC_CDR_PUSH:
+ case XBC_PUSH_NIL:
+ case XBC_PUSH_UNSPEC:
+ case XBC_PUSH_T:
+ case XBC_LSTAR:
+ case XBC_LCONS:
+ case XBC_LFINI:
+ case XBC_RETURN:
+ case XBC_CSTAR:
+ case XBC_CFINI:
+ case XBC_CAR:
+ case XBC_CDR:
+ case XBC_RPLACA:
+ case XBC_RPLACD:
+ case XBC_EQ:
+ case XBC_EQL:
+ case XBC_EQUAL:
+ case XBC_EQUALP:
+ case XBC_LENGTH:
+ case XBC_LAST:
+ case XBC_NTHCDR:
+ ++offset;
+ break;
+
+ /* byte + byte */
+ case XBC_PUSH_NIL_N:
+ case XBC_PUSH_UNSPEC_N:
+ case XBC_PRED:
+ case XBC_LETREC:
+ case XBC_LOAD_PUSH:
+ case XBC_CAR_SET:
+ case XBC_CDR_SET:
+ case XBC_SET:
+ case XBC_SET_NIL:
+ case XBC_LETBIND:
+ case XBC_UNLET:
+ case XBC_LOAD:
+ case XBC_LOAD_CAR:
+ case XBC_LOAD_CDR:
+ case XBC_LOAD_CAR_STORE:
+ case XBC_LOAD_CDR_STORE:
+ case XBC_LET:
+ case XBC_LETX:
+ case XBC_LET_NIL:
+ case XBC_LETX_NIL:
+ case XBC_STRUCTP:
+ case XBC_SETSYM:
+ case XBC_LOADCON_PUSH:
+ case XBC_LOADSYM_PUSH:
+ case XBC_LOADCON:
+ case XBC_LOADSYM:
+ offset += 2;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_CALL:
+ case XBC_BYTECALL:
+ case XBC_LOAD_SET:
+ case XBC_LOAD_CAR_SET:
+ case XBC_LOAD_CDR_SET:
+ case XBC_LOADCON_SET:
+ case XBC_LOAD_LET:
+ case XBC_LOAD_LETX:
+ case XBC_STRUCT:
+ case XBC_LOADCON_LET:
+ case XBC_LOADCON_LETX:
+ case XBC_LOADSYM_LET:
+ case XBC_LOADSYM_LETX:
+ case XBC_CCONS:
+ case XBC_FUNCALL:
+ offset += 3;
+ break;
+
+ /* byte + short */
+ case XBC_JUMP:
+ case XBC_JUMPT:
+ case XBC_JUMPNIL:
+ /* XXX this is likely a jump to random address here */
+ INTERNAL_ERROR(__LINE__);
+ offset += sizeof(short) + 1;
+ break;
+
+ /* byte + byte + byte + byte */
+ case XBC_CALL_SET:
+ offset += 4;
+ break;
+ }
+ break;
+ case CodeTreeLabel:
+ /* Labels are not loaded */
+ break;
+ case CodeTreeJump:
+ case CodeTreeJumpIf:
+ case CodeTreeCond:
+ /* If not the point where the conditional block finishes */
+ if (tree->code != XBC_NOOP)
+ /* Reserve space for the jump opcode */
+ offset += sizeof(short) + 1;
+ break;
+ case CodeTreeGo:
+ case CodeTreeReturn:
+ /* Reserve space for the jump opcode */
+ offset += sizeof(short) + 1;
+ break;
+ case CodeTreeBlock:
+ offset = LinkBuildOffsets(com, tree->data.block->tree, offset);
+ break;
+ }
+ }
+
+ return (offset);
+}
+
+static void
+LinkDoOptimize_0(LispCom *com, CodeBlock *block)
+{
+ CodeTree *tree, *prev, *next;
+
+ /* Remove redundant or join opcodes that can be joined. Do it here
+ * because some of these are hard to detect earlier, and/or would
+ * require a lot of duplicated code or more time. */
+ tree = prev = block->tree;
+ while (tree) {
+ next = tree->next;
+
+ /* LET -> LET* */
+ if (next &&
+ next->type == CodeTreeBytecode &&
+ next->code == XBC_LETBIND &&
+ next->data.signed_short == 1) {
+ switch (tree->code) {
+ case XBC_LET:
+ tree->code = XBC_LETX;
+ goto remove_next_label;
+ case XBC_LET_NIL:
+ tree->code = XBC_LETX_NIL;
+ goto remove_next_label;
+ case XBC_LOAD_LET:
+ tree->code = XBC_LOAD_LETX;
+ goto remove_next_label;
+ case XBC_LOADCON_LET:
+ tree->code = XBC_LOADCON_LETX;
+ goto remove_next_label;
+ case XBC_LOADSYM_LET:
+ tree->code = XBC_LOADSYM_LETX;
+ goto remove_next_label;
+ default:
+ break;
+ }
+ }
+
+ switch (tree->type) {
+ case CodeTreeBytecode:
+ switch (tree->code) {
+ case XBC_LOADCON:
+ if (next && next->type == CodeTreeBytecode) {
+ switch (next->code) {
+ case XBC_LET:
+ next->code = XBC_LOADCON_LET;
+ next->data.let_con.name =
+ next->data.atom;
+ next->data.let_con.object =
+ tree->data.object;
+ goto remove_label;
+ case XBC_PUSH:
+ next->code = XBC_LOADCON_PUSH;
+ next->data.object = tree->data.object;
+ goto remove_label;
+ case XBC_CAR:
+ if (tree->data.object != NIL) {
+ if (!CONSP(tree->data.object))
+ LispDestroy("CAR: %s is not a list",
+ STROBJ(
+ tree->data.object));
+ next->code = XBC_LOADCON;
+ next->data.object =
+ CAR(tree->data.object);
+ }
+ goto remove_label;
+ case XBC_CDR:
+ if (tree->data.object != NIL) {
+ if (!CONSP(tree->data.object))
+ LispDestroy("CAR: %s is not a list",
+ STROBJ(
+ tree->data.object));
+ next->code = XBC_LOADCON;
+ next->data.object =
+ CDR(tree->data.object);
+ }
+ goto remove_label;
+ case XBC_SET:
+ next->code = XBC_LOADCON_SET;
+ next->data.load_con_set.offset =
+ next->data.signed_short;
+ next->data.load_con_set.object =
+ tree->data.object;
+ goto remove_label;
+ default:
+ break;
+ }
+ }
+ break;
+ case XBC_LOADSYM:
+ if (next && next->type == CodeTreeBytecode) {
+ switch (next->code) {
+ case XBC_LET:
+ next->code = XBC_LOADSYM_LET;
+ next->data.let_sym.name =
+ next->data.atom;
+ next->data.let_sym.symbol =
+ tree->data.atom;
+ goto remove_label;
+ case XBC_PUSH:
+ next->code = XBC_LOADSYM_PUSH;
+ next->data.atom = tree->data.atom;
+ goto remove_label;
+ default:
+ break;
+ }
+ }
+ break;
+ case XBC_LOAD:
+ if (next && next->type == CodeTreeBytecode) {
+ switch (next->code) {
+ case XBC_SET:
+ next->code = XBC_LOAD_SET;
+ next->data.load_set.set =
+ next->data.signed_short;
+ next->data.load_set.load =
+ tree->data.signed_short;
+ goto remove_label;
+ /* TODO add XBC_LOAD_SETSYM */
+ case XBC_CAR:
+ next->code = XBC_LOAD_CAR;
+ next->data.signed_short =
+ tree->data.signed_short;
+ goto remove_label;
+ case XBC_CDR:
+ next->code = XBC_LOAD_CDR;
+ next->data.signed_short =
+ tree->data.signed_short;
+ goto remove_label;
+ case XBC_PUSH:
+ tree->code = XBC_LOAD_PUSH;
+ goto remove_next_label;
+ case XBC_LET:
+ next->code = XBC_LOAD_LET;
+ next->data.let.name = next->data.atom;
+ next->data.let.offset =
+ tree->data.signed_short;
+ goto remove_label;
+ default:
+ break;
+ }
+ }
+ break;
+ case XBC_LOAD_CAR:
+ if (next && next->type == CodeTreeBytecode &&
+ next->code == XBC_SET) {
+ if (next->data.signed_short ==
+ tree->data.signed_short)
+ next->code = XBC_LOAD_CAR_STORE;
+ else {
+ next->code = XBC_LOAD_CAR_SET;
+ next->data.load_set.set =
+ next->data.signed_short;
+ next->data.load_set.load =
+ tree->data.signed_short;
+ }
+ goto remove_label;
+ }
+ break;
+ case XBC_LOAD_CDR:
+ if (next && next->type == CodeTreeBytecode &&
+ next->code == XBC_SET) {
+ if (next->data.signed_short ==
+ tree->data.signed_short)
+ next->code = XBC_LOAD_CDR_STORE;
+ else {
+ next->code = XBC_LOAD_CDR_SET;
+ next->data.load_set.set =
+ next->data.signed_short;
+ next->data.load_set.load =
+ tree->data.signed_short;
+ }
+ goto remove_label;
+ }
+ break;
+ case XBC_CALL:
+ if (next && next->type == CodeTreeBytecode) {
+ switch (next->code) {
+ case XBC_SET:
+ next->code = XBC_CALL_SET;
+ next->data.builtin.offset =
+ next->data.signed_short;
+ next->data.builtin.num_arguments =
+ tree->data.builtin.num_arguments;
+ next->data.builtin.builtin =
+ tree->data.builtin.builtin;
+ goto remove_label;
+ /* TODO add XBC_CALL_SETSYM */
+ default:
+ break;
+ }
+ }
+ break;
+ case XBC_CAR:
+ if (next && next->type == CodeTreeBytecode) {
+ switch (next->code) {
+ case XBC_SET:
+ next->code = XBC_CAR_SET;
+ goto remove_label;
+ /* TODO add XBC_CAR_SETSYM */
+ case XBC_PUSH:
+ next->code = XBC_CAR_PUSH;
+ goto remove_label;
+ default:
+ break;
+ }
+ }
+ break;
+ case XBC_CDR:
+ if (next && next->type == CodeTreeBytecode) {
+ switch (next->code) {
+ case XBC_SET:
+ next->code = XBC_CDR_SET;
+ goto remove_label;
+ /* TODO add XBC_CDR_SETSYM */
+ case XBC_PUSH:
+ next->code = XBC_CDR_PUSH;
+ goto remove_label;
+ default:
+ break;
+ }
+ }
+ break;
+ case XBC_NIL:
+ if (next && next->type == CodeTreeBytecode) {
+ switch (next->code) {
+ case XBC_SET:
+ next->code = XBC_SET_NIL;
+ goto remove_label;
+ /* TODO add XBC_SETSYM_NIL */
+ default:
+ break;
+ }
+ }
+ break;
+ case XBC_PUSH_NIL:
+ if (next && next->type == CodeTreeBytecode &&
+ next->code == XBC_PUSH_NIL) {
+ next->code = XBC_PUSH_NIL_N;
+ next->data.signed_char = 2;
+ goto remove_label;
+ }
+ break;
+ case XBC_PUSH_NIL_N:
+ if (next && next->type == CodeTreeBytecode &&
+ next->code == XBC_PUSH_NIL) {
+ next->code = XBC_PUSH_NIL_N;
+ next->data.signed_char = tree->data.signed_char + 1;
+ goto remove_label;
+ }
+ break;
+ case XBC_PUSH_UNSPEC:
+ if (next && next->type == CodeTreeBytecode &&
+ next->code == XBC_PUSH_UNSPEC) {
+ next->code = XBC_PUSH_UNSPEC_N;
+ next->data.signed_char = 2;
+ goto remove_label;
+ }
+ break;
+ case XBC_PUSH_UNSPEC_N:
+ if (next && next->type == CodeTreeBytecode &&
+ next->code == XBC_PUSH_UNSPEC) {
+ next->code = XBC_PUSH_UNSPEC_N;
+ next->data.signed_char = tree->data.signed_char + 1;
+ goto remove_label;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case CodeTreeBlock:
+ LinkDoOptimize_0(com, tree->data.block);
+ break;
+ default:
+ break;
+ }
+ goto update_label;
+remove_label:
+ if (tree == block->tree) {
+ block->tree = prev = next;
+ if (tree == block->tail)
+ block->tail = tree;
+ }
+ else
+ prev->next = next;
+ CompileFreeTree(tree);
+ tree = next;
+ continue;
+remove_next_label:
+ tree->next = next->next;
+ CompileFreeTree(next);
+ continue;
+update_label:
+ prev = tree;
+ tree = tree->next;
+ }
+}
+
+static void
+LinkOptimize_0(LispCom *com)
+{
+ /* Recursive */
+ LinkDoOptimize_0(com, com->block);
+}
+
+static void
+LinkResolveLabels(LispCom *com, CodeBlock *block)
+{
+ int i;
+ CodeTree *tree = block->tree;
+
+ for (; tree; tree = tree->next) {
+ if (tree->type == CodeTreeBlock)
+ LinkResolveLabels(com, tree->data.block);
+ else if (tree->type == CodeTreeLabel) {
+ for (i = 0; i < block->tagbody.length; i++)
+ if (tree->data.object == block->tagbody.labels[i]) {
+ block->tagbody.codes[i] = tree;
+ break;
+ }
+ }
+ }
+}
+
+static void
+LinkResolveJumps(LispCom *com, CodeBlock *block)
+{
+ int i;
+ CodeBlock *body = block;
+ CodeTree *ptr, *tree = block->tree;
+
+ /* Check if there is a tagbody. Error checking already done */
+ while (body && body->type != LispBlockBody)
+ body = body->prev;
+
+ for (; tree; tree = tree->next) {
+ switch (tree->type) {
+ case CodeTreeBytecode:
+ case CodeTreeLabel:
+ break;
+
+ case CodeTreeBlock:
+ LinkResolveJumps(com, tree->data.block);
+ break;
+
+ case CodeTreeGo:
+ for (i = 0; i < body->tagbody.length; i++)
+ if (tree->data.object == body->tagbody.labels[i])
+ break;
+ if (i == body->tagbody.length)
+ LispDestroy("COMPILE: no visible tag %s to GO",
+ STROBJ(tree->data.object));
+ /* Now the jump code is known */
+ tree->data.tree = body->tagbody.codes[i];
+ break;
+
+ case CodeTreeCond:
+ if (tree->code == XBC_JUMPNIL)
+ /* If test is NIL, go to next test */
+ tree->data.tree = tree->group->next;
+ else if (tree->code == XBC_JUMPT) {
+ /* After executing code, test was T */
+ for (ptr = tree->group;
+ ptr->code != XBC_NOOP;
+ ptr = ptr->group)
+ ;
+ tree->data.tree = ptr;
+ }
+ break;
+
+ case CodeTreeJumpIf:
+ if (tree->code != XBC_NOOP) {
+ for (ptr = tree->group;
+ ptr->code != XBC_NOOP;
+ ptr = ptr->group) {
+ if (ptr->type == CodeTreeJump) {
+ /* ELSE code of IF */
+ ptr = ptr->next;
+ /* Skip inconditional jump node */
+ break;
+ }
+ }
+ tree->data.tree = ptr;
+ }
+ break;
+
+ case CodeTreeJump:
+ if (tree->code != XBC_NOOP)
+ tree->data.tree = tree->group;
+ break;
+
+ case CodeTreeReturn:
+ /* One bytecode is guaranteed to exist in the code tree */
+ if (tree->data.block->parent == NULL)
+ /* Returning from the function or toplevel form */
+ tree->data.tree = tree->data.block->tail;
+ else {
+ for (;;) {
+ ptr = tree->data.block->parent->next;
+ if (ptr) {
+ tree->data.tree = ptr;
+ break;
+ }
+ else
+ /* Move one BLOCK up */
+ tree->data.block = tree->data.block->prev;
+ }
+ }
+ break;
+ }
+ }
+}
+
+static long
+LinkPad(long offset, long adjust, int preffix, int datalen)
+{
+ /* If byte or aligned data */
+ if (datalen <= preffix || ((offset + adjust + preffix) % datalen) == 0)
+ return (adjust);
+
+ return (adjust + (datalen - ((offset + adjust + preffix) % datalen)));
+}
+
+static long
+LinkFixupOffsets(LispCom *com, CodeTree *tree, long adjust)
+{
+ for (; tree; tree = tree->next) {
+ switch (tree->type) {
+ case CodeTreeBytecode:
+ switch (tree->code) {
+ /* byte + short */
+ case XBC_JUMP:
+ case XBC_JUMPT:
+ case XBC_JUMPNIL:
+ adjust = LinkPad(tree->offset, adjust, 1,
+ sizeof(short));
+ /*FALLTROUGH*/
+ default:
+ tree->offset += adjust;
+ break;
+ }
+ break;
+ case CodeTreeLabel:
+ /* Labels are not loaded, just adjust offset */
+ tree->offset += adjust;
+ break;
+ case CodeTreeJump:
+ case CodeTreeCond:
+ case CodeTreeJumpIf:
+ /* If an opcode will be generated. */
+ if (tree->code != XBC_NOOP)
+ adjust = LinkPad(tree->offset, adjust, 1, sizeof(short));
+ tree->offset += adjust;
+ break;
+ case CodeTreeGo:
+ case CodeTreeReturn:
+ adjust = LinkPad(tree->offset, adjust, 1, sizeof(short));
+ tree->offset += adjust;
+ break;
+ case CodeTreeBlock:
+ adjust = LinkFixupOffsets(com, tree->data.block->tree, adjust);
+ break;
+ }
+ }
+
+ return (adjust);
+}
+
+static void
+LinkSkipPadding(LispCom *com, CodeTree *tree)
+{
+ int found;
+ CodeTree *ptr;
+
+ /* Recurse to adjust forward jumps or jumps to the start of the block */
+ for (ptr = tree; ptr; ptr = ptr->next) {
+ if (ptr->type == CodeTreeBlock) {
+ LinkSkipPadding(com, ptr->data.block->tree);
+ ptr->offset = ptr->data.block->tree->offset;
+ }
+ }
+
+ /* Adjust the nodes offsets */
+ for (; tree; tree = tree->next) {
+ switch (tree->type) {
+ case CodeTreeBytecode:
+ case CodeTreeBlock:
+ case CodeTreeGo:
+ case CodeTreeReturn:
+ break;
+ case CodeTreeJump:
+ case CodeTreeCond:
+ case CodeTreeJumpIf:
+ if (tree->code != XBC_NOOP)
+ /* If code will be generated */
+ break;
+ case CodeTreeLabel:
+ /* This should be done in reversed order, but to avoid
+ * the requirement of a prev pointer, do the job in a
+ * harder way here. */
+ for (found = 0, ptr = tree->next; ptr; ptr = ptr->next) {
+ switch (ptr->type) {
+ case CodeTreeBytecode:
+ case CodeTreeBlock:
+ case CodeTreeGo:
+ case CodeTreeReturn:
+ found = 1;
+ break;
+ case CodeTreeJump:
+ case CodeTreeCond:
+ case CodeTreeJumpIf:
+ if (ptr->code != XBC_NOOP)
+ found = 1;
+ break;
+ case CodeTreeLabel:
+ break;
+ }
+ if (found)
+ break;
+ }
+ if (found)
+ tree->offset = ptr->offset;
+ break;
+ }
+ }
+}
+
+static void
+LinkCalculateJump(LispCom *com, CodeTree *tree, LispByteOpcode code)
+{
+ long jumpto, offset, distance;
+
+ tree->type = CodeTreeBytecode;
+ /* After the opcode */
+ offset = tree->offset + 1;
+ jumpto = tree->data.tree->offset;
+ /* Effective distance */
+ distance = jumpto - offset;
+ tree->code = code;
+ if (distance < -32768 || distance > 32767) {
+ COMPILE_FAILURE("jump too long");
+ }
+ tree->data.signed_int = distance;
+}
+
+static void
+LinkFixupJumps(LispCom *com, CodeTree *tree)
+{
+ for (; tree; tree = tree->next) {
+ switch (tree->type) {
+ case CodeTreeBytecode:
+ case CodeTreeLabel:
+ break;
+ case CodeTreeCond:
+ if (tree->code == XBC_JUMPNIL)
+ /* Go to next test if NIL */
+ LinkCalculateJump(com, tree, XBC_JUMPNIL);
+ else if (tree->code == XBC_JUMPT)
+ /* After executing T code */
+ LinkCalculateJump(com, tree, XBC_JUMP);
+ break;
+ case CodeTreeJumpIf:
+ if (tree->code != XBC_NOOP)
+ LinkCalculateJump(com, tree, tree->code);
+ break;
+ case CodeTreeGo:
+ /* Inconditional jump */
+ LinkCalculateJump(com, tree, XBC_JUMP);
+ break;
+ case CodeTreeReturn:
+ /* Inconditional jump */
+ if (tree->data.tree != tree)
+ /* If need to skip something */
+ LinkCalculateJump(com, tree, XBC_JUMP);
+ break;
+ case CodeTreeBlock:
+ LinkFixupJumps(com, tree->data.block->tree);
+ break;
+ case CodeTreeJump:
+ if (tree->code != XBC_NOOP)
+ LinkCalculateJump(com, tree, tree->code);
+ }
+ }
+}
+
+static void
+LinkBuildTableSymbol(LispCom *com, LispAtom *symbol)
+{
+ if (BuildTablePointer(symbol, (void***)&com->table.symbols,
+ &com->table.num_symbols) > 0xff) {
+ COMPILE_FAILURE("more than 256 symbols");
+ }
+}
+
+static void
+LinkBuildTableConstant(LispCom *com, LispObj *constant)
+{
+ if (BuildTablePointer(constant, (void***)&com->table.constants,
+ &com->table.num_constants) > 0xff) {
+ COMPILE_FAILURE("more than 256 constants");
+ }
+}
+
+static void
+LinkBuildTableBuiltin(LispCom *com, LispBuiltin *builtin)
+{
+ if (BuildTablePointer(builtin, (void***)&com->table.builtins,
+ &com->table.num_builtins) > 0xff) {
+ COMPILE_FAILURE("more than 256 functions");
+ }
+}
+
+static void
+LinkBuildTableBytecode(LispCom *com, LispObj *bytecode)
+{
+ if (BuildTablePointer(bytecode, (void***)&com->table.bytecodes,
+ &com->table.num_bytecodes) > 0xff) {
+ COMPILE_FAILURE("more than 256 bytecode functions");
+ }
+}
+
+static void
+LinkBuildTables(LispCom *com, CodeBlock *block)
+{
+ CodeTree *tree;
+
+ for (tree = block->tree; tree; tree = tree->next) {
+ switch (tree->type) {
+ case CodeTreeBytecode:
+ switch (tree->code) {
+ case XBC_LET:
+ case XBC_LETX:
+ case XBC_LET_NIL:
+ case XBC_LETX_NIL:
+ case XBC_SETSYM:
+ case XBC_LOADSYM:
+ case XBC_LOADSYM_PUSH:
+ LinkBuildTableSymbol(com, tree->data.atom);
+ break;
+ case XBC_STRUCTP:
+ case XBC_LOADCON:
+ case XBC_LOADCON_PUSH:
+ LinkBuildTableConstant(com, tree->data.object);
+ break;
+ case XBC_LOADCON_SET:
+ LinkBuildTableConstant(com, tree->data.load_con_set.object);
+ break;
+ case XBC_CALL:
+ case XBC_CALL_SET:
+ LinkBuildTableBuiltin(com, tree->data.builtin.builtin);
+ break;
+ case XBC_BYTECALL:
+ LinkBuildTableBytecode(com, tree->data.bytecall.code);
+ break;
+ case XBC_LOAD_LET:
+ case XBC_LOAD_LETX:
+ LinkBuildTableSymbol(com, tree->data.let.name);
+ break;
+ case XBC_STRUCT:
+ LinkBuildTableConstant(com, tree->data.struc.definition);
+ break;
+ case XBC_LOADSYM_LET:
+ case XBC_LOADSYM_LETX:
+ LinkBuildTableSymbol(com, tree->data.let_sym.symbol);
+ LinkBuildTableSymbol(com, tree->data.let_sym.name);
+ break;
+ case XBC_LOADCON_LET:
+ case XBC_LOADCON_LETX:
+ LinkBuildTableConstant(com, tree->data.let_con.object);
+ LinkBuildTableSymbol(com, tree->data.let_con.name);
+ break;
+ case XBC_CCONS:
+ case XBC_FUNCALL:
+ LinkBuildTableConstant(com, tree->data.cons.car);
+ LinkBuildTableConstant(com, tree->data.cons.cdr);
+ break;
+ default:
+ break;
+ }
+ break;
+ case CodeTreeBlock:
+ LinkBuildTables(com, tree->data.block);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static long
+LinkEmmitBytecode(LispCom *com, CodeTree *tree,
+ unsigned char *bytecode, long offset)
+{
+ short i;
+
+ for (; tree; tree = tree->next) {
+ /* Fill padding */
+ while (offset < tree->offset)
+ bytecode[offset++] = XBC_NOOP;
+
+ switch (tree->type) {
+ case CodeTreeBytecode:
+ bytecode[offset++] = tree->code;
+ switch (tree->code) {
+ /* Noop should not enter the CodeTree */
+ case XBC_NOOP:
+ INTERNAL_ERROR(__LINE__);
+ break;
+
+ /* byte */
+ case XBC_BCONS:
+ case XBC_BCONS1:
+ case XBC_BCONS2:
+ case XBC_BCONS3:
+ case XBC_BCONS4:
+ case XBC_BCONS5:
+ case XBC_BCONS6:
+ case XBC_BCONS7:
+ case XBC_INV:
+ case XBC_NIL:
+ case XBC_T:
+ case XBC_PUSH_NIL:
+ case XBC_PUSH_UNSPEC:
+ case XBC_PUSH_T:
+ case XBC_CAR_PUSH:
+ case XBC_CDR_PUSH:
+ case XBC_PUSH:
+ case XBC_LSTAR:
+ case XBC_LCONS:
+ case XBC_LFINI:
+ case XBC_RETURN:
+ case XBC_CSTAR:
+ case XBC_CFINI:
+ case XBC_CAR:
+ case XBC_CDR:
+ case XBC_RPLACA:
+ case XBC_RPLACD:
+ case XBC_EQ:
+ case XBC_EQL:
+ case XBC_EQUAL:
+ case XBC_EQUALP:
+ case XBC_LENGTH:
+ case XBC_LAST:
+ case XBC_NTHCDR:
+ break;
+
+ /* byte + byte */
+ case XBC_LETREC:
+ case XBC_PRED:
+ case XBC_PUSH_NIL_N:
+ case XBC_PUSH_UNSPEC_N:
+ bytecode[offset++] = tree->data.signed_char;
+ break;
+
+ /* byte + byte */
+ case XBC_CAR_SET:
+ case XBC_CDR_SET:
+ case XBC_SET:
+ case XBC_SET_NIL:
+ case XBC_LETBIND:
+ case XBC_UNLET:
+ case XBC_LOAD_PUSH:
+ case XBC_LOAD:
+ case XBC_LOAD_CAR:
+ case XBC_LOAD_CDR:
+ case XBC_LOAD_CAR_STORE:
+ case XBC_LOAD_CDR_STORE:
+ bytecode[offset++] = tree->data.signed_short;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_LOAD_SET:
+ case XBC_LOAD_CAR_SET:
+ case XBC_LOAD_CDR_SET:
+ bytecode[offset++] = tree->data.load_set.load;
+ bytecode[offset++] = tree->data.load_set.set;
+ break;
+
+ /* byte + short */
+ case XBC_JUMP:
+ case XBC_JUMPT:
+ case XBC_JUMPNIL:
+ *(short*)(bytecode + offset) = tree->data.signed_int;
+ offset += sizeof(short);
+ break;
+
+ /* byte + byte */
+ case XBC_LET:
+ case XBC_LETX:
+ case XBC_LET_NIL:
+ case XBC_LETX_NIL:
+ case XBC_SETSYM:
+ case XBC_LOADSYM:
+ case XBC_LOADSYM_PUSH:
+ i = FindIndex(tree->data.atom,
+ (void**)com->table.symbols,
+ com->table.num_symbols);
+ bytecode[offset++] = i;
+ break;
+
+ /* byte + byte */
+ case XBC_STRUCTP:
+ case XBC_LOADCON:
+ case XBC_LOADCON_PUSH:
+ i = FindIndex(tree->data.object,
+ (void**)com->table.constants,
+ com->table.num_constants);
+ bytecode[offset++] = i;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_LOADCON_SET:
+ i = FindIndex(tree->data.load_con_set.object,
+ (void**)com->table.constants,
+ com->table.num_constants);
+ bytecode[offset++] = i;
+ bytecode[offset++] = tree->data.load_con_set.offset;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_CALL:
+ bytecode[offset++] = tree->data.builtin.num_arguments;
+ i = FindIndex(tree->data.builtin.builtin,
+ (void**)com->table.builtins,
+ com->table.num_builtins);
+ bytecode[offset++] = i;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_BYTECALL:
+ bytecode[offset++] = tree->data.bytecall.num_arguments;
+ i = FindIndex(tree->data.bytecall.code,
+ (void**)com->table.bytecodes,
+ com->table.num_bytecodes);
+ bytecode[offset++] = i;
+ break;
+
+ /* byte + byte + byte + byte */
+ case XBC_CALL_SET:
+ bytecode[offset++] = tree->data.builtin.num_arguments;
+ i = FindIndex(tree->data.builtin.builtin,
+ (void**)com->table.builtins,
+ com->table.num_builtins);
+ bytecode[offset++] = i;
+ bytecode[offset++] = tree->data.builtin.offset;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_LOAD_LET:
+ case XBC_LOAD_LETX:
+ bytecode[offset++] = tree->data.let.offset;
+ i = FindIndex(tree->data.let.name,
+ (void**)com->table.symbols,
+ com->table.num_symbols);
+ bytecode[offset++] = i;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_STRUCT:
+ bytecode[offset++] = tree->data.struc.offset;
+ i = FindIndex(tree->data.struc.definition,
+ (void**)com->table.constants,
+ com->table.num_constants);
+ bytecode[offset++] = i;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_LOADSYM_LET:
+ case XBC_LOADSYM_LETX:
+ i = FindIndex(tree->data.let_sym.symbol,
+ (void**)com->table.symbols,
+ com->table.num_symbols);
+ bytecode[offset++] = i;
+ i = FindIndex(tree->data.let_sym.name,
+ (void**)com->table.symbols,
+ com->table.num_symbols);
+ bytecode[offset++] = i;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_LOADCON_LET:
+ case XBC_LOADCON_LETX:
+ i = FindIndex(tree->data.let_con.object,
+ (void**)com->table.constants,
+ com->table.num_constants);
+ bytecode[offset++] = i;
+ i = FindIndex(tree->data.let_con.name,
+ (void**)com->table.symbols,
+ com->table.num_symbols);
+ bytecode[offset++] = i;
+ break;
+
+ /* byte + byte + byte */
+ case XBC_CCONS:
+ case XBC_FUNCALL:
+ i = FindIndex(tree->data.cons.car,
+ (void**)com->table.constants,
+ com->table.num_constants);
+ bytecode[offset++] = i;
+ i = FindIndex(tree->data.cons.cdr,
+ (void**)com->table.constants,
+ com->table.num_constants);
+ bytecode[offset++] = i;
+ break;
+ }
+ break;
+ case CodeTreeLabel:
+ /* Labels are not loaded */
+ break;
+ case CodeTreeCond:
+ case CodeTreeJump:
+ case CodeTreeJumpIf:
+ if (tree->code != XBC_NOOP)
+ INTERNAL_ERROR(__LINE__);
+ break;
+ case CodeTreeGo:
+ INTERNAL_ERROR(__LINE__);
+ break;
+ case CodeTreeReturn:
+ if (tree->data.tree != tree)
+ INTERNAL_ERROR(__LINE__);
+ break;
+ case CodeTreeBlock:
+ offset = LinkEmmitBytecode(com, tree->data.block->tree,
+ bytecode, offset);
+ break;
+ }
+ }
+
+ return (offset);
+}
+
+static void
+LinkBytecode(LispCom *com)
+{
+ long offset, count;
+ unsigned char **codes;
+ LispObj **names;
+
+ /* Close bytecode */
+ com_Bytecode(com, XBC_RETURN);
+
+ /* The only usage of this information for now, and still may generate
+ * false positives because arguments to unamed functions are not being
+ * parsed as well as arguments to yet undefined function/maros.
+ * XXX should also add declaim/declare to let the code specify that
+ * the argument is unused */
+ LinkWarnUnused(com, com->block);
+
+ /* First level optimization */
+ LinkOptimize_0(com);
+
+ /* Resolve tagbody labels */
+ LinkResolveLabels(com, com->block);
+
+ /* Resolve any pending jumps */
+ LinkResolveJumps(com, com->block);
+
+ /* Calculate unpadded offsets */
+ LinkBuildOffsets(com, com->block->tree, 0);
+
+ /* Do padding for aligned memory reads */
+ LinkFixupOffsets(com, com->block->tree, 0);
+
+ /* Jumps normally are to a node that does not generate code,
+ * and due to padding, the jump may go to a address with a
+ * XBC_NOOP, so adjust the jump to the next useful opcode. */
+ LinkSkipPadding(com, com->block->tree);
+
+ /* Now addresses are known */
+ LinkFixupJumps(com, com->block->tree);
+
+ /* Build symbol, constant and builtin tables */
+ LinkBuildTables(com, com->block);
+
+ /* Stack info */
+ com->length = sizeof(short) * 3;
+ /* Tables info */
+ com->length += sizeof(short) * 4;
+ com->length += com->table.num_constants * sizeof(LispObj*);
+ com->length += com->table.num_symbols * sizeof(LispAtom*);
+ com->length += com->table.num_builtins * sizeof(LispBuiltin*);
+ com->length += com->table.num_bytecodes * sizeof(unsigned char*);
+ com->length += com->table.num_bytecodes * sizeof(LispObj*);
+
+ /* Allocate space for the bytecode stream */
+ com->length += com->block->tail->offset + 1;
+ com->bytecode = LispMalloc(com->length);
+
+ /* Add header */
+ offset = 0;
+ *(short*)(com->bytecode + offset) = com->stack.stack;
+ offset += sizeof(short);
+ *(short*)(com->bytecode + offset) = com->stack.bstack;
+ offset += sizeof(short);
+ *(short*)(com->bytecode + offset) = com->stack.pstack;
+ offset += sizeof(short);
+
+ *(short*)(com->bytecode + offset) = com->table.num_constants;
+ offset += sizeof(short);
+ *(short*)(com->bytecode + offset) = com->table.num_symbols;
+ offset += sizeof(short);
+ *(short*)(com->bytecode + offset) = com->table.num_builtins;
+ offset += sizeof(short);
+ *(short*)(com->bytecode + offset) = com->table.num_bytecodes;
+ offset += sizeof(short);
+
+ count = sizeof(LispObj*) * com->table.num_constants;
+ memcpy(com->bytecode + offset, com->table.constants, count);
+ offset += count;
+ count = sizeof(LispAtom*) * com->table.num_symbols;
+ memcpy(com->bytecode + offset, com->table.symbols, count);
+ offset += count;
+ count = sizeof(LispBuiltin*) * com->table.num_builtins;
+ memcpy(com->bytecode + offset, com->table.builtins, count);
+ offset += count;
+
+ /* Store bytecode information */
+ for (count = 0, codes = (unsigned char**)(com->bytecode + offset);
+ count < com->table.num_bytecodes; count++, codes++)
+ *codes = com->table.bytecodes[count]->data.bytecode.bytecode->code;
+ offset += com->table.num_bytecodes * sizeof(unsigned char*);
+ /* Store names, only useful for disassemble but may also be used
+ * to check if a function was redefined, and the bytecode is referencing
+ * the older version, the current version can be checked looking at
+ * <name>->data.atom */
+ for (count = 0, names = (LispObj**)(com->bytecode + offset);
+ count < com->table.num_bytecodes; count++, names++)
+ *names = com->table.bytecodes[count]->data.bytecode.name;
+ offset += com->table.num_bytecodes * sizeof(LispObj*);
+
+ /* Generate it */
+ LinkEmmitBytecode(com, com->block->tree, com->bytecode + offset, 0);
+}
+
+static LispObj *
+ExecuteBytecode(register unsigned char *stream)
+{
+ register LispObj *reg0;
+ register LispAtom *atom;
+ register short offset;
+ LispObj *reg1;
+ LispBuiltin *builtin;
+ LispObj *lambda;
+ LispObj *arguments;
+ unsigned char *bytecode;
+
+ LispObj **constants;
+ LispAtom **symbols;
+ LispBuiltin **builtins;
+ unsigned char **codes;
+ short num_constants, num_symbols, num_builtins, num_codes;
+
+ int lex, len;
+
+ /* To control gc protected slots */
+ int phead, pbase;
+
+ long fixnum = 0;
+
+#if defined(__GNUC__) && !defined(ANSI_SOURCE)
+#define ALLOW_GOTO_ADDRESS
+#endif
+
+#ifdef ALLOW_GOTO_ADDRESS
+#define JUMP_ADDRESS(label) &&label
+ static const void *opcode_labels[] = {
+ JUMP_ADDRESS(XBC_NOOP),
+ JUMP_ADDRESS(XBC_INV),
+ JUMP_ADDRESS(XBC_NIL),
+ JUMP_ADDRESS(XBC_T),
+ JUMP_ADDRESS(XBC_PRED),
+ JUMP_ADDRESS(XBC_CAR),
+ JUMP_ADDRESS(XBC_CDR),
+ JUMP_ADDRESS(XBC_CAR_SET),
+ JUMP_ADDRESS(XBC_CDR_SET),
+ JUMP_ADDRESS(XBC_RPLACA),
+ JUMP_ADDRESS(XBC_RPLACD),
+ JUMP_ADDRESS(XBC_EQ),
+ JUMP_ADDRESS(XBC_EQL),
+ JUMP_ADDRESS(XBC_EQUAL),
+ JUMP_ADDRESS(XBC_EQUALP),
+ JUMP_ADDRESS(XBC_LENGTH),
+ JUMP_ADDRESS(XBC_LAST),
+ JUMP_ADDRESS(XBC_NTHCDR),
+ JUMP_ADDRESS(XBC_CAR_PUSH),
+ JUMP_ADDRESS(XBC_CDR_PUSH),
+ JUMP_ADDRESS(XBC_PUSH),
+ JUMP_ADDRESS(XBC_PUSH_NIL),
+ JUMP_ADDRESS(XBC_PUSH_UNSPEC),
+ JUMP_ADDRESS(XBC_PUSH_T),
+ JUMP_ADDRESS(XBC_PUSH_NIL_N),
+ JUMP_ADDRESS(XBC_PUSH_UNSPEC_N),
+ JUMP_ADDRESS(XBC_LET),
+ JUMP_ADDRESS(XBC_LETX),
+ JUMP_ADDRESS(XBC_LET_NIL),
+ JUMP_ADDRESS(XBC_LETX_NIL),
+ JUMP_ADDRESS(XBC_LETBIND),
+ JUMP_ADDRESS(XBC_UNLET),
+ JUMP_ADDRESS(XBC_LOAD),
+ JUMP_ADDRESS(XBC_LOAD_LET),
+ JUMP_ADDRESS(XBC_LOAD_LETX),
+ JUMP_ADDRESS(XBC_LOAD_PUSH),
+ JUMP_ADDRESS(XBC_LOADCON),
+ JUMP_ADDRESS(XBC_LOADCON_LET),
+ JUMP_ADDRESS(XBC_LOADCON_LETX),
+ JUMP_ADDRESS(XBC_LOADCON_PUSH),
+ JUMP_ADDRESS(XBC_LOAD_CAR),
+ JUMP_ADDRESS(XBC_LOAD_CDR),
+ JUMP_ADDRESS(XBC_LOAD_CAR_STORE),
+ JUMP_ADDRESS(XBC_LOAD_CDR_STORE),
+ JUMP_ADDRESS(XBC_LOADCON_SET),
+ JUMP_ADDRESS(XBC_LOADSYM),
+ JUMP_ADDRESS(XBC_LOADSYM_LET),
+ JUMP_ADDRESS(XBC_LOADSYM_LETX),
+ JUMP_ADDRESS(XBC_LOADSYM_PUSH),
+ JUMP_ADDRESS(XBC_LOAD_SET),
+ JUMP_ADDRESS(XBC_LOAD_CAR_SET),
+ JUMP_ADDRESS(XBC_LOAD_CDR_SET),
+ JUMP_ADDRESS(XBC_SET),
+ JUMP_ADDRESS(XBC_SETSYM),
+ JUMP_ADDRESS(XBC_SET_NIL),
+ JUMP_ADDRESS(XBC_CALL),
+ JUMP_ADDRESS(XBC_CALL_SET),
+ JUMP_ADDRESS(XBC_BYTECALL),
+ JUMP_ADDRESS(XBC_FUNCALL),
+ JUMP_ADDRESS(XBC_LETREC),
+ JUMP_ADDRESS(XBC_BCONS),
+ JUMP_ADDRESS(XBC_BCONS1),
+ JUMP_ADDRESS(XBC_BCONS2),
+ JUMP_ADDRESS(XBC_BCONS3),
+ JUMP_ADDRESS(XBC_BCONS4),
+ JUMP_ADDRESS(XBC_BCONS5),
+ JUMP_ADDRESS(XBC_BCONS6),
+ JUMP_ADDRESS(XBC_BCONS7),
+ JUMP_ADDRESS(XBC_CCONS),
+ JUMP_ADDRESS(XBC_CSTAR),
+ JUMP_ADDRESS(XBC_CFINI),
+ JUMP_ADDRESS(XBC_LSTAR),
+ JUMP_ADDRESS(XBC_LCONS),
+ JUMP_ADDRESS(XBC_LFINI),
+ JUMP_ADDRESS(XBC_JUMP),
+ JUMP_ADDRESS(XBC_JUMPT),
+ JUMP_ADDRESS(XBC_JUMPNIL),
+ JUMP_ADDRESS(XBC_STRUCT),
+ JUMP_ADDRESS(XBC_STRUCTP),
+ JUMP_ADDRESS(XBC_RETURN)
+ };
+ static const void *predicate_opcode_labels[] = {
+ JUMP_ADDRESS(XBP_CONSP),
+ JUMP_ADDRESS(XBP_LISTP),
+ JUMP_ADDRESS(XBP_NUMBERP)
+ };
+#endif
+
+ reg0 = NIL;
+
+ bytecode = stream;
+ pbase = lisp__data.protect.length;
+
+ /* stack */
+ offset = *(short*)stream;
+ stream += sizeof(short);
+ if (lisp__data.env.length + offset > lisp__data.env.space) {
+ do
+ LispMoreEnvironment();
+ while (lisp__data.env.length + offset >= lisp__data.env.space);
+ }
+ /* builtin stack */
+ offset = *(short*)stream;
+ stream += sizeof(short);
+ if (lisp__data.stack.length + offset >= lisp__data.stack.space) {
+ do
+ LispMoreStack();
+ while (lisp__data.stack.length + offset >= lisp__data.stack.space);
+ }
+ /* protect stack */
+ phead = *(short*)stream;
+ stream += sizeof(short);
+ if (lisp__data.protect.length + phead > lisp__data.protect.space) {
+ do
+ LispMoreProtects();
+ while (lisp__data.protect.length + phead >= lisp__data.protect.space);
+ }
+
+ num_constants = *(short*)stream;
+ stream += sizeof(short);
+ num_symbols = *(short*)stream;
+ stream += sizeof(short);
+ num_builtins = *(short*)stream;
+ stream += sizeof(short);
+ num_codes = *(short*)stream;
+ stream += sizeof(short);
+
+ constants = (LispObj**)stream;
+ stream += num_constants * sizeof(LispObj*);
+ symbols = (LispAtom**)stream;
+ stream += num_symbols * sizeof(LispAtom*);
+ builtins = (LispBuiltin**)stream;
+ stream += num_builtins * sizeof(LispBuiltin*);
+ codes = (unsigned char**)stream;
+ stream += num_codes * (sizeof(unsigned char*) + sizeof(LispObj*));
+
+ for (; phead > 0; phead--)
+ lisp__data.protect.objects[lisp__data.protect.length++] = NIL;
+ phead = pbase;
+
+#ifdef ALLOW_GOTO_ADDRESS
+#define OPCODE_LABEL(label) label
+#define NEXT_OPCODE() goto *opcode_labels[*stream++]
+#define GOTO_PREDICATE() goto *predicate_opcode_labels[*stream++]
+#else
+#define OPCODE_LABEL(label) case label
+#define NEXT_OPCODE() goto next_opcode
+#define GOTO_PREDICATE() goto predicate_label
+ for (;;) {
+next_opcode:
+ switch (*stream++) {
+#endif /* ALLOW_GOTO_ADDRESS */
+
+OPCODE_LABEL(XBC_NOOP):
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_PRED):
+ GOTO_PREDICATE();
+
+OPCODE_LABEL(XBC_INV):
+ reg0 = reg0 == NIL ? T : NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_NIL):
+ reg0 = NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_T):
+ reg0 = T;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_CAR):
+car:
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("CAR: %s is not a list", STROBJ(reg0));
+ reg0 = CAR(reg0);
+ }
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_CDR):
+cdr:
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("CDR: %s is not a list", STROBJ(reg0));
+ reg0 = CDR(reg0);
+ }
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_RPLACA):
+ reg1 = lisp__data.stack.values[--lisp__data.stack.length];
+ if (!CONSP(reg1))
+ LispDestroy("RPLACA: %s is not a cons", STROBJ(reg1));
+ RPLACA(reg1, reg0);
+ reg0 = reg1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_RPLACD):
+ reg1 = lisp__data.stack.values[--lisp__data.stack.length];
+ if (!CONSP(reg1))
+ LispDestroy("RPLACD: %s is not a cons", STROBJ(reg1));
+ RPLACD(reg1, reg0);
+ reg0 = reg1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_BCONS):
+ CAR(cons) = reg0;
+ lisp__data.stack.values[lisp__data.stack.length++] = cons;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_BCONS1):
+ offset = lisp__data.stack.length - 1;
+ CAR(cons) = reg0;
+ CAR(cons1) = lisp__data.stack.values[offset];
+ lisp__data.stack.values[offset] = cons1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_BCONS2):
+ offset = lisp__data.stack.length;
+ CAR(cons) = reg0;
+ CAR(cons1) = lisp__data.stack.values[--offset];
+ CAR(cons2) = lisp__data.stack.values[--offset];
+ lisp__data.stack.values[offset] = cons2;
+ lisp__data.stack.length = offset + 1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_BCONS3):
+ offset = lisp__data.stack.length;
+ CAR(cons) = reg0;
+ CAR(cons1) = lisp__data.stack.values[--offset];
+ CAR(cons2) = lisp__data.stack.values[--offset];
+ CAR(cons3) = lisp__data.stack.values[--offset];
+ lisp__data.stack.values[offset] = cons3;
+ lisp__data.stack.length = offset + 1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_BCONS4):
+ offset = lisp__data.stack.length;
+ CAR(cons) = reg0;
+ CAR(cons1) = lisp__data.stack.values[--offset];
+ CAR(cons2) = lisp__data.stack.values[--offset];
+ CAR(cons3) = lisp__data.stack.values[--offset];
+ CAR(cons4) = lisp__data.stack.values[--offset];
+ lisp__data.stack.values[offset] = cons4;
+ lisp__data.stack.length = offset + 1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_BCONS5):
+ offset = lisp__data.stack.length;
+ CAR(cons) = reg0;
+ CAR(cons1) = lisp__data.stack.values[--offset];
+ CAR(cons2) = lisp__data.stack.values[--offset];
+ CAR(cons3) = lisp__data.stack.values[--offset];
+ CAR(cons4) = lisp__data.stack.values[--offset];
+ CAR(cons5) = lisp__data.stack.values[--offset];
+ lisp__data.stack.values[offset] = cons5;
+ lisp__data.stack.length = offset + 1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_BCONS6):
+ offset = lisp__data.stack.length;
+ CAR(cons) = reg0;
+ CAR(cons1) = lisp__data.stack.values[--offset];
+ CAR(cons2) = lisp__data.stack.values[--offset];
+ CAR(cons3) = lisp__data.stack.values[--offset];
+ CAR(cons4) = lisp__data.stack.values[--offset];
+ CAR(cons5) = lisp__data.stack.values[--offset];
+ CAR(cons6) = lisp__data.stack.values[--offset];
+ lisp__data.stack.values[offset] = cons6;
+ lisp__data.stack.length = offset + 1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_BCONS7):
+ offset = lisp__data.stack.length;
+ CAR(cons) = reg0;
+ CAR(cons1) = lisp__data.stack.values[--offset];
+ CAR(cons2) = lisp__data.stack.values[--offset];
+ CAR(cons3) = lisp__data.stack.values[--offset];
+ CAR(cons4) = lisp__data.stack.values[--offset];
+ CAR(cons5) = lisp__data.stack.values[--offset];
+ CAR(cons6) = lisp__data.stack.values[--offset];
+ CAR(cons7) = lisp__data.stack.values[--offset];
+ lisp__data.stack.values[offset] = cons7;
+ lisp__data.stack.length = offset + 1;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_EQ):
+ reg0 = reg0 == lisp__data.stack.values[--lisp__data.stack.length] ? T : NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_EQL):
+ reg1 = lisp__data.stack.values[--lisp__data.stack.length];
+ reg0 = XEQL(reg1, reg0);
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_EQUAL):
+ reg1 = lisp__data.stack.values[--lisp__data.stack.length];
+ reg0 = XEQUAL(reg1, reg0);
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_EQUALP):
+ reg1 = lisp__data.stack.values[--lisp__data.stack.length];
+ reg0 = XEQUALP(reg1, reg0);
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LENGTH):
+ reg0 = FIXNUM(LispLength(reg0));
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LAST):
+ {
+ long length;
+
+ reg1 = lisp__data.stack.values[--lisp__data.stack.length];
+ if (CONSP(reg1)) {
+ if (reg0 != NIL) {
+ if (!FIXNUMP(reg0) || (fixnum = FIXNUM_VALUE(reg0)) < 0)
+ LispDestroy("LAST: %s is not a positive fixnum",
+ STROBJ(reg0));
+ }
+ else
+ fixnum = 1;
+ reg0 = reg1;
+ for (reg0 = reg1, length = 0;
+ CONSP(reg0);
+ reg0 = CDR(reg0), length++)
+ ;
+ for (length -= fixnum, reg0 = reg1; length > 0; length--)
+ reg0 = CDR(reg0);
+ }
+ else
+ reg0 = reg1;
+ } NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_NTHCDR):
+ reg1 = lisp__data.stack.values[--lisp__data.stack.length];
+ if (!FIXNUMP(reg1) || (fixnum = FIXNUM_VALUE(reg1)) < 0)
+ LispDestroy("NTHCDR: %s is not a positive fixnum",
+ STROBJ(reg1));
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("NTHCDR: %s is not a list", STROBJ(reg0));
+ for (; fixnum > 0; fixnum--) {
+ if (!CONSP(reg0))
+ break;
+ reg0 = CDR(reg0);
+ }
+ }
+ NEXT_OPCODE();
+
+ /* Push to builtin stack */
+OPCODE_LABEL(XBC_CAR_PUSH):
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("CAR: %s is not a list", STROBJ(reg0));
+ reg0 = CAR(reg0);
+ }
+ goto push_builtin;
+
+OPCODE_LABEL(XBC_CDR_PUSH):
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("CDR: %s is not a list", STROBJ(reg0));
+ reg0 = CDR(reg0);
+ }
+ /*FALLTROUGH*/
+
+OPCODE_LABEL(XBC_PUSH):
+push_builtin:
+ lisp__data.stack.values[lisp__data.stack.length++] = reg0;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_PUSH_NIL):
+ lisp__data.stack.values[lisp__data.stack.length++] = NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_PUSH_UNSPEC):
+ lisp__data.stack.values[lisp__data.stack.length++] = UNSPEC;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_PUSH_T):
+ lisp__data.stack.values[lisp__data.stack.length++] = T;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_PUSH_NIL_N):
+ for (offset = *stream++; offset > 0; offset--)
+ lisp__data.stack.values[lisp__data.stack.length++] = NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_PUSH_UNSPEC_N):
+ for (offset = *stream++; offset > 0; offset--)
+ lisp__data.stack.values[lisp__data.stack.length++] = UNSPEC;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LET):
+let_argument:
+ /* The global object value is not changed, so it does not
+ * matter if it is a constant symbol. An error would be
+ * generated if it was declared as constant at the time of
+ * bytecode generation. Check can be done looking at the
+ * atom->constant field. */
+ atom = symbols[*stream++];
+ atom->offset = lisp__data.env.length;
+ lisp__data.env.names[lisp__data.env.length] = atom->string;
+ lisp__data.env.values[lisp__data.env.length++] = reg0;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LETX):
+letx_argument:
+ atom = symbols[*stream++];
+ atom->offset = lisp__data.env.length;
+ lisp__data.env.names[lisp__data.env.length] = atom->string;
+ lisp__data.env.values[lisp__data.env.length++] = reg0;
+ lisp__data.env.head++;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LET_NIL):
+ atom = symbols[*stream++];
+ atom->offset = lisp__data.env.length;
+ lisp__data.env.names[lisp__data.env.length] = atom->string;
+ lisp__data.env.values[lisp__data.env.length++] = NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LETX_NIL):
+ atom = symbols[*stream++];
+ atom->offset = lisp__data.env.length;
+ lisp__data.env.names[lisp__data.env.length] = atom->string;
+ lisp__data.env.values[lisp__data.env.length++] = NIL;
+ lisp__data.env.head++;
+ NEXT_OPCODE();
+
+ /* Bind locally added variables to a block */
+OPCODE_LABEL(XBC_LETBIND):
+ offset = *stream++;
+ lisp__data.env.head += offset;
+ NEXT_OPCODE();
+
+ /* Unbind locally added variables to a block */
+OPCODE_LABEL(XBC_UNLET):
+ offset = *stream++;
+ lisp__data.env.head -= offset;
+ lisp__data.env.length -= offset;
+ NEXT_OPCODE();
+
+ /* Load value from stack */
+OPCODE_LABEL(XBC_LOAD):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LOAD_CAR):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ goto car;
+
+OPCODE_LABEL(XBC_LOAD_CDR):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ goto cdr;
+
+OPCODE_LABEL(XBC_LOAD_CAR_STORE):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("CAR: %s is not a list", STROBJ(reg0));
+ reg0 = CAR(reg0);
+ lisp__data.env.values[lisp__data.env.lex + offset] = reg0;
+ }
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LOAD_CDR_STORE):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("CDR: %s is not a list", STROBJ(reg0));
+ reg0 = CDR(reg0);
+ lisp__data.env.values[lisp__data.env.lex + offset] = reg0;
+ }
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LOAD_LET):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ goto let_argument;
+
+OPCODE_LABEL(XBC_LOAD_LETX):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ goto letx_argument;
+
+OPCODE_LABEL(XBC_LOAD_PUSH):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ lisp__data.stack.values[lisp__data.stack.length++] = reg0;
+ NEXT_OPCODE();
+
+ /* Load pointer to constant */
+OPCODE_LABEL(XBC_LOADCON):
+ reg0 = constants[*stream++];
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LOADCON_LET):
+ reg0 = constants[*stream++];
+ goto let_argument;
+
+OPCODE_LABEL(XBC_LOADCON_LETX):
+ reg0 = constants[*stream++];
+ goto letx_argument;
+
+OPCODE_LABEL(XBC_LOADCON_PUSH):
+ reg0 = constants[*stream++];
+ lisp__data.stack.values[lisp__data.stack.length++] = reg0;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LOADCON_SET):
+ reg0 = constants[*stream++];
+ offset = *stream++;
+ lisp__data.env.values[lisp__data.env.lex + offset] = reg0;
+ NEXT_OPCODE();
+
+ /* Change value of local variable */
+OPCODE_LABEL(XBC_CAR_SET):
+car_set:
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("CAR: %s is not a list", STROBJ(reg0));
+ reg0 = CAR(reg0);
+ }
+ goto set_local_variable;
+
+OPCODE_LABEL(XBC_CDR_SET):
+cdr_set:
+ if (reg0 != NIL) {
+ if (!CONSP(reg0))
+ LispDestroy("CDR: %s is not a list", STROBJ(reg0));
+ reg0 = CDR(reg0);
+ }
+ goto set_local_variable;
+
+OPCODE_LABEL(XBC_LOAD_CAR_SET):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ goto car_set;
+
+OPCODE_LABEL(XBC_LOAD_CDR_SET):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ goto cdr_set;
+
+OPCODE_LABEL(XBC_LOAD_SET):
+ offset = *stream++;
+ reg0 = lisp__data.env.values[lisp__data.env.lex + offset];
+ /*FALLTROUGH*/
+
+OPCODE_LABEL(XBC_SET):
+set_local_variable:
+ offset = *stream++;
+ lisp__data.env.values[lisp__data.env.lex + offset] = reg0;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_SET_NIL):
+ offset = *stream++;
+ lisp__data.env.values[lisp__data.env.lex + offset] = NIL;
+ NEXT_OPCODE();
+
+ /* Change value of a global/special variable */
+OPCODE_LABEL(XBC_SETSYM):
+ atom = symbols[*stream++];
+ if (atom->dyn) {
+ /* atom->dyn and atom->constant are exclusive, no
+ * need to check if variable declared as constant. */
+ if (atom->offset < lisp__data.env.head &&
+ lisp__data.env.names[atom->offset] == atom->string)
+ lisp__data.env.values[atom->offset] = reg0;
+ else {
+ if (atom->watch)
+ LispSetAtomObjectProperty(atom, reg0);
+ else
+ SETVALUE(atom, reg0);
+ }
+ }
+ else if (atom->a_object) {
+ if (atom->constant)
+ LispDestroy("EVAL: %s is a constant",
+ STROBJ(atom->object));
+ else if (atom->watch)
+ LispSetAtomObjectProperty(atom, reg0);
+ else
+ SETVALUE(atom, reg0);
+ }
+ else {
+ /* Create new global variable */
+ LispPackage *pack;
+
+ LispWarning("the variable %s was not declared",
+ atom->string);
+ LispSetAtomObjectProperty(atom, reg0);
+ pack = atom->package->data.package.package;
+ if (pack->glb.length >= pack->glb.space)
+ LispMoreGlobals(pack);
+ pack->glb.pairs[pack->glb.length++] = atom->object;
+ }
+ NEXT_OPCODE();
+
+/* Resolve symbol value at runtime */
+#define LOAD_SYMBOL_VALUE() \
+ atom = symbols[*stream++]; \
+ if (atom->dyn) { \
+ if (atom->offset < lisp__data.env.head && \
+ lisp__data.env.names[atom->offset] == atom->string) \
+ reg0 = lisp__data.env.values[atom->offset]; \
+ else { \
+ reg0 = atom->property->value; \
+ if (reg0 == UNBOUND) \
+ LispDestroy("EVAL: the symbol %s is unbound", \
+ STROBJ(atom->object)); \
+ } \
+ } \
+ else { \
+ if (atom->a_object) \
+ reg0 = atom->property->value; \
+ else \
+ LispDestroy("EVAL: the symbol %s is unbound", \
+ STROBJ(atom->object)); \
+ }
+
+OPCODE_LABEL(XBC_LOADSYM):
+ LOAD_SYMBOL_VALUE();
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LOADSYM_LET):
+ LOAD_SYMBOL_VALUE();
+ goto let_argument;
+
+OPCODE_LABEL(XBC_LOADSYM_LETX):
+ LOAD_SYMBOL_VALUE();
+ goto letx_argument;
+
+OPCODE_LABEL(XBC_LOADSYM_PUSH):
+ LOAD_SYMBOL_VALUE();
+ lisp__data.stack.values[lisp__data.stack.length++] = reg0;
+ NEXT_OPCODE();
+
+ /* Builtin function */
+OPCODE_LABEL(XBC_CALL):
+ offset = *stream++;
+ lisp__data.stack.base = lisp__data.stack.length - offset;
+ builtin = builtins[*stream++];
+ if (builtin->multiple_values) {
+ RETURN_COUNT = 0;
+ reg0 = builtin->function(builtin);
+ }
+ else {
+ reg0 = builtin->function(builtin);
+ RETURN_COUNT = 0;
+ }
+ lisp__data.stack.length -= offset;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_CALL_SET):
+ offset = *stream++;
+ lisp__data.stack.base = lisp__data.stack.length - offset;
+ builtin = builtins[*stream++];
+ if (builtin->multiple_values) {
+ RETURN_COUNT = 0;
+ reg0 = builtin->function(builtin);
+ }
+ else {
+ reg0 = builtin->function(builtin);
+ RETURN_COUNT = 0;
+ }
+ lisp__data.stack.length -= offset;
+ offset = *stream++;
+ lisp__data.env.values[lisp__data.env.lex + offset] = reg0;
+ NEXT_OPCODE();
+
+ /* Bytecode call */
+OPCODE_LABEL(XBC_BYTECALL):
+ lex = lisp__data.env.lex;
+ offset = *stream++;
+ lisp__data.env.head = lisp__data.env.length;
+ len = lisp__data.env.lex = lisp__data.env.length - offset;
+ reg0 = ExecuteBytecode(codes[*stream++]);
+ lisp__data.env.length = lisp__data.env.head = len;
+ lisp__data.env.lex = lex;
+ NEXT_OPCODE();
+
+ /* Unimplemented function/macro call */
+OPCODE_LABEL(XBC_FUNCALL):
+ lambda = constants[*stream++];
+ arguments = constants[*stream++];
+ reg0 = LispFuncall(lambda, arguments, 1);
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_JUMP):
+ stream += *(signed short*)stream;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_JUMPT):
+ if (reg0 != NIL)
+ stream += *(signed short*)stream;
+ else
+ /* skip jump relative offset */
+ stream += sizeof(signed short);
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_JUMPNIL):
+ if (reg0 == NIL)
+ stream += *(signed short*)stream;
+ else
+ /* skip jump relative offset */
+ stream += sizeof(signed short);
+ NEXT_OPCODE();
+
+ /* Build CONS of two constant arguments */
+OPCODE_LABEL(XBC_CCONS):
+ reg0 = constants[*stream++];
+ reg1 = constants[*stream++];
+ reg0 = CONS(reg0, reg1);
+ NEXT_OPCODE();
+
+ /* Start CONS */
+OPCODE_LABEL(XBC_CSTAR):
+ /* This the CAR of the CONS */
+ lisp__data.protect.objects[phead++] = reg0;
+ NEXT_OPCODE();
+
+ /* Finish CONS */
+OPCODE_LABEL(XBC_CFINI):
+ reg0 = CONS(lisp__data.protect.objects[--phead], reg0);
+ NEXT_OPCODE();
+
+ /* Start building list */
+OPCODE_LABEL(XBC_LSTAR):
+ reg1 = CONS(reg0, NIL);
+ /* Start of list stored here */
+ lisp__data.protect.objects[phead++] = reg1;
+ /* Tail of list stored here */
+ lisp__data.protect.objects[phead++] = reg1;
+ NEXT_OPCODE();
+
+ /* Add to list */
+OPCODE_LABEL(XBC_LCONS):
+ reg1 = lisp__data.protect.objects[phead - 2];
+ RPLACD(reg1, CONS(reg0, NIL));
+ lisp__data.protect.objects[phead - 2] = CDR(reg1);
+ NEXT_OPCODE();
+
+ /* Finish list */
+OPCODE_LABEL(XBC_LFINI):
+ phead -= 2;
+ reg0 = lisp__data.protect.objects[phead + 1];
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_STRUCT):
+ offset = *stream++;
+ reg1 = constants[*stream++];
+ if (!STRUCTP(reg0) || reg0->data.struc.def != reg1) {
+ char *name = ATOMID(CAR(reg1));
+
+ for (reg1 = CDR(reg1); offset; offset--)
+ reg1 = CDR(reg1);
+ LispDestroy("%s-%s: %s is not a %s",
+ name, ATOMID(CAR(reg1)), STROBJ(reg0), name);
+ }
+ for (reg0 = reg0->data.struc.fields; offset; offset--)
+ reg0 = CDR(reg0);
+ reg0 = CAR(reg0);
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_STRUCTP):
+ reg1 = constants[*stream++];
+ reg0 = STRUCTP(reg0) && reg0->data.struc.def == reg1 ? T : NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_LETREC):
+ /* XXX could/should optimize, shouldn't need to parse
+ * the bytecode header again */
+ lex = lisp__data.env.lex;
+ offset = *stream++;
+ lisp__data.env.head = lisp__data.env.length;
+ len = lisp__data.env.lex = lisp__data.env.length - offset;
+ reg0 = ExecuteBytecode(bytecode);
+ lisp__data.env.length = lisp__data.env.head = len;
+ lisp__data.env.lex = lex;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBC_RETURN):
+ lisp__data.protect.length = pbase;
+ return (reg0);
+
+#ifndef ALLOW_GOTO_ADDRESS
+ } /* end of switch */
+
+predicate_label:
+ switch (*stream++) {
+#endif
+
+OPCODE_LABEL(XBP_CONSP):
+ reg0 = CONSP(reg0) ? T : NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBP_LISTP):
+ reg0 = LISTP(reg0) ? T : NIL;
+ NEXT_OPCODE();
+
+OPCODE_LABEL(XBP_NUMBERP):
+ reg0 = NUMBERP(reg0) ? T : NIL;
+ NEXT_OPCODE();
+
+#ifndef ALLOW_GOTO_ADDRESS
+ } /* end of switch */
+ }
+#endif
+
+ /*NOTREACHED*/
+ return (reg0);
+}
diff --git a/lisp/bytecode.h b/lisp/bytecode.h
new file mode 100644
index 0000000..40d8444
--- /dev/null
+++ b/lisp/bytecode.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/bytecode.h,v 1.5 2002/11/23 08:26:48 paulo Exp $ */
+
+#include "private.h"
+
+#ifndef Lisp_Bytecode_h
+#define Lisp_Bytecode_h
+
+/* Number of arguments for &REST functions with no side effects,
+ * i.e. does not need to allocate new cells to build argument list.
+ * If this value is changed, it is also required to add more
+ * XBC_BCONS? opcodes and change ExecuteBytecode() */
+#define MAX_BCONS 8
+
+typedef enum {
+ XBP_CONSP,
+ XBP_LISTP,
+ XBP_NUMBERP
+} LispBytePredicate;
+
+typedef enum {
+ XBC_NOOP,
+
+ XBC_INV, /* If NIL loaded, change to T else to NIL */
+ XBC_NIL, /* Load NIL */
+ XBC_T, /* Load T */
+
+ XBC_PRED, /* Preffix for predicate test */
+ /* Followed by byte indicating test */
+
+ XBC_CAR, /* Set loaded value to it's car */
+ XBC_CDR, /* Set loaded value to it's cdr */
+
+ XBC_CAR_SET, /* Set local variable to CAR or CDR of loaded value */
+ XBC_CDR_SET,
+ /* Offset of local variable follows */
+
+ /* Sets C[AD]R of top of builtin stack with
+ * loaded value and pop builtin stack */
+ XBC_RPLACA,
+ XBC_RPLACD,
+
+ /* Push only one argument in builtin stack, and call directly
+ * LispObjectCompare without the need of pushing the second arguument */
+ XBC_EQ,
+ XBC_EQL,
+ XBC_EQUAL,
+ XBC_EQUALP,
+
+ XBC_LENGTH,
+ XBC_LAST,
+ XBC_NTHCDR,
+
+ XBC_CAR_PUSH, /* Pushes CAR or CDR of loaded value to builtin stack */
+ XBC_CDR_PUSH,
+
+ XBC_PUSH, /* Push argument in builtin stack */
+ XBC_PUSH_NIL, /* Pushes NIL in builtin stack */
+ XBC_PUSH_UNSPEC, /* Pushes #<UNSPEC> in builtin stack */
+ XBC_PUSH_T, /* Pushes T in builtin stack */
+ XBC_PUSH_NIL_N, /* Pushes NIL in the builtin stack N times */
+ XBC_PUSH_UNSPEC_N, /* Pushes #<UNSPEC> in the builtin stack N times */
+
+
+ /* The builtin stack base is saved when entering the bytecode
+ * interpreter, and the bytecode does not reenter from builtin
+ * functions, yet, so there is no need, for XBC_BSAVE and
+ * XBC_BREST instructions */
+
+ XBC_LET, /* Push loaded value to stack */
+ XBC_LETX, /* Push loaded value to stack and bind */
+ /* Next byte(s) are the symbol offset */
+ XBC_LET_NIL, /* Push loaded value to stack */
+ XBC_LETX_NIL, /* Push loaded value to stack and bind */
+ /* Next byte(s) are the symbol offset */
+
+ XBC_LETBIND, /* Bind locally added variables */
+ /* Followed by number of symbols to bind */
+
+ XBC_UNLET, /* Unbind locally binded variables */
+ /* Followed by number of symbols to unbind */
+
+ XBC_LOAD, /* Load argument already from the stack */
+ /* Followed by offset follows the opcode */
+ XBC_LOAD_LET, /* Load argument and push */
+ XBC_LOAD_LETX, /* Load argument, push and bind */
+ /* Followed by a short and the atom to be bound */
+ XBC_LOAD_PUSH,
+
+ XBC_LOADCON, /* Load a literal constant */
+ /* Next bytes are the constant object */
+ XBC_LOADCON_LET, /* Load a literal constant and push */
+ XBC_LOADCON_LETX, /* Load a literal constant, push and bind */
+ /* Followed by object constant and symbol to be bound */
+ XBC_LOADCON_PUSH,
+
+ /* Load CAR or CDR of local variable */
+ XBC_LOAD_CAR,
+ XBC_LOAD_CDR,
+
+ /* Change local variable value to it's CAR or CDR */
+ XBC_LOAD_CAR_STORE,
+ XBC_LOAD_CDR_STORE,
+
+ XBC_LOADCON_SET,
+ /* Followed by constant offset and local variable offset */
+
+ XBC_LOADSYM, /* Load object symbol value */
+ /* The object atom pointer follows de opcode */
+ XBC_LOADSYM_LET, /* Load object symbol value and push */
+ XBC_LOADSYM_LETX, /* Load object symbol value, push and bind */
+ /* The symbol atom name and bounded atom name to be bound follows */
+ XBC_LOADSYM_PUSH,
+
+ XBC_LOAD_SET, /* Set value of local variable to the value of another */
+ /* Followed by two shorts */
+ XBC_LOAD_CAR_SET, /* Like LOAD_SET, but apply CAR or CDR in the value */
+ XBC_LOAD_CDR_SET,
+
+ XBC_SET, /* Change value of local variable */
+ /* A short integer with relative offset follows opcode */
+ XBC_SETSYM, /* Change value of symbol */
+ /* The atom symbol pointer follows opcode */
+
+ XBC_SET_NIL, /* Like XBC_SET but sets the local variable to NIL */
+
+ XBC_CALL, /* Call builtin function */
+ /* 1 byte follows telling how many arguments to use */
+ /* LispBuiltin pointer follows opcode */
+
+ XBC_CALL_SET,
+ /* Like BCALL, but also followed by an short index of local variable */
+
+ XBC_BYTECALL, /* Call bytecode */
+ /* 1 byte for number of arguments */
+ /* 1 byte for index in bytecode table */
+
+ XBC_FUNCALL,
+ /* Opcode followed by function and arguments objects, to
+ * be evaluated at run time, as it was not resolved at
+ * bytecode generation time (or is not implemented in
+ * the bytecode compiler) */
+
+
+ XBC_LETREC, /* Recursive function call */
+ /* 1 byte follows telling how many arguments the funtion receives */
+
+ /* Helper for math functions. Uses a preallocated CONS,
+ * setting it's CAR to the loaded value, and in the same step
+ * pushes the CONS to the builtin stack */
+ XBC_BCONS,
+ /* Like BCONS but it is a list of 2 arguments, first argument
+ * is saved on the stack, replace with list of 2 arguments */
+ XBC_BCONS1,
+ /* Like BCONS1 but it is a list of 3 arguments, first arguments
+ * are saved on the stack, replace with list of first stack
+ * argument with list or 3 arguments, and pop stack */
+ XBC_BCONS2,
+ XBC_BCONS3,
+ XBC_BCONS4,
+ XBC_BCONS5,
+ XBC_BCONS6,
+ XBC_BCONS7,
+
+ /* Build a CONS */
+ XBC_CCONS, /* Make CONS of two constants */
+ /* Constants follow opcode */
+ XBC_CSTAR, /* Save the CAR of the CONS */
+ XBC_CFINI, /* Loaded value is the CDR */
+
+ /* These are to help in interactively building lists */
+ XBC_LSTAR, /* Start building a list in the gc protected stack */
+ XBC_LCONS, /* Add loaded object to list */
+ XBC_LFINI, /* List is finished */
+
+ /* Inconditional jumps */
+ XBC_JUMP, /* Jump relative to following signed int */
+
+ /* Conditional jumps, if true */
+ XBC_JUMPT, /* Jump relative to following signed int */
+
+ /* Conditional jumps, if false */
+ XBC_JUMPNIL, /* Jump relative to following signed int */
+
+ /* Structure field access and type check */
+ XBC_STRUCT,
+ XBC_STRUCTP,
+
+ XBC_RETURN /* Resume bytecode execution */
+} LispByteOpcode;
+
+
+struct _LispBytecode {
+ unsigned char *code; /* Bytecode data */
+ long length; /* length of bytecode stream */
+};
+
+/*
+ * Prototypes
+ */
+void LispBytecodeInit(void);
+
+LispObj *Lisp_Compile(LispBuiltin*);
+LispObj *Lisp_Disassemble(LispBuiltin*);
+
+LispObj *LispCompileForm(LispObj*);
+LispObj *LispExecuteBytecode(LispObj*);
+
+void Com_And(LispCom*, LispBuiltin*);
+void Com_Block(LispCom*, LispBuiltin*);
+void Com_C_r(LispCom*, LispBuiltin*);
+void Com_Cond(LispCom*, LispBuiltin*);
+void Com_Cons(LispCom*, LispBuiltin*);
+void Com_Consp(LispCom*, LispBuiltin*);
+void Com_Dolist(LispCom*, LispBuiltin*);
+void Com_Eq(LispCom*, LispBuiltin*);
+void Com_Go(LispCom*, LispBuiltin*);
+void Com_If(LispCom*, LispBuiltin*);
+void Com_Last(LispCom*, LispBuiltin*);
+void Com_Length(LispCom*, LispBuiltin*);
+void Com_Let(LispCom*, LispBuiltin*);
+void Com_Letx(LispCom*, LispBuiltin*);
+void Com_Listp(LispCom*, LispBuiltin*);
+void Com_Loop(LispCom*, LispBuiltin*);
+void Com_Nthcdr(LispCom*, LispBuiltin*);
+void Com_Null(LispCom*, LispBuiltin*);
+void Com_Numberp(LispCom*, LispBuiltin*);
+void Com_Or(LispCom*, LispBuiltin*);
+void Com_Progn(LispCom*, LispBuiltin*);
+void Com_Return(LispCom*, LispBuiltin*);
+void Com_ReturnFrom(LispCom*, LispBuiltin*);
+void Com_Rplac_(LispCom*, LispBuiltin*);
+void Com_Setq(LispCom*, LispBuiltin*);
+void Com_Tagbody(LispCom*, LispBuiltin*);
+void Com_Unless(LispCom*, LispBuiltin*);
+void Com_Until(LispCom*, LispBuiltin*);
+void Com_When(LispCom*, LispBuiltin*);
+void Com_While(LispCom*, LispBuiltin*);
+
+#endif /* Lisp_Bytecode_h */
diff --git a/lisp/compile.c b/lisp/compile.c
new file mode 100644
index 0000000..f699f39
--- /dev/null
+++ b/lisp/compile.c
@@ -0,0 +1,2225 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/compile.c,v 1.14 2003/01/30 02:46:25 paulo Exp $ */
+
+#define VARIABLE_USED 0x0001
+#define VARIABLE_ARGUMENT 0x0002
+
+/*
+ * Prototypes
+ */
+static void ComPredicate(LispCom*, LispBuiltin*, LispBytePredicate);
+static void ComReturnFrom(LispCom*, LispBuiltin*, int);
+
+static int ComConstantp(LispCom*, LispObj*);
+static void ComAddVariable(LispCom*, LispObj*, LispObj*);
+static int ComGetVariable(LispCom*, LispObj*);
+static void ComVariableSetFlag(LispCom*, LispAtom*, int);
+#define COM_VARIABLE_USED(atom) \
+ ComVariableSetFlag(com, atom, VARIABLE_USED)
+#define COM_VARIABLE_ARGUMENT(atom) \
+ ComVariableSetFlag(com, atom, VARIABLE_ARGUMENT)
+
+static int FindIndex(void*, void**, int);
+static int compare(const void*, const void*);
+static int BuildTablePointer(void*, void***, int*);
+
+static void ComLabel(LispCom*, LispObj*);
+static void ComPush(LispCom*, LispObj*, LispObj*, int, int, int);
+static int ComCall(LispCom*, LispArgList*, LispObj*, LispObj*, int, int, int);
+static void ComFuncall(LispCom*, LispObj*, LispObj*, int);
+static void ComProgn(LispCom*, LispObj*);
+static void ComEval(LispCom*, LispObj*);
+
+static void ComRecursiveCall(LispCom*, LispArgList*, LispObj*, LispObj*);
+static void ComInlineCall(LispCom*, LispArgList*, LispObj*, LispObj*, LispObj*);
+
+static void ComMacroBackquote(LispCom*, LispObj*);
+static void ComMacroCall(LispCom*, LispArgList*, LispObj*, LispObj*, LispObj*);
+static LispObj *ComMacroExpandBackquote(LispCom*, LispObj*);
+static LispObj *ComMacroExpand(LispCom*, LispObj*);
+static LispObj *ComMacroExpandFuncall(LispCom*, LispObj*, LispObj*);
+static LispObj *ComMacroExpandEval(LispCom*, LispObj*);
+
+/*
+ * Implementation
+ */
+void
+Com_And(LispCom *com, LispBuiltin *builtin)
+/*
+ and &rest args
+ */
+{
+ LispObj *args;
+
+ args = ARGUMENT(0);
+
+ if (CONSP(args)) {
+ /* Evaluate first argument */
+ ComEval(com, CAR(args));
+ args = CDR(args);
+
+ /* If more than one argument, create jump list */
+ if (CONSP(args)) {
+ CodeTree *tree = NULL, *group;
+
+ group = NEW_TREE(CodeTreeJumpIf);
+ group->code = XBC_JUMPNIL;
+
+ for (; CONSP(args); args = CDR(args)) {
+ ComEval(com, CAR(args));
+ tree = NEW_TREE(CodeTreeJumpIf);
+ tree->code = XBC_JUMPNIL;
+ group->group = tree;
+ group = tree;
+ }
+ /* Finish form the last CodeTree code is changed to sign the
+ * end of the AND list */
+ group->code = XBC_NOOP;
+ if (group)
+ group->group = tree;
+ }
+ }
+ else
+ /* Identity of AND is T */
+ com_Bytecode(com, XBC_T);
+}
+
+void
+Com_Block(LispCom *com, LispBuiltin *builtin)
+/*
+ block name &rest body
+ */
+{
+
+ LispObj *name, *body;
+
+ body = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ if (name != NIL && name != T && !SYMBOLP(name))
+ LispDestroy("%s: %s cannot name a block",
+ STRFUN(builtin), STROBJ(name));
+ if (CONSP(body)) {
+ CompileIniBlock(com, LispBlockTag, name);
+ ComProgn(com, body);
+ CompileFiniBlock(com);
+ }
+ else
+ /* Just load NIL without starting an empty block */
+ com_Bytecode(com, XBC_NIL);
+}
+
+void
+Com_C_r(LispCom *com, LispBuiltin *builtin)
+/*
+ c[ad]{1,4}r list
+ */
+{
+ LispObj *list;
+ char *desc;
+
+ list = ARGUMENT(0);
+
+ desc = STRFUN(builtin);
+ if (*desc == 'F') /* FIRST */
+ desc = "CAR";
+ else if (*desc == 'R') /* REST */
+ desc = "CDR";
+
+ /* Check if it is a list of constants */
+ while (desc[1] != 'R')
+ desc++;
+ ComEval(com, list);
+ while (*desc != 'C') {
+ com_Bytecode(com, *desc == 'A' ? XBC_CAR : XBC_CDR);
+ --desc;
+ }
+}
+
+void
+Com_Cond(LispCom *com, LispBuiltin *builtin)
+/*
+ cond &rest body
+ */
+{
+ int count;
+ LispObj *code, *body;
+ CodeTree *group, *tree;
+
+ body = ARGUMENT(0);
+
+ count = 0;
+ group = NULL;
+ if (CONSP(body)) {
+ for (; CONSP(body); body = CDR(body)) {
+ code = CAR(body);
+ CHECK_CONS(code);
+ ++count;
+ ComEval(com, CAR(code));
+ tree = NEW_TREE(CodeTreeCond);
+ if (group)
+ group->group = tree;
+ tree->code = XBC_JUMPNIL;
+ group = tree;
+ /* The code to execute if the test is true */
+ ComProgn(com, CDR(code));
+ /* Add a node signaling the end of the PROGN code */
+ tree = NEW_TREE(CodeTreeCond);
+ tree->code = XBC_JUMPT;
+ if (group)
+ group->group = tree;
+ group = tree;
+ }
+ }
+ if (!count)
+ com_Bytecode(com, XBC_NIL);
+ else
+ /* Where to jump after T progn */
+ group->code = XBC_NOOP;
+}
+
+void
+Com_Cons(LispCom *com, LispBuiltin *builtin)
+/*
+ cons car cdr
+ */
+{
+ LispObj *car, *cdr;
+
+ cdr = ARGUMENT(1);
+ car = ARGUMENT(0);
+
+ if (ComConstantp(com, car) && ComConstantp(com, cdr))
+ com_BytecodeCons(com, XBC_CCONS, car, cdr);
+ else {
+ ++com->stack.cpstack;
+ if (com->stack.pstack < com->stack.cpstack)
+ com->stack.pstack = com->stack.cpstack;
+ ComEval(com, car);
+ com_Bytecode(com, XBC_CSTAR);
+ ComEval(com, cdr);
+ com_Bytecode(com, XBC_CFINI);
+ --com->stack.cpstack;
+ }
+}
+
+void
+Com_Consp(LispCom *com, LispBuiltin *builtin)
+/*
+ consp object
+ */
+{
+ ComPredicate(com, builtin, XBP_CONSP);
+}
+
+void
+Com_Dolist(LispCom *com, LispBuiltin *builtin)
+/*
+ dolist init &rest body
+ */
+{
+ int unbound, item;
+ LispObj *symbol, *list, *result;
+ LispObj *init, *body;
+ CodeTree *group, *tree;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ CHECK_CONS(init);
+ symbol = CAR(init);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ init = CDR(init);
+ if (CONSP(init)) {
+ list = CAR(init);
+ init = CDR(init);
+ }
+ else
+ list = NIL;
+ if (CONSP(init)) {
+ result = CAR(init);
+ if (CONSP(CDR(init)))
+ LispDestroy("%s: too many arguments %s",
+ STRFUN(builtin), STROBJ(CDR(init)));
+ }
+ else
+ result = NIL;
+
+ /* Generate code for the body of the form.
+ * The generated code uses two objects unavailable to user code,
+ * in the format:
+ * (block NIL
+ * (let ((? list) (item NIL))
+ * (tagbody
+ * . ; the DOT object as a label
+ * (when (consp list)
+ * (setq item (car ?))
+ * @body ; code to be executed
+ * (setq ? (cdr ?))
+ * (go .)
+ * )
+ * )
+ * (setq item nil)
+ * result
+ * )
+ * )
+ */
+
+ /* XXX All of the logic below should be simplified at some time
+ * by adding more opcodes for compound operations ... */
+
+ /* Relative offsets the locally added variables will have at run time */
+ unbound = lisp__data.env.length - lisp__data.env.lex;
+ item = unbound + 1;
+
+ /* Start BLOCK NIL */
+ FORM_ENTER();
+ CompileIniBlock(com, LispBlockTag, NIL);
+
+ /* Add the <?> variable */
+ ComPush(com, UNBOUND, list, 1, 0, 0);
+ /* Add the <item> variable */
+ ComPush(com, symbol, NIL, 0, 0, 0);
+ /* Stack length is increased */
+ CompileStackEnter(com, 2, 0);
+ /* Bind variables */
+ com_Bind(com, 2);
+ com->block->bind += 2;
+ lisp__data.env.head += 2;
+
+ /* Remember that iteration variable is used even if it not referenced */
+ COM_VARIABLE_USED(symbol->data.atom);
+
+ /* Initialize the TAGBODY */
+ FORM_ENTER();
+ CompileIniBlock(com, LispBlockBody, NIL);
+
+ /* Create the <.> label */
+ ComLabel(com, DOT);
+
+ /* Load <?> variable */
+ com_BytecodeShort(com, XBC_LOAD, unbound);
+ /* Check if <?> is a list */
+ com_BytecodeChar(com, XBC_PRED, XBP_CONSP);
+
+ /* Start WHEN block */
+ group = NEW_TREE(CodeTreeJumpIf);
+ group->code = XBC_JUMPNIL;
+ /* Load <?> again */
+ com_BytecodeShort(com, XBC_LOAD, unbound);
+ /* Get CAR of <?> */
+ com_Bytecode(com, XBC_CAR);
+ /* Store it in <item> */
+ com_BytecodeShort(com, XBC_SET, item);
+ /* Execute @BODY */
+ ComProgn(com, body);
+
+ /* Load <?> again */
+ com_BytecodeShort(com, XBC_LOAD, unbound);
+ /* Get CDR of <?> */
+ com_Bytecode(com, XBC_CDR);
+ /* Change value of <?> */
+ com_BytecodeShort(com, XBC_SET, unbound);
+
+ /* GO back to <.> */
+ tree = NEW_TREE(CodeTreeGo);
+ tree->data.object = DOT;
+
+ /* Finish WHEN block */
+ tree = NEW_TREE(CodeTreeJumpIf);
+ tree->code = XBC_NOOP;
+ group->group = tree;
+
+ /* Finish the TAGBODY */
+ CompileFiniBlock(com);
+ FORM_LEAVE();
+
+ /* Set <item> to NIL, in case result references it...
+ * Loaded value is NIL as the CONSP predicate */
+ com_BytecodeShort(com, XBC_SET, item);
+
+ /* Evaluate <result> */
+ ComEval(com, result);
+
+ /* Unbind variables */
+ lisp__data.env.head -= 2;
+ lisp__data.env.length -= 2;
+ com->block->bind -= 2;
+ com_Unbind(com, 2);
+ /* Stack length is reduced. */
+ CompileStackLeave(com, 2, 0);
+
+ /* Finish BLOCK NIL */
+ CompileFiniBlock(com);
+ FORM_LEAVE();
+}
+
+void
+Com_Eq(LispCom *com, LispBuiltin *builtin)
+/*
+ eq left right
+ eql left right
+ equal left right
+ equalp left right
+ */
+{
+ LispObj *left, *right;
+ LispByteOpcode code;
+ char *name;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ CompileStackEnter(com, 1, 1);
+ /* Just like preparing to call a builtin function */
+ ComEval(com, left);
+ com_Bytecode(com, XBC_PUSH);
+ /* The second argument is now loaded */
+ ComEval(com, right);
+
+ /* Compare arguments and restore builtin stack */
+ name = STRFUN(builtin);
+ switch (name[3]) {
+ case 'L':
+ code = XBC_EQL;
+ break;
+ case 'U':
+ code = name[5] == 'P' ? XBC_EQUALP : XBC_EQUAL;
+ break;
+ default:
+ code = XBC_EQ;
+ break;
+ }
+ com_Bytecode(com, code);
+
+ CompileStackLeave(com, 1, 1);
+}
+
+void
+Com_Go(LispCom *com, LispBuiltin *builtin)
+/*
+ go tag
+ */
+{
+ int bind;
+ LispObj *tag;
+ CodeTree *tree;
+ CodeBlock *block;
+
+ tag = ARGUMENT(0);
+
+ block = com->block;
+ bind = block->bind;
+
+ while (block) {
+ if (block->type == LispBlockClosure || block->type == LispBlockBody)
+ break;
+ block = block->prev;
+ if (block)
+ bind += block->bind;
+ }
+
+ if (!block || block->type != LispBlockBody)
+ LispDestroy("%s called not within a block", STRFUN(builtin));
+
+ /* Unbind any local variables */
+ com_Unbind(com, bind);
+ tree = NEW_TREE(CodeTreeGo);
+ tree->data.object = tag;
+}
+
+void
+Com_If(LispCom *com, LispBuiltin *builtin)
+/*
+ if test then &optional else
+ */
+{
+ CodeTree *group, *tree;
+ LispObj *test, *then, *oelse;
+
+ oelse = ARGUMENT(2);
+ then = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ /* Build code to execute test */
+ ComEval(com, test);
+
+ /* Add jump node to use if test is NIL */
+ group = NEW_TREE(CodeTreeJumpIf);
+ group->code = XBC_JUMPNIL;
+
+ /* Build T code */
+ ComEval(com, then);
+
+ if (oelse != UNSPEC) {
+ /* Remember start of NIL code */
+ tree = NEW_TREE(CodeTreeJump);
+ tree->code = XBC_JUMP;
+ group->group = tree;
+ group = tree;
+ /* Build NIL code */
+ ComEval(com, oelse);
+ }
+
+ /* Remember jump of T code */
+ tree = NEW_TREE(CodeTreeJumpIf);
+ tree->code = XBC_NOOP;
+ group->group = tree;
+}
+
+void
+Com_Last(LispCom *com, LispBuiltin *builtin)
+/*
+ last list &optional count
+ */
+{
+ LispObj *list, *count;
+
+ count = ARGUMENT(1);
+ list = ARGUMENT(0);
+
+ ComEval(com, list);
+ CompileStackEnter(com, 1, 1);
+ com_Bytecode(com, XBC_PUSH);
+ if (count == UNSPEC)
+ count = FIXNUM(1);
+ ComEval(com, count);
+ CompileStackLeave(com, 1, 1);
+ com_Bytecode(com, XBC_LAST);
+}
+
+void
+Com_Length(LispCom *com, LispBuiltin *builtin)
+/*
+ length sequence
+ */
+{
+ LispObj *sequence;
+
+ sequence = ARGUMENT(0);
+
+ ComEval(com, sequence);
+ com_Bytecode(com, XBC_LENGTH);
+}
+
+void
+Com_Let(LispCom *com, LispBuiltin *builtin)
+/*
+ let init &rest body
+ */
+{
+ int count;
+ LispObj *symbol, *value, *pair;
+
+ LispObj *init, *body;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ if (init == NIL) {
+ /* If no local variables */
+ ComProgn(com, body);
+ return;
+ }
+ CHECK_CONS(init);
+
+ /* Could optimize if the body is empty and the
+ * init form is known to have no side effects */
+
+ for (count = 0; CONSP(init); init = CDR(init), count++) {
+ pair = CAR(init);
+ if (CONSP(pair)) {
+ symbol = CAR(pair);
+ pair = CDR(pair);
+ if (CONSP(pair)) {
+ value = CAR(pair);
+ if (CDR(pair) != NIL)
+ LispDestroy("%s: too much arguments to initialize %s",
+ STRFUN(builtin), STROBJ(symbol));
+ }
+ else
+ value = NIL;
+ }
+ else {
+ symbol = pair;
+ value = NIL;
+ }
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+
+ /* Add the variable */
+ ComPush(com, symbol, value, 1, 0, 0);
+ }
+
+ /* Stack length is increased */
+ CompileStackEnter(com, count, 0);
+ /* Bind the added variables */
+ com_Bind(com, count);
+ com->block->bind += count;
+ lisp__data.env.head += count;
+ /* Generate code for the body of the form */
+ ComProgn(com, body);
+ /* Unbind the added variables */
+ lisp__data.env.head -= count;
+ lisp__data.env.length -= count;
+ com->block->bind -= count;
+ com_Unbind(com, count);
+ /* Stack length is reduced. */
+ CompileStackLeave(com, count, 0);
+}
+
+void
+Com_Letx(LispCom *com, LispBuiltin *builtin)
+/*
+ let* init &rest body
+ */
+{
+ int count;
+ LispObj *symbol, *value, *pair;
+
+ LispObj *init, *body;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ if (init == NIL) {
+ /* If no local variables */
+ ComProgn(com, body);
+ return;
+ }
+ CHECK_CONS(body);
+
+ /* Could optimize if the body is empty and the
+ * init form is known to have no side effects */
+
+ for (count = 0; CONSP(init); init = CDR(init), count++) {
+ pair = CAR(init);
+ if (CONSP(pair)) {
+ symbol = CAR(pair);
+ pair = CDR(pair);
+ if (CONSP(pair)) {
+ value = CAR(pair);
+ if (CDR(pair) != NIL)
+ LispDestroy("%s: too much arguments to initialize %s",
+ STRFUN(builtin), STROBJ(symbol));
+ }
+ else
+ value = NIL;
+ }
+ else {
+ symbol = pair;
+ value = NIL;
+ }
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+
+ /* LET* is identical to &AUX arguments, just bind the symbol */
+ ComPush(com, symbol, value, 1, 0, 0);
+ /* Every added variable is binded */
+ com_Bind(com, 1);
+ /* Must be binded at compile time also */
+ ++lisp__data.env.head;
+ ++com->block->bind;
+ }
+
+ /* Generate code for the body of the form */
+ CompileStackEnter(com, count, 0);
+ ComProgn(com, body);
+ com_Unbind(com, count);
+ com->block->bind -= count;
+ lisp__data.env.head -= count;
+ lisp__data.env.length -= count;
+ CompileStackLeave(com, count, 0);
+}
+
+void
+Com_Listp(LispCom *com, LispBuiltin *builtin)
+/*
+ listp object
+ */
+{
+ ComPredicate(com, builtin, XBP_LISTP);
+}
+
+void
+Com_Loop(LispCom *com, LispBuiltin *builtin)
+/*
+ loop &rest body
+ */
+{
+ CodeTree *tree, *group;
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ /* Start NIL block */
+ CompileIniBlock(com, LispBlockTag, NIL);
+
+ /* Insert node to mark LOOP start */
+ tree = NEW_TREE(CodeTreeJump);
+ tree->code = XBC_NOOP;
+
+ /* Execute @BODY */
+ if (CONSP(body))
+ ComProgn(com, body);
+ else
+ /* XXX bytecode.c code require that blocks have at least one opcode */
+ com_Bytecode(com, XBC_NIL);
+
+ /* Insert node to jump of start of LOOP */
+ group = NEW_TREE(CodeTreeJump);
+ group->code = XBC_JUMP;
+ group->group = tree;
+
+ /* Finish NIL block */
+ CompileFiniBlock(com);
+}
+
+void
+Com_Nthcdr(LispCom *com, LispBuiltin *builtin)
+/*
+ nthcdr index list
+ */
+{
+ LispObj *oindex, *list;
+
+ list = ARGUMENT(1);
+ oindex = ARGUMENT(0);
+
+ ComEval(com, oindex);
+ CompileStackEnter(com, 1, 1);
+ com_Bytecode(com, XBC_PUSH);
+ ComEval(com, list);
+ CompileStackLeave(com, 1, 1);
+ com_Bytecode(com, XBC_NTHCDR);
+}
+
+void
+Com_Null(LispCom *com, LispBuiltin *builtin)
+/*
+ null list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ if (list == NIL)
+ com_Bytecode(com, XBC_T);
+ else if (ComConstantp(com, list))
+ com_Bytecode(com, XBC_NIL);
+ else {
+ ComEval(com, list);
+ com_Bytecode(com, XBC_INV);
+ }
+}
+
+void
+Com_Numberp(LispCom *com, LispBuiltin *builtin)
+/*
+ numberp object
+ */
+{
+ ComPredicate(com, builtin, XBP_NUMBERP);
+}
+
+void
+Com_Or(LispCom *com, LispBuiltin *builtin)
+/*
+ or &rest args
+ */
+{
+ LispObj *args;
+
+ args = ARGUMENT(0);
+
+ if (CONSP(args)) {
+ /* Evaluate first argument */
+ ComEval(com, CAR(args));
+ args = CDR(args);
+
+ /* If more than one argument, create jump list */
+ if (CONSP(args)) {
+ CodeTree *tree = NULL, *group;
+
+ group = NEW_TREE(CodeTreeJumpIf);
+ group->code = XBC_JUMPT;
+
+ for (; CONSP(args); args = CDR(args)) {
+ ComEval(com, CAR(args));
+ tree = NEW_TREE(CodeTreeJumpIf);
+ tree->code = XBC_JUMPT;
+ group->group = tree;
+ group = tree;
+ }
+ /* Finish form the last CodeTree code is changed to sign the
+ * end of the AND list */
+ group->code = XBC_NOOP;
+ group->group = tree;
+ }
+ }
+ else
+ /* Identity of OR is NIL */
+ com_Bytecode(com, XBC_NIL);
+}
+
+void
+Com_Progn(LispCom *com, LispBuiltin *builtin)
+/*
+ progn &rest body
+ */
+{
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ ComProgn(com, body);
+}
+
+void
+Com_Return(LispCom *com, LispBuiltin *builtin)
+/*
+ return &optional result
+ */
+{
+ ComReturnFrom(com, builtin, 0);
+}
+
+void
+Com_ReturnFrom(LispCom *com, LispBuiltin *builtin)
+/*
+ return-from name &optional result
+ */
+{
+ ComReturnFrom(com, builtin, 1);
+}
+
+void
+Com_Rplac_(LispCom *com, LispBuiltin *builtin)
+/*
+ rplac[ad] place value
+ */
+{
+ LispObj *place, *value;
+
+ value = ARGUMENT(1);
+ place = ARGUMENT(0);
+
+ CompileStackEnter(com, 1, 1);
+ ComEval(com, place);
+ com_Bytecode(com, XBC_PUSH);
+ ComEval(com, value);
+ com_Bytecode(com, STRFUN(builtin)[5] == 'A' ? XBC_RPLACA : XBC_RPLACD);
+ CompileStackLeave(com, 1, 1);
+}
+
+void
+Com_Setq(LispCom *com, LispBuiltin *builtin)
+/*
+ setq &rest form
+ */
+{
+ int offset;
+ LispObj *form, *symbol, *value;
+
+ form = ARGUMENT(0);
+
+ for (; CONSP(form); form = CDR(form)) {
+ symbol = CAR(form);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ form = CDR(form);
+ if (!CONSP(form))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ value = CAR(form);
+ /* Generate code to load value */
+ ComEval(com, value);
+ offset = ComGetVariable(com, symbol);
+ if (offset >= 0)
+ com_Set(com, offset);
+ else
+ com_SetSym(com, symbol->data.atom);
+ }
+}
+
+void
+Com_Tagbody(LispCom *com, LispBuiltin *builtin)
+/*
+ tagbody &rest body
+ */
+{
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ if (CONSP(body)) {
+ CompileIniBlock(com, LispBlockBody, NIL);
+ ComProgn(com, body);
+ /* Tagbody returns NIL */
+ com_Bytecode(com, XBC_NIL);
+ CompileFiniBlock(com);
+ }
+ else
+ /* Tagbody always returns NIL */
+ com_Bytecode(com, XBC_NIL);
+}
+
+void
+Com_Unless(LispCom *com, LispBuiltin *builtin)
+/*
+ unless test &rest body
+ */
+{
+ CodeTree *group, *tree;
+ LispObj *test, *body;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ /* Generate code to evaluate test */
+ ComEval(com, test);
+ /* Add node after test */
+ group = NEW_TREE(CodeTreeJumpIf);
+ group->code = XBC_JUMPT;
+ /* Generate NIL code */
+ ComProgn(com, body);
+ /* Insert node to know where to jump if test is T */
+ tree = NEW_TREE(CodeTreeJumpIf);
+ tree->code = XBC_NOOP;
+ group->group = tree;
+}
+
+void
+Com_Until(LispCom *com, LispBuiltin *builtin)
+/*
+ until test &rest body
+ */
+{
+ CodeTree *tree, *group, *ltree, *lgroup;
+ LispObj *test, *body;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ /* Insert node to mark LOOP start */
+ ltree = NEW_TREE(CodeTreeJump);
+ ltree->code = XBC_NOOP;
+
+ /* Build code for test */
+ ComEval(com, test);
+ group = NEW_TREE(CodeTreeJumpIf);
+ group->code = XBC_JUMPT;
+
+ /* Execute @BODY */
+ ComProgn(com, body);
+
+ /* Insert node to jump to test again */
+ lgroup = NEW_TREE(CodeTreeJump);
+ lgroup->code = XBC_JUMP;
+ lgroup->group = ltree;
+
+ /* Insert node to know where to jump if test is T */
+ tree = NEW_TREE(CodeTreeJumpIf);
+ tree->code = XBC_NOOP;
+ group->group = tree;
+}
+
+void
+Com_When(LispCom *com, LispBuiltin *builtin)
+/*
+ when test &rest body
+ */
+{
+ CodeTree *group, *tree;
+ LispObj *test, *body;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ /* Generate code to evaluate test */
+ ComEval(com, test);
+ /* Add node after test */
+ group = NEW_TREE(CodeTreeJumpIf);
+ group->code = XBC_JUMPNIL;
+ /* Generate T code */
+ ComProgn(com, body);
+ /* Insert node to know where to jump if test is NIL */
+ tree = NEW_TREE(CodeTreeJumpIf);
+ tree->code = XBC_NOOP;
+ group->group = tree;
+}
+
+void
+Com_While(LispCom *com, LispBuiltin *builtin)
+/*
+ while test &rest body
+ */
+{
+ CodeTree *tree, *group, *ltree, *lgroup;
+ LispObj *test, *body;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ /* Insert node to mark LOOP start */
+ ltree = NEW_TREE(CodeTreeJump);
+ ltree->code = XBC_NOOP;
+
+ /* Build code for test */
+ ComEval(com, test);
+ group = NEW_TREE(CodeTreeJumpIf);
+ group->code = XBC_JUMPNIL;
+
+ /* Execute @BODY */
+ ComProgn(com, body);
+
+ /* Insert node to jump to test again */
+ lgroup = NEW_TREE(CodeTreeJump);
+ lgroup->code = XBC_JUMP;
+ lgroup->group = ltree;
+
+ /* Insert node to know where to jump if test is NIL */
+ tree = NEW_TREE(CodeTreeJumpIf);
+ tree->code = XBC_NOOP;
+ group->group = tree;
+}
+
+
+/***********************************************************************
+ * Com_XXX helper functions
+ ***********************************************************************/
+static void
+ComPredicate(LispCom *com, LispBuiltin *builtin, LispBytePredicate predicate)
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ if (ComConstantp(com, object)) {
+ switch (predicate) {
+ case XBP_CONSP:
+ com_Bytecode(com, CONSP(object) ? XBC_T : XBC_NIL);
+ break;
+ case XBP_LISTP:
+ com_Bytecode(com, CONSP(object) || object == NIL ?
+ XBC_T : XBC_NIL);
+ break;
+ case XBP_NUMBERP:
+ com_Bytecode(com, NUMBERP(object) ? XBC_T : XBC_NIL);
+ break;
+ }
+ }
+ else {
+ ComEval(com, object);
+ com_BytecodeChar(com, XBC_PRED, predicate);
+ }
+}
+
+/* XXX Could receive an argument telling if is the last statement in the
+ * block(s), i.e. if a jump opcode should be generated or just the
+ * evaluation of the returned value. Probably this is better done in
+ * an optimization step. */
+static void
+ComReturnFrom(LispCom *com, LispBuiltin *builtin, int from)
+{
+ int bind;
+ CodeTree *tree;
+ LispObj *name, *result;
+ CodeBlock *block = com->block;
+
+ if (from) {
+ result = ARGUMENT(1);
+ name = ARGUMENT(0);
+ }
+ else {
+ result = ARGUMENT(0);
+ name = NIL;
+ }
+ if (result == UNSPEC)
+ result = NIL;
+
+ bind = block->bind;
+ while (block) {
+ if (block->type == LispBlockClosure)
+ /* A function call */
+ break;
+ else if (block->type == LispBlockTag && block->tag == name)
+ break;
+ block = block->prev;
+ if (block)
+ bind += block->bind;
+ }
+
+ if (!block || block->tag != name)
+ LispDestroy("%s: no visible %s block", STRFUN(builtin), STROBJ(name));
+
+ /* Generate code to load result */
+ ComEval(com, result);
+
+ /* Check for added variables that the jump is skiping the unbind opcode */
+ com_Unbind(com, bind);
+
+ tree = NEW_TREE(CodeTreeReturn);
+ tree->data.block = block;
+}
+
+/***********************************************************************
+ * Helper functions
+ ***********************************************************************/
+static int
+ComConstantp(LispCom *com, LispObj *object)
+{
+ switch (OBJECT_TYPE(object)) {
+ case LispAtom_t:
+ /* Keywords are guaranteed to evaluate to itself */
+ if (object->data.atom->package == lisp__data.keyword)
+ break;
+ return (0);
+
+ /* Function call */
+ case LispCons_t:
+
+ /* Need macro expansion, these are special abstract objects */
+ case LispQuote_t:
+ case LispBackquote_t:
+ case LispComma_t:
+ case LispFunctionQuote_t:
+ return (0);
+
+ /* Anything else is a literal constant */
+ default:
+ break;
+ }
+
+ return (1);
+}
+
+static int
+FindIndex(void *item, void **table, int length)
+{
+ long cmp;
+ int left, right, i;
+
+ left = 0;
+ right = length - 1;
+ while (left <= right) {
+ i = (left + right) >> 1;
+ cmp = (char*)item - (char*)table[i];
+ if (cmp == 0)
+ return (i);
+ else if (cmp < 0)
+ right = i - 1;
+ else
+ left = i + 1;
+ }
+
+ return (-1);
+}
+
+static int
+compare(const void *left, const void *right)
+{
+ long cmp = *(char**)left - *(char**)right;
+
+ return (cmp < 0 ? -1 : 1);
+}
+
+static int
+BuildTablePointer(void *pointer, void ***pointers, int *num_pointers)
+{
+ int i;
+
+ if ((i = FindIndex(pointer, *pointers, *num_pointers)) < 0) {
+ *pointers = LispRealloc(*pointers,
+ sizeof(void*) * (*num_pointers + 1));
+ (*pointers)[*num_pointers] = pointer;
+ if (++*num_pointers > 1)
+ qsort(*pointers, *num_pointers, sizeof(void*), compare);
+ i = FindIndex(pointer, *pointers, *num_pointers);
+ }
+
+ return (i);
+}
+
+static void
+ComAddVariable(LispCom *com, LispObj *symbol, LispObj *value)
+{
+ LispAtom *atom = symbol->data.atom;
+
+ if (atom && atom->string && !com->macro) {
+ int i, length = com->block->variables.length;
+
+ i = BuildTablePointer(atom, (void***)&com->block->variables.symbols,
+ &com->block->variables.length);
+
+ if (com->block->variables.length != length) {
+ com->block->variables.flags =
+ LispRealloc(com->block->variables.flags,
+ com->block->variables.length * sizeof(int));
+
+ /* Variable was inserted in the middle of the list */
+ if (i < length)
+ memmove(com->block->variables.flags + i + 1,
+ com->block->variables.flags + i,
+ (length - i) * sizeof(int));
+
+ com->block->variables.flags[i] = 0;
+ }
+ }
+
+ LispAddVar(symbol, value);
+}
+
+static int
+ComGetVariable(LispCom *com, LispObj *symbol)
+{
+ LispAtom *name;
+ int i, base, offset;
+ Atom_id id;
+
+ name = symbol->data.atom;
+ if (name->constant) {
+ if (name->package == lisp__data.keyword)
+ /* Just load <symbol> from the byte stream, keywords are
+ * guaranteed to evaluate to itself. */
+ return (SYMBOL_KEYWORD);
+ return (SYMBOL_CONSTANT);
+ }
+
+ offset = name->offset;
+ id = name->string;
+ base = lisp__data.env.lex;
+ i = lisp__data.env.head - 1;
+
+ /* If variable is local */
+ if (offset <= i && offset >= com->lex && lisp__data.env.names[offset] == id) {
+ COM_VARIABLE_USED(name);
+ /* Relative offset */
+ return (offset - base);
+ }
+
+ /* name->offset may have been changed in a macro expansion */
+ for (; i >= com->lex; i--)
+ if (lisp__data.env.names[i] == id) {
+ name->offset = i;
+ COM_VARIABLE_USED(name);
+ return (i - base);
+ }
+
+ if (!name->a_object) {
+ ++com->warnings;
+ LispWarning("variable %s is neither declared nor bound",
+ name->string);
+ }
+
+ /* Not found, resolve <symbol> at run time */
+ return (SYMBOL_UNBOUND);
+}
+
+static void
+ComVariableSetFlag(LispCom *com, LispAtom *atom, int flag)
+{
+ int i;
+ CodeBlock *block = com->block;
+
+ while (block) {
+ i = FindIndex(atom, (void**)block->variables.symbols,
+ block->variables.length);
+ if (i >= 0) {
+ block->variables.flags[i] |= flag;
+ /* Descend block list if an argument to function being called
+ * has the same name as a bound variable in the current function.
+ */
+ if ((flag & VARIABLE_ARGUMENT) ||
+ !(block->variables.flags[i] & VARIABLE_ARGUMENT))
+ break;
+ }
+ block = block->prev;
+ }
+}
+
+/***********************************************************************
+ * Bytecode compiler functions
+ ***********************************************************************/
+static void
+ComLabel(LispCom *com, LispObj *label)
+{
+ int i;
+ CodeTree *tree;
+
+ for (i = 0; i < com->block->tagbody.length; i++)
+ if (label == com->block->tagbody.labels[i])
+ LispDestroy("TAGBODY: tag %s specified more than once",
+ STROBJ(label));
+
+ if (com->block->tagbody.length >= com->block->tagbody.space) {
+ com->block->tagbody.labels =
+ LispRealloc(com->block->tagbody.labels,
+ sizeof(LispObj*) * (com->block->tagbody.space + 8));
+ /* Reserve space, will be used at link time when
+ * resolving GO jumps. */
+ com->block->tagbody.codes =
+ LispRealloc(com->block->tagbody.codes,
+ sizeof(CodeTree*) * (com->block->tagbody.space + 8));
+ com->block->tagbody.space += 8;
+ }
+
+ com->block->tagbody.labels[com->block->tagbody.length++] = label;
+ tree = NEW_TREE(CodeTreeLabel);
+ tree->data.object = label;
+}
+
+static void
+ComPush(LispCom *com, LispObj *symbol, LispObj *value,
+ int eval, int builtin, int compile)
+{
+ /* If <compile> is set, it is pushing an argument to one of
+ * Com_XXX functions. */
+ if (compile) {
+ if (builtin)
+ lisp__data.stack.values[lisp__data.stack.length++] = value;
+ else
+ ComAddVariable(com, symbol, value);
+ return;
+ }
+
+ /* If <com->macro> is set, it is expanding a macro, just add the local
+ * variable <symbol> bounded to <value>, so that it will be available
+ * when calling the interpreter to expand the macro. */
+ else if (com->macro) {
+ ComAddVariable(com, symbol, value);
+ return;
+ }
+
+ /* If <eval> is set, it must generate the opcodes to evaluate <value>.
+ * If <value> is a constant, just generate the opcodes to load it. */
+ else if (eval && !ComConstantp(com, value)) {
+ switch (OBJECT_TYPE(value)) {
+ case LispAtom_t: {
+ int offset = ComGetVariable(com, value);
+
+ if (offset >= 0) {
+ /* Load <value> from user stack at the relative offset */
+ if (builtin)
+ com_LoadPush(com, offset);
+ else
+ com_LoadLet(com, offset, symbol->data.atom);
+ }
+ /* ComConstantp() does not return true for this, as the
+ * current value must be computed. */
+ else if (offset == SYMBOL_CONSTANT) {
+ value = value->data.atom->property->value;
+ if (builtin)
+ com_LoadConPush(com, value);
+ else
+ com_LoadConLet(com, value, symbol->data.atom);
+ }
+ else {
+ /* Load value bound to <value> at run time */
+ if (builtin)
+ com_LoadSymPush(com, value->data.atom);
+ else
+ com_LoadSymLet(com, value->data.atom,
+ symbol->data.atom);
+ }
+ } break;
+
+ default:
+ /* Generate code to evaluate <value> */
+ ComEval(com, value);
+ if (builtin)
+ com_Bytecode(com, XBC_PUSH);
+ else
+ com_Let(com, symbol->data.atom);
+ break;
+ }
+
+ /* Remember <symbol> will be bound, <value> only matters for
+ * the Com_XXX functions */
+ if (builtin)
+ lisp__data.stack.values[lisp__data.stack.length++] = value;
+ else
+ ComAddVariable(com, symbol, value);
+ return;
+ }
+
+ if (builtin) {
+ /* Load <value> as a constant in builtin stack */
+ com_LoadConPush(com, value);
+ lisp__data.stack.values[lisp__data.stack.length++] = value;
+ }
+ else {
+ /* Load <value> as a constant in stack */
+ com_LoadConLet(com, value, symbol->data.atom);
+ /* Remember <symbol> will be bound */
+ ComAddVariable(com, symbol, value);
+ }
+}
+
+/* This function does almost the same job as LispMakeEnvironment, but
+ * it is not optimized for speed, as it is not building argument lists
+ * to user code, but to Com_XXX functions, or helping in generating the
+ * opcodes to load arguments at bytecode run time. */
+static int
+ComCall(LispCom *com, LispArgList *alist,
+ LispObj *name, LispObj *values,
+ int eval, int builtin, int compile)
+{
+ char *desc;
+ int i, count, base;
+ LispObj **symbols, **defaults, **sforms;
+
+ if (builtin) {
+ base = lisp__data.stack.length;
+ /* This should never be executed, but make the check for safety */
+ if (base + alist->num_arguments > lisp__data.stack.space) {
+ do
+ LispMoreStack();
+ while (base + alist->num_arguments > lisp__data.stack.space);
+ }
+ }
+ else
+ base = lisp__data.env.length;
+
+ desc = alist->description;
+ switch (*desc++) {
+ case '.':
+ goto normal_label;
+ case 'o':
+ goto optional_label;
+ case 'k':
+ goto key_label;
+ case 'r':
+ goto rest_label;
+ case 'a':
+ goto aux_label;
+ default:
+ goto done_label;
+ }
+
+
+ /* Normal arguments */
+normal_label:
+ i = 0;
+ symbols = alist->normals.symbols;
+ count = alist->normals.num_symbols;
+ for (; i < count && CONSP(values); i++, values = CDR(values)) {
+ ComPush(com, symbols[i], CAR(values), eval, builtin, compile);
+ if (!builtin && !com->macro)
+ COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
+ }
+ if (i < count)
+ LispDestroy("%s: too few arguments", STROBJ(name));
+
+ switch (*desc++) {
+ case 'o':
+ goto optional_label;
+ case 'k':
+ goto key_label;
+ case 'r':
+ goto rest_label;
+ case 'a':
+ goto aux_label;
+ default:
+ goto done_label;
+ }
+
+
+ /* &OPTIONAL */
+optional_label:
+ i = 0;
+ count = alist->optionals.num_symbols;
+ symbols = alist->optionals.symbols;
+ defaults = alist->optionals.defaults;
+ sforms = alist->optionals.sforms;
+ for (; i < count && CONSP(values); i++, values = CDR(values)) {
+ ComPush(com, symbols[i], CAR(values), eval, builtin, compile);
+ if (!builtin && !com->macro)
+ COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
+ if (sforms[i]) {
+ ComPush(com, sforms[i], T, 0, builtin, compile);
+ if (!builtin && !com->macro)
+ COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
+ }
+ }
+ for (; i < count; i++) {
+ if (!builtin) {
+ int lex = com->lex;
+ int head = lisp__data.env.head;
+
+ com->lex = base;
+ lisp__data.env.head = lisp__data.env.length;
+ /* default arguments are evaluated for macros */
+ ComPush(com, symbols[i], defaults[i], 1, 0, compile);
+ if (!com->macro)
+ COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
+ lisp__data.env.head = head;
+ com->lex = lex;
+ }
+ else
+ ComPush(com, symbols[i], defaults[i], eval, 1, compile);
+ if (sforms[i]) {
+ ComPush(com, sforms[i], NIL, 0, builtin, compile);
+ if (!builtin && !com->macro)
+ COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
+ }
+ }
+
+ switch (*desc++) {
+ case 'k':
+ goto key_label;
+ case 'r':
+ goto rest_label;
+ case 'a':
+ goto aux_label;
+ default:
+ goto done_label;
+ }
+
+
+ /* &KEY */
+key_label:
+ {
+ int varset;
+ LispObj *val, *karg, **keys;
+
+ count = alist->keys.num_symbols;
+ symbols = alist->keys.symbols;
+ defaults = alist->keys.defaults;
+ sforms = alist->keys.sforms;
+ keys = alist->keys.keys;
+
+ /* Check if arguments are correctly specified */
+ for (karg = values; CONSP(karg); karg = CDR(karg)) {
+ val = CAR(karg);
+ if (KEYWORDP(val)) {
+ for (i = 0; i < alist->keys.num_symbols; i++)
+ if (!keys[i] && symbols[i] == val)
+ break;
+ }
+
+ else if (!builtin &&
+ QUOTEP(val) && SYMBOLP(val->data.quote)) {
+ for (i = 0; i < alist->keys.num_symbols; i++)
+ if (keys[i] && ATOMID(keys[i]) == ATOMID(val->data.quote))
+ break;
+ }
+
+ else
+ /* Just make the error test true */
+ i = alist->keys.num_symbols;
+
+ if (i == alist->keys.num_symbols) {
+ /* If not in argument specification list... */
+ char function_name[36];
+
+ strcpy(function_name, STROBJ(name));
+ LispDestroy("%s: invalid keyword %s",
+ function_name, STROBJ(val));
+ }
+
+ karg = CDR(karg);
+ if (!CONSP(karg))
+ LispDestroy("%s: &KEY needs arguments as pairs",
+ STROBJ(name));
+ }
+
+ /* Add variables */
+ for (i = 0; i < alist->keys.num_symbols; i++) {
+ val = defaults[i];
+ varset = 0;
+ if (!builtin && keys[i]) {
+ Atom_id atom = ATOMID(keys[i]);
+
+ /* Special keyword specification, need to compare ATOMID
+ * and keyword specification must be a quoted object */
+ for (karg = values; CONSP(karg); karg = CDR(karg)) {
+ val = CAR(karg);
+ if (QUOTEP(val) && atom == ATOMID(val->data.quote)) {
+ val = CADR(karg);
+ varset = 1;
+ break;
+ }
+ karg = CDR(karg);
+ }
+ }
+
+ else {
+ /* Normal keyword specification, can compare object pointers,
+ * as they point to the same object in the keyword package */
+ for (karg = values; CONSP(karg); karg = CDR(karg)) {
+ /* Don't check if argument is a valid keyword or
+ * special quoted keyword */
+ if (symbols[i] == CAR(karg)) {
+ val = CADR(karg);
+ varset = 1;
+ break;
+ }
+ karg = CDR(karg);
+ }
+ }
+
+ /* Add the variable to environment */
+ if (varset) {
+ ComPush(com, symbols[i], val, eval, builtin, compile);
+ if (sforms[i])
+ ComPush(com, sforms[i], T, 0, builtin, compile);
+ }
+ else {
+ /* default arguments are evaluated for macros */
+ if (!builtin) {
+ int lex = com->lex;
+ int head = lisp__data.env.head;
+
+ com->lex = base;
+ lisp__data.env.head = lisp__data.env.length;
+ ComPush(com, symbols[i], val, eval, 0, compile);
+ lisp__data.env.head = head;
+ com->lex = lex;
+ }
+ else
+ ComPush(com, symbols[i], val, eval, builtin, compile);
+ if (sforms[i])
+ ComPush(com, sforms[i], NIL, 0, builtin, compile);
+ }
+ if (!builtin && !com->macro) {
+ COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
+ if (sforms[i])
+ COM_VARIABLE_ARGUMENT(sforms[i]->data.atom);
+ }
+ }
+ }
+
+ if (*desc == 'a') {
+ /* &KEY uses all remaining arguments */
+ values = NIL;
+ goto aux_label;
+ }
+ goto finished_label;
+
+
+ /* &REST */
+rest_label:
+ if (!eval || !CONSP(values) || (compile && !builtin))
+ ComPush(com, alist->rest, values, eval, builtin, compile);
+ else {
+ char *string;
+ LispObj *list, *car = NIL;
+ int count, constantp;
+
+ /* Count number of arguments and check if it is a list of constants */
+ for (count = 0, constantp = 1, list = values;
+ CONSP(list);
+ list = CDR(list), count++) {
+ car = CAR(list);
+ if (!ComConstantp(com, car))
+ constantp = 0;
+ }
+
+ string = builtin ? ATOMID(name) : 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). */
+ if (string && (count < MAX_BCONS || constantp) &&
+ strcmp(string, "LIST") &&
+ strcmp(string, "APPLY") && /* XXX depends on function argument */
+ strcmp(string, "VECTOR") &&
+ /* Append does not copy the last/single list */
+ (strcmp(string, "APPEND") || !CONSP(car))) {
+ if (constantp) {
+ /* If the builtin function changes the &REST parameters, must
+ * define a Com_XXX function for it. */
+ ComPush(com, alist->rest, values, 0, builtin, compile);
+ }
+ else {
+ CompileStackEnter(com, count - 1, 1);
+ for (; CONSP(CDR(values)); values = CDR(values)) {
+ /* Evaluate this argument */
+ ComEval(com, CAR(values));
+ /* Save result in builtin stack */
+ com_Bytecode(com, XBC_PUSH);
+ }
+ CompileStackLeave(com, count - 1, 1);
+ /* The last argument is not saved in the stack */
+ ComEval(com, CAR(values));
+ values = NIL;
+ com_Bytecode(com, XBC_BCONS + (count - 1));
+ }
+ }
+ else {
+ /* Allocate a fresh list of cons */
+
+ /* Generate code to load object */
+ ComEval(com, CAR(values));
+
+ com->stack.cpstack += 2;
+ if (com->stack.pstack < com->stack.cpstack)
+ com->stack.pstack = com->stack.cpstack;
+ /* Start building a gc protected list, with the loaded value */
+ com_Bytecode(com, XBC_LSTAR);
+
+ for (values = CDR(values); CONSP(values); values = CDR(values)) {
+ /* Generate code to load object */
+ ComEval(com, CAR(values));
+
+ /* Add loaded value to gc protected list */
+ com_Bytecode(com, XBC_LCONS);
+ }
+
+ /* Finish gc protected list */
+ com_Bytecode(com, XBC_LFINI);
+
+ /* Push loaded value */
+ if (builtin)
+ com_Bytecode(com, XBC_PUSH);
+ else {
+ com_Let(com, alist->rest->data.atom);
+
+ /* Remember this symbol will be bound */
+ ComAddVariable(com, alist->rest, values);
+ }
+ com->stack.cpstack -= 2;
+ }
+ }
+ if (!builtin && !com->macro)
+ COM_VARIABLE_ARGUMENT(alist->rest->data.atom);
+ if (*desc != 'a')
+ goto finished_label;
+
+
+ /* &AUX */
+aux_label:
+ i = 0;
+ count = alist->auxs.num_symbols;
+ symbols = alist->auxs.symbols;
+ defaults = alist->auxs.initials;
+ if (!builtin && !compile) {
+ int lex = com->lex;
+
+ com->lex = base;
+ lisp__data.env.head = lisp__data.env.length;
+ for (; i < count; i++) {
+ ComPush(com, symbols[i], defaults[i], 1, 0, 0);
+ if (!com->macro)
+ COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
+ ++lisp__data.env.head;
+ }
+ com->lex = lex;
+ }
+ else {
+ for (; i < count; i++) {
+ ComPush(com, symbols[i], defaults[i], eval, builtin, compile);
+ if (!builtin && !com->macro)
+ COM_VARIABLE_ARGUMENT(symbols[i]->data.atom);
+ }
+ }
+
+done_label:
+ if (CONSP(values))
+ LispDestroy("%s: too many arguments", STROBJ(name));
+
+finished_label:
+ if (builtin)
+ lisp__data.stack.base = base;
+ else
+ lisp__data.env.head = lisp__data.env.length;
+
+ return (base);
+}
+
+static void
+ComFuncall(LispCom *com, LispObj *function, LispObj *arguments, int eval)
+{
+ int base, compile;
+ LispAtom *atom;
+ LispArgList *alist;
+ LispBuiltin *builtin;
+ LispObj *lambda;
+
+ switch (OBJECT_TYPE(function)) {
+ case LispFunction_t:
+ function = function->data.atom->object;
+ case LispAtom_t:
+ atom = function->data.atom;
+ alist = atom->property->alist;
+
+ if (atom->a_builtin) {
+ builtin = atom->property->fun.builtin;
+ compile = builtin->compile != NULL;
+
+ /* If one of:
+ * o expanding a macro
+ * o calling a builtin special form
+ * o builtin function is a macro
+ * don't evaluate arguments. */
+ if (com->macro || compile || builtin->type == LispMacro)
+ eval = 0;
+
+ if (!com->macro && builtin->type == LispMacro) {
+ /* Set flag of variable used, in case variable is only
+ * used as a builtin macro argument. */
+ LispObj *obj;
+
+ for (obj = arguments; CONSP(obj); obj = CDR(obj)) {
+ if (SYMBOLP(CAR(obj)))
+ COM_VARIABLE_USED(CAR(obj)->data.atom);
+ }
+ }
+
+ FORM_ENTER();
+ if (!compile && !com->macro)
+ CompileStackEnter(com, alist->num_arguments, 1);
+
+ /* Build argument list in the interpreter stacks */
+ base = ComCall(com, alist, function, arguments,
+ eval, 1, compile);
+
+ /* If <compile> is set, it is a special form */
+ if (compile)
+ builtin->compile(com, builtin);
+
+ /* Else, generate opcodes to call builtin function */
+ else {
+ com_Call(com, alist->num_arguments, builtin);
+ CompileStackLeave(com, alist->num_arguments, 1);
+ }
+ lisp__data.stack.base = lisp__data.stack.length = base;
+ FORM_LEAVE();
+ }
+ else if (atom->a_function) {
+ int macro;
+
+ lambda = atom->property->fun.function;
+ macro = lambda->funtype == LispMacro;
+
+ /* If <macro> is set, expand macro */
+ if (macro)
+ ComMacroCall(com, alist, function, lambda, arguments);
+
+ else {
+ if (com->toplevel->type == LispBlockClosure &&
+ com->toplevel->tag == function)
+ ComRecursiveCall(com, alist, function, arguments);
+ else {
+#if 0
+ ComInlineCall(com, alist, function, arguments,
+ lambda->data.lambda.code);
+#else
+ com_Funcall(com, function, arguments);
+#endif
+ }
+ }
+ }
+ else if (atom->a_defstruct &&
+ atom->property->structure.function != STRUCT_NAME &&
+ atom->property->structure.function != STRUCT_CONSTRUCTOR) {
+ LispObj *definition = atom->property->structure.definition;
+
+ if (!CONSP(arguments) || CONSP(CDR(arguments)))
+ LispDestroy("%s: too %s arguments", atom->string,
+ CONSP(arguments) ? "many" : "few");
+
+ ComEval(com, CAR(arguments));
+ if (atom->property->structure.function == STRUCT_CHECK)
+ com_Structp(com, definition);
+ else
+ com_Struct(com,
+ atom->property->structure.function, definition);
+ }
+ else if (atom->a_compiled) {
+ FORM_ENTER();
+ CompileStackEnter(com, alist->num_arguments, 0);
+
+ /* Build argument list in the interpreter stacks */
+ base = ComCall(com, alist, function, arguments, 1, 0, 0);
+ com_Bytecall(com, alist->num_arguments,
+ atom->property->fun.function);
+ CompileStackLeave(com, alist->num_arguments, 0);
+ lisp__data.env.head = lisp__data.env.length = base;
+ FORM_LEAVE();
+ }
+ else {
+ /* Not yet defined function/macro. */
+ ++com->warnings;
+ LispWarning("call to undefined function %s", atom->string);
+ com_Funcall(com, function, arguments);
+ }
+ break;
+
+ case LispLambda_t:
+ lambda = function->data.lambda.code;
+ alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
+ ComInlineCall(com, alist, NIL, arguments, lambda->data.lambda.code);
+ break;
+
+ case LispCons_t:
+ if (CAR(function) == Olambda) {
+ function = EVAL(function);
+ if (LAMBDAP(function)) {
+ GC_ENTER();
+
+ GC_PROTECT(function);
+ lambda = function->data.lambda.code;
+ alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
+ ComInlineCall(com, alist, NIL, arguments, lambda->data.lambda.code);
+ GC_LEAVE();
+ break;
+ }
+ }
+
+ default:
+ /* XXX If bytecode objects are made available, should
+ * handle it here. */
+ LispDestroy("EVAL: %s is invalid as a function",
+ STROBJ(function));
+ /*NOTREACHED*/
+ break;
+ }
+}
+
+/* Generate opcodes for an implicit PROGN */
+static void
+ComProgn(LispCom *com, LispObj *code)
+{
+ if (CONSP(code)) {
+ for (; CONSP(code); code = CDR(code))
+ ComEval(com, CAR(code));
+ }
+ else
+ /* If no code to execute, empty PROGN returns NIL */
+ com_Bytecode(com, XBC_NIL);
+}
+
+/* Generate opcodes to evaluate <object>. */
+static void
+ComEval(LispCom *com, LispObj *object)
+{
+ int offset;
+ LispObj *form;
+
+ switch (OBJECT_TYPE(object)) {
+ case LispAtom_t:
+ if (IN_TAGBODY())
+ ComLabel(com, object);
+ else {
+ offset = ComGetVariable(com, object);
+ if (offset >= 0)
+ /* Load from user stack at relative offset */
+ com_Load(com, offset);
+ else if (offset == SYMBOL_KEYWORD)
+ com_LoadCon(com, object);
+ else if (offset == SYMBOL_CONSTANT)
+ /* Symbol defined as constant, just load it's value */
+ com_LoadCon(com, LispGetVar(object));
+ else
+ /* Load value bound to symbol at run time */
+ com_LoadSym(com, object->data.atom);
+ }
+ break;
+
+ case LispCons_t: {
+ /* Macro expansion may be done in the object form */
+ form = com->form;
+ com->form = object;
+ ComFuncall(com, CAR(object), CDR(object), 1);
+ com->form = form;
+ } break;
+
+ case LispQuote_t:
+ com_LoadCon(com, object->data.quote);
+ break;
+
+ case LispBackquote_t:
+ /* Macro expansion is stored in the current value of com->form */
+ ComMacroBackquote(com, object);
+ break;
+
+ case LispComma_t:
+ LispDestroy("EVAL: comma outside of backquote");
+ break;
+
+ case LispFunctionQuote_t:
+ object = object->data.quote;
+ if (SYMBOLP(object))
+ object = LispSymbolFunction(object);
+ else if (CONSP(object) && CAR(object) == Olambda) {
+ /* object will only be associated with bytecode later,
+ * so, make sure it is protected until compilation finishes */
+ object = EVAL(object);
+ RPLACD(com->plist, CONS(CAR(com->plist), CDR(com->plist)));
+ RPLACA(com->plist, object);
+ }
+ else
+ LispDestroy("FUNCTION: %s is not a function", STROBJ(object));
+ com_LoadCon(com, object);
+ break;
+
+ case LispFixnum_t:
+ if (IN_TAGBODY()) {
+ ComLabel(com, object);
+ break;
+ }
+ /*FALLTROUGH*/
+
+ default:
+ /* Constant object */
+ com_LoadCon(com, object);
+ break;
+ }
+}
+
+/***********************************************************************
+ * Lambda expansion helper functions
+ ***********************************************************************/
+static void
+ComRecursiveCall(LispCom *com, LispArgList *alist,
+ LispObj *name, LispObj *arguments)
+{
+ int base, lex;
+
+ /* Save state */
+ lex = lisp__data.env.lex;
+
+ FORM_ENTER();
+
+ /* Generate code to push function arguments in the stack */
+ base = ComCall(com, alist, name, arguments, 1, 0, 0);
+
+ /* Stack will grow this amount */
+ CompileStackEnter(com, alist->num_arguments, 0);
+
+#if 0
+ /* Make the variables available at run time */
+ com_Bind(com, alist->num_arguments);
+ com->block->bind += alist->num_arguments;
+#endif
+
+ com_BytecodeChar(com, XBC_LETREC, alist->num_arguments);
+
+#if 0
+ /* The variables are now unbound */
+ com_Unbind(com, alist->num_arguments);
+ com->block->bind -= alist->num_arguments;
+#endif
+
+ /* Stack length is reduced */
+ CompileStackLeave(com, alist->num_arguments, 0);
+ FORM_LEAVE();
+
+ /* Restore state */
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = base;
+}
+
+static void
+ComInlineCall(LispCom *com, LispArgList *alist,
+ LispObj *name, LispObj *arguments, LispObj *lambda)
+{
+ int base, lex;
+
+ /* Save state */
+ lex = lisp__data.env.lex;
+
+ FORM_ENTER();
+ /* Start the inline function block */
+ CompileIniBlock(com, LispBlockClosure, name);
+
+ /* Generate code to push function arguments in the stack */
+ base = ComCall(com, alist, name, arguments, 1, 0, 0);
+
+ /* Stack will grow this amount */
+ CompileStackEnter(com, alist->num_arguments, 0);
+
+ /* Make the variables available at run time */
+ com_Bind(com, alist->num_arguments);
+ com->block->bind += alist->num_arguments;
+
+ /* Expand the lambda list */
+ ComProgn(com, lambda);
+
+ /* The variables are now unbound */
+ com_Unbind(com, alist->num_arguments);
+ com->block->bind -= alist->num_arguments;
+
+ /* Stack length is reduced */
+ CompileStackLeave(com, alist->num_arguments, 0);
+
+ /* Finish the inline function block */
+ CompileFiniBlock(com);
+ FORM_LEAVE();
+
+ /* Restore state */
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = base;
+}
+
+/***********************************************************************
+ * Macro expansion helper functions.
+ ***********************************************************************/
+static LispObj *
+ComMacroExpandBackquote(LispCom *com, LispObj *object)
+{
+ return (LispEvalBackquote(object->data.quote, 1));
+}
+
+static LispObj *
+ComMacroExpandFuncall(LispCom *com, LispObj *function, LispObj *arguments)
+{
+ return (LispFuncall(function, arguments, 1));
+}
+
+static LispObj *
+ComMacroExpandEval(LispCom *com, LispObj *object)
+{
+ LispObj *result;
+
+ switch (OBJECT_TYPE(object)) {
+ case LispAtom_t:
+ result = LispGetVar(object);
+
+ /* Macro expansion requires bounded symbols */
+ if (result == NULL)
+ LispDestroy("EVAL: the variable %s is unbound",
+ STROBJ(object));
+ break;
+
+ case LispCons_t:
+ result = ComMacroExpandFuncall(com, CAR(object), CDR(object));
+ break;
+
+ case LispQuote_t:
+ result = object->data.quote;
+ break;
+
+ case LispBackquote_t:
+ result = ComMacroExpandBackquote(com, object);
+ break;
+
+ case LispComma_t:
+ LispDestroy("EVAL: comma outside of backquote");
+
+ case LispFunctionQuote_t:
+ result = EVAL(object);
+ break;
+
+ default:
+ result = object;
+ break;
+ }
+
+ return (result);
+}
+
+static LispObj *
+ComMacroExpand(LispCom *com, LispObj *lambda)
+{
+ LispObj *result, **presult = &result, **plambda;
+ int jumped, *pjumped = &jumped, backquote, *pbackquote = &backquote;
+ LispBlock *block;
+
+ int interpreter_lex, interpreter_head, interpreter_base;
+
+ /* Save interpreter state */
+ interpreter_base = lisp__data.stack.length;
+ interpreter_head = lisp__data.env.length;
+ interpreter_lex = lisp__data.env.lex;
+
+ /* Use the variables */
+ plambda = &lambda;
+ *presult = NIL;
+ *pjumped = 1;
+ *pbackquote = !CONSP(lambda);
+
+ block = LispBeginBlock(NIL, LispBlockProtect);
+ if (setjmp(block->jmp) == 0) {
+ if (!backquote) {
+ for (; CONSP(lambda); lambda = CDR(lambda))
+ result = ComMacroExpandEval(com, CAR(lambda));
+ }
+ else
+ result = ComMacroExpandBackquote(com, lambda);
+
+ *pjumped = 0;
+ }
+ LispEndBlock(block);
+
+ /* If tried to jump out of the macro expansion block */
+ if (!lisp__data.destroyed && jumped)
+ LispDestroy("*** EVAL: bad jump in macro expansion");
+
+ /* Macro expansion did something wrong */
+ if (lisp__data.destroyed) {
+ LispMessage("*** EVAL: aborting macro expansion");
+ LispDestroy(".");
+ }
+
+ /* Restore interpreter state */
+ lisp__data.env.lex = interpreter_lex;
+ lisp__data.stack.length = interpreter_base;
+ lisp__data.env.head = lisp__data.env.length = interpreter_head;
+
+ return (result);
+}
+
+static void
+ComMacroCall(LispCom *com, LispArgList *alist,
+ LispObj *name, LispObj *lambda, LispObj *arguments)
+{
+ int base;
+ LispObj *body;
+
+ ++com->macro;
+ base = ComCall(com, alist, name, arguments, 0, 0, 0);
+ body = lambda->data.lambda.code;
+ body = ComMacroExpand(com, body);
+ --com->macro;
+ lisp__data.env.head = lisp__data.env.length = base;
+
+ /* Macro is expanded, store the result */
+ CAR(com->form) = body;
+ ComEval(com, body);
+}
+
+static void
+ComMacroBackquote(LispCom *com, LispObj *lambda)
+{
+ LispObj *body;
+
+ ++com->macro;
+ body = ComMacroExpand(com, lambda);
+ --com->macro;
+
+ /* Macro is expanded, store the result */
+ CAR(com->form) = body;
+
+ com_LoadCon(com, body);
+}
diff --git a/lisp/core.c b/lisp/core.c
new file mode 100644
index 0000000..d834dd5
--- /dev/null
+++ b/lisp/core.c
@@ -0,0 +1,7040 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/core.c,v 1.69 2002/12/20 04:32:45 paulo Exp $ */
+
+#include "io.h"
+#include "core.h"
+#include "format.h"
+#include "helper.h"
+#include "package.h"
+#include "private.h"
+#include "write.h"
+
+/*
+ * Types
+ */
+typedef struct _SeqInfo {
+ LispType type;
+ union {
+ LispObj *list;
+ LispObj **vector;
+ unsigned char *string;
+ } data;
+} SeqInfo;
+
+#define SETSEQ(seq, object) \
+ switch (seq.type = XOBJECT_TYPE(object)) { \
+ case LispString_t: \
+ seq.data.string = (unsigned char*)THESTR(object); \
+ break; \
+ case LispCons_t: \
+ seq.data.list = object; \
+ break; \
+ default: \
+ seq.data.list = object->data.array.list; \
+ break; \
+ }
+
+#ifdef NEED_SETENV
+extern int setenv(const char *name, const char *value, int overwrite);
+extern void unsetenv(const char *name);
+#endif
+
+/*
+ * Prototypes
+ */
+#define NONE 0
+
+#define REMOVE 1
+#define SUBSTITUTE 2
+#define DELETE 3
+#define NSUBSTITUTE 4
+
+#define ASSOC 1
+#define MEMBER 2
+
+#define FIND 1
+#define POSITION 2
+
+#define IF 1
+#define IFNOT 2
+
+#define UNION 1
+#define INTERSECTION 2
+#define SETDIFFERENCE 3
+#define SETEXCLUSIVEOR 4
+#define SUBSETP 5
+#define NSETDIFFERENCE 6
+#define NINTERSECTION 7
+#define NUNION 8
+#define NSETEXCLUSIVEOR 9
+
+#define COPY_LIST 1
+#define COPY_ALIST 2
+#define COPY_TREE 3
+
+#define EVERY 1
+#define SOME 2
+#define NOTEVERY 3
+#define NOTANY 4
+
+/* Call directly LispObjectCompare() if possible */
+#define FCODE(predicate) \
+ predicate == Oeql ? FEQL : \
+ predicate == Oequal ? FEQUAL : \
+ predicate == Oeq ? FEQ : \
+ predicate == Oequalp ? FEQUALP : 0
+#define FCOMPARE(predicate, left, right, code) \
+ code == FEQ ? left == right : \
+ code ? LispObjectCompare(left, right, code) != NIL : \
+ APPLY2(predicate, left, right) != NIL
+
+#define FUNCTION_CHECK(predicate) \
+ if (FUNCTIONP(predicate)) \
+ predicate = (predicate)->data.atom->object
+
+#define CHECK_TEST_0() \
+ if (test != UNSPEC && test_not != UNSPEC) \
+ LispDestroy("%s: specify either :TEST or :TEST-NOT", \
+ STRFUN(builtin))
+
+#define CHECK_TEST() \
+ CHECK_TEST_0(); \
+ if (test_not == UNSPEC) { \
+ if (test == UNSPEC) \
+ lambda = Oeql; \
+ else \
+ lambda = test; \
+ expect = 1; \
+ } \
+ else { \
+ lambda = test_not; \
+ expect = 0; \
+ } \
+ FUNCTION_CHECK(lambda); \
+ code = FCODE(lambda)
+
+
+static LispObj *LispAdjoin(LispBuiltin*,
+ LispObj*, LispObj*, LispObj*, LispObj*, LispObj*);
+static LispObj *LispAssocOrMember(LispBuiltin*, int, int);
+static LispObj *LispEverySomeAnyNot(LispBuiltin*, int);
+static LispObj *LispFindOrPosition(LispBuiltin*, int, int);
+static LispObj *LispDeleteOrRemoveDuplicates(LispBuiltin*, int);
+static LispObj *LispDeleteRemoveXSubstitute(LispBuiltin*, int, int);
+static LispObj *LispListSet(LispBuiltin*, int);
+static LispObj *LispMapc(LispBuiltin*, int);
+static LispObj *LispMapl(LispBuiltin*, int);
+static LispObj *LispMapnconc(LispObj*);
+extern LispObj *LispRunSetf(LispArgList*, LispObj*, LispObj*, LispObj*);
+extern LispObj *LispRunSetfMacro(LispAtom*, LispObj*, LispObj*);
+static LispObj *LispMergeSort(LispObj*, LispObj*, LispObj*, int);
+static LispObj *LispXReverse(LispBuiltin*, int);
+static LispObj *LispCopyList(LispBuiltin*, LispObj*, int);
+static LispObj *LispValuesList(LispBuiltin*, int);
+static LispObj *LispTreeEqual(LispObj*, LispObj*, LispObj*, int);
+static LispDocType_t LispDocumentationType(LispBuiltin*, LispObj*);
+
+extern void LispSetAtomObjectProperty(LispAtom*, LispObj*);
+
+/*
+ * Initialization
+ */
+LispObj *Oeq, *Oeql, *Oequal, *Oequalp, *Omake_array,
+ *Kinitial_contents, *Osetf, *Ootherwise, *Oquote;
+LispObj *Ogensym_counter;
+
+Atom_id Svariable, Sstructure, Stype, Ssetf;
+
+/*
+ * Implementation
+ */
+void
+LispCoreInit(void)
+{
+ Oeq = STATIC_ATOM("EQ");
+ Oeql = STATIC_ATOM("EQL");
+ Oequal = STATIC_ATOM("EQUAL");
+ Oequalp = STATIC_ATOM("EQUALP");
+ Omake_array = STATIC_ATOM("MAKE-ARRAY");
+ Kinitial_contents = KEYWORD("INITIAL-CONTENTS");
+ Osetf = STATIC_ATOM("SETF");
+ Ootherwise = STATIC_ATOM("OTHERWISE");
+ LispExportSymbol(Ootherwise);
+ Oquote = STATIC_ATOM("QUOTE");
+ LispExportSymbol(Oquote);
+
+ Svariable = GETATOMID("VARIABLE");
+ Sstructure = GETATOMID("STRUCTURE");
+ Stype = GETATOMID("TYPE");
+
+ /* Create as a constant so that only the C code should change the value */
+ Ogensym_counter = STATIC_ATOM("*GENSYM-COUNTER*");
+ LispDefconstant(Ogensym_counter, FIXNUM(0), NIL);
+ LispExportSymbol(Ogensym_counter);
+
+ Ssetf = ATOMID(Osetf);
+}
+
+LispObj *
+Lisp_Acons(LispBuiltin *builtin)
+/*
+ acons key datum alist
+ */
+{
+ LispObj *key, *datum, *alist;
+
+ alist = ARGUMENT(2);
+ datum = ARGUMENT(1);
+ key = ARGUMENT(0);
+
+ return (CONS(CONS(key, datum), alist));
+}
+
+static LispObj *
+LispAdjoin(LispBuiltin*builtin, LispObj *item, LispObj *list,
+ LispObj *key, LispObj *test, LispObj *test_not)
+{
+ GC_ENTER();
+ int code, expect, value;
+ LispObj *lambda, *compare, *object;
+
+ CHECK_LIST(list);
+ CHECK_TEST();
+
+ if (key != UNSPEC) {
+ item = APPLY1(key, item);
+ /* Result is not guaranteed to be gc protected */
+ GC_PROTECT(item);
+ }
+
+ /* Check if item is not already in place */
+ for (object = list; CONSP(object); object = CDR(object)) {
+ compare = CAR(object);
+ if (key != UNSPEC) {
+ compare = APPLY1(key, compare);
+ GC_PROTECT(compare);
+ value = FCOMPARE(lambda, item, compare, code);
+ /* Unprotect compare... */
+ --lisp__data.protect.length;
+ }
+ else
+ value = FCOMPARE(lambda, item, compare, code);
+
+ if (value == expect) {
+ /* Item is already in list */
+ GC_LEAVE();
+
+ return (list);
+ }
+ }
+ GC_LEAVE();
+
+ return (CONS(item, list));
+}
+
+LispObj *
+Lisp_Adjoin(LispBuiltin *builtin)
+/*
+ adjoin item list &key key test test-not
+ */
+{
+ LispObj *item, *list, *key, *test, *test_not;
+
+ test_not = ARGUMENT(4);
+ test = ARGUMENT(3);
+ key = ARGUMENT(2);
+ list = ARGUMENT(1);
+ item = ARGUMENT(0);
+
+ return (LispAdjoin(builtin, item, list, key, test, test_not));
+}
+
+LispObj *
+Lisp_Append(LispBuiltin *builtin)
+/*
+ append &rest lists
+ */
+{
+ GC_ENTER();
+ LispObj *result, *cons, *list;
+
+ LispObj *lists;
+
+ lists = ARGUMENT(0);
+
+ /* no arguments */
+ if (!CONSP(lists))
+ return (NIL);
+
+ /* skip initial nil lists */
+ for (; CONSP(CDR(lists)) && CAR(lists) == NIL; lists = CDR(lists))
+ ;
+
+ /* last argument is not copied (even if it is the single argument) */
+ if (!CONSP(CDR(lists)))
+ return (CAR(lists));
+
+ /* make sure result is a list */
+ list = CAR(lists);
+ CHECK_CONS(list);
+ result = cons = CONS(CAR(list), NIL);
+ GC_PROTECT(result);
+ for (list = CDR(list); CONSP(list); list = CDR(list)) {
+ RPLACD(cons, CONS(CAR(list), NIL));
+ cons = CDR(cons);
+ }
+ lists = CDR(lists);
+
+ /* copy intermediate lists */
+ for (; CONSP(CDR(lists)); lists = CDR(lists)) {
+ list = CAR(lists);
+ if (list == NIL)
+ continue;
+ /* intermediate elements must be lists */
+ CHECK_CONS(list);
+ for (; CONSP(list); list = CDR(list)) {
+ RPLACD(cons, CONS(CAR(list), NIL));
+ cons = CDR(cons);
+ }
+ }
+
+ /* add last element */
+ RPLACD(cons, CAR(lists));
+
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Aref(LispBuiltin *builtin)
+/*
+ aref array &rest subscripts
+ */
+{
+ long c, count, idx, seq;
+ LispObj *obj, *dim;
+
+ LispObj *array, *subscripts;
+
+ subscripts = ARGUMENT(1);
+ array = ARGUMENT(0);
+
+ /* accept strings also */
+ if (STRINGP(array) && CONSP(subscripts) && CDR(subscripts) == NIL) {
+ long offset, length = STRLEN(array);
+
+ CHECK_INDEX(CAR(subscripts));
+ offset = FIXNUM_VALUE(CAR(subscripts));
+
+ if (offset >= length)
+ LispDestroy("%s: index %ld too large for sequence length %ld",
+ STRFUN(builtin), offset, length);
+
+ return (SCHAR(THESTR(array)[offset]));
+ }
+
+ CHECK_ARRAY(array);
+
+ for (count = 0, dim = subscripts, obj = array->data.array.dim; CONSP(dim);
+ count++, dim = CDR(dim), obj = CDR(obj)) {
+ if (count >= array->data.array.rank)
+ LispDestroy("%s: too many subscripts %s",
+ STRFUN(builtin), STROBJ(subscripts));
+ if (!INDEXP(CAR(dim)) ||
+ FIXNUM_VALUE(CAR(dim)) >= FIXNUM_VALUE(CAR(obj)))
+ LispDestroy("%s: %s is out of range or a bad index",
+ STRFUN(builtin), STROBJ(CAR(dim)));
+ }
+ if (count < array->data.array.rank)
+ LispDestroy("%s: too few subscripts %s",
+ STRFUN(builtin), STROBJ(subscripts));
+
+ for (count = seq = 0, dim = subscripts; CONSP(dim); dim = CDR(dim), seq++) {
+ for (idx = 0, obj = array->data.array.dim; idx < seq;
+ obj = CDR(obj), ++idx)
+ ;
+ for (c = 1, obj = CDR(obj); obj != NIL; obj = CDR(obj))
+ c *= FIXNUM_VALUE(CAR(obj));
+ count += c * FIXNUM_VALUE(CAR(dim));
+ }
+
+ for (array = array->data.array.list; count > 0; array = CDR(array), count--)
+ ;
+
+ return (CAR(array));
+}
+
+static LispObj *
+LispAssocOrMember(LispBuiltin *builtin, int function, int comparison)
+/*
+ assoc item list &key test test-not key
+ assoc-if predicate list &key key
+ assoc-if-not predicate list &key key
+ member item list &key test test-not key
+ member-if predicate list &key key
+ member-if-not predicate list &key key
+ */
+{
+ int code = 0, expect, value;
+ LispObj *lambda, *result, *compare;
+
+ LispObj *item, *list, *test, *test_not, *key;
+
+ if (comparison == NONE) {
+ key = ARGUMENT(4);
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ list = ARGUMENT(1);
+ item = ARGUMENT(0);
+ lambda = NIL;
+ }
+ else {
+ key = ARGUMENT(2);
+ list = ARGUMENT(1);
+ lambda = ARGUMENT(0);
+ test = test_not = UNSPEC;
+ item = NIL;
+ }
+
+ if (list == NIL)
+ return (NIL);
+ CHECK_CONS(list);
+
+ /* Resolve compare function, and expected result of comparison */
+ if (comparison == NONE) {
+ CHECK_TEST();
+ }
+ else
+ expect = comparison == IFNOT ? 0 : 1;
+
+ result = NIL;
+ for (; CONSP(list); list = CDR(list)) {
+ compare = CAR(list);
+ if (function == ASSOC) {
+ if (!CONSP(compare))
+ continue;
+ compare = CAR(compare);
+ }
+ if (key != UNSPEC)
+ compare = APPLY1(key, compare);
+
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ if (value == expect) {
+ result = list;
+ if (function == ASSOC)
+ result = CAR(result);
+ break;
+ }
+ }
+ if (function == MEMBER) {
+ CHECK_LIST(list);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Assoc(LispBuiltin *builtin)
+/*
+ assoc item list &key test test-not key
+ */
+{
+ return (LispAssocOrMember(builtin, ASSOC, NONE));
+}
+
+LispObj *
+Lisp_AssocIf(LispBuiltin *builtin)
+/*
+ assoc-if predicate list &key key
+ */
+{
+ return (LispAssocOrMember(builtin, ASSOC, IF));
+}
+
+LispObj *
+Lisp_AssocIfNot(LispBuiltin *builtin)
+/*
+ assoc-if-not predicate list &key key
+ */
+{
+ return (LispAssocOrMember(builtin, ASSOC, IFNOT));
+}
+
+LispObj *
+Lisp_And(LispBuiltin *builtin)
+/*
+ and &rest args
+ */
+{
+ LispObj *result = T, *args;
+
+ args = ARGUMENT(0);
+
+ for (; CONSP(args); args = CDR(args)) {
+ result = EVAL(CAR(args));
+ if (result == NIL)
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Apply(LispBuiltin *builtin)
+/*
+ apply function arg &rest more-args
+ */
+{
+ GC_ENTER();
+ LispObj *result, *arguments;
+
+ LispObj *function, *arg, *more_args;
+
+ more_args = ARGUMENT(2);
+ arg = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ if (more_args == NIL) {
+ CHECK_LIST(arg);
+ arguments = arg;
+ for (; CONSP(arg); arg = CDR(arg))
+ ;
+ CHECK_LIST(arg);
+ }
+ else {
+ LispObj *cons;
+
+ CHECK_CONS(more_args);
+ arguments = cons = CONS(arg, NIL);
+ GC_PROTECT(arguments);
+ for (arg = CDR(more_args);
+ CONSP(arg);
+ more_args = arg, arg = CDR(arg)) {
+ RPLACD(cons, CONS(CAR(more_args), NIL));
+ cons = CDR(cons);
+ }
+ more_args = CAR(more_args);
+ if (more_args != NIL) {
+ for (arg = more_args; CONSP(arg); arg = CDR(arg))
+ ;
+ CHECK_LIST(arg);
+ RPLACD(cons, more_args);
+ }
+ }
+
+ result = APPLY(function, arguments);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Atom(LispBuiltin *builtin)
+/*
+ atom object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (CONSP(object) ? NIL : T);
+}
+
+LispObj *
+Lisp_Block(LispBuiltin *builtin)
+/*
+ block name &rest body
+ */
+{
+ int did_jump, *pdid_jump = &did_jump;
+ LispObj *res, **pres = &res, **pbody;
+ LispBlock *block;
+
+ LispObj *name, *body;
+
+ body = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ if (!SYMBOLP(name) && name != NIL && name != T)
+ LispDestroy("%s: %s cannot name a block",
+ STRFUN(builtin), STROBJ(name));
+
+ pbody = &body;
+ *pres = NIL;
+ *pdid_jump = 1;
+ block = LispBeginBlock(name, LispBlockTag);
+ if (setjmp(block->jmp) == 0) {
+ for (; CONSP(body); body = CDR(body))
+ res = EVAL(CAR(body));
+ *pdid_jump = 0;
+ }
+ LispEndBlock(block);
+ if (*pdid_jump)
+ *pres = lisp__data.block.block_ret;
+
+ return (res);
+}
+
+LispObj *
+Lisp_Boundp(LispBuiltin *builtin)
+/*
+ boundp symbol
+ */
+{
+ LispAtom *atom;
+
+ LispObj *symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ atom = symbol->data.atom;
+ if (atom->package == lisp__data.keyword ||
+ (atom->a_object && atom->property->value != UNBOUND))
+ return (T);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Butlast(LispBuiltin *builtin)
+/*
+ butlast list &optional count
+ */
+{
+ GC_ENTER();
+ long length, count;
+ LispObj *result, *cons, *list, *ocount;
+
+ ocount = ARGUMENT(1);
+ list = ARGUMENT(0);
+
+ CHECK_LIST(list);
+ if (ocount == UNSPEC)
+ count = 1;
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+ length = LispLength(list);
+
+ if (count == 0)
+ return (list);
+ else if (count >= length)
+ return (NIL);
+
+ length -= count + 1;
+ result = cons = CONS(CAR(list), NIL);
+ GC_PROTECT(result);
+ for (list = CDR(list); length > 0; list = CDR(list), length--) {
+ RPLACD(cons, CONS(CAR(list), NIL));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Nbutlast(LispBuiltin *builtin)
+/*
+ nbutlast list &optional count
+ */
+{
+ long length, count;
+ LispObj *result, *list, *ocount;
+
+ ocount = ARGUMENT(1);
+ list = ARGUMENT(0);
+
+ CHECK_LIST(list);
+ if (ocount == UNSPEC)
+ count = 1;
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+ length = LispLength(list);
+
+ if (count == 0)
+ return (list);
+ else if (count >= length)
+ return (NIL);
+
+ length -= count + 1;
+ result = list;
+ for (; length > 0; list = CDR(list), length--)
+ ;
+ RPLACD(list, NIL);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Car(LispBuiltin *builtin)
+/*
+ car list
+ */
+{
+ LispObj *list, *result = NULL;
+
+ list = ARGUMENT(0);
+
+ if (list == NIL)
+ result = NIL;
+ else {
+ CHECK_CONS(list);
+ result = CAR(list);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Case(LispBuiltin *builtin)
+/*
+ case keyform &rest body
+ */
+{
+ LispObj *result, *code, *keyform, *body, *form;
+
+ body = ARGUMENT(1);
+ keyform = ARGUMENT(0);
+
+ result = NIL;
+ keyform = EVAL(keyform);
+
+ for (; CONSP(body); body = CDR(body)) {
+ code = CAR(body);
+ CHECK_CONS(code);
+
+ form = CAR(code);
+ if (form == T || form == Ootherwise) {
+ if (CONSP(CDR(body)))
+ LispDestroy("%s: %s must be the last clause",
+ STRFUN(builtin), STROBJ(CAR(code)));
+ result = CDR(code);
+ break;
+ }
+ else if (CONSP(form)) {
+ for (; CONSP(form); form = CDR(form))
+ if (XEQL(keyform, CAR(form)) == T) {
+ result = CDR(code);
+ break;
+ }
+ if (CONSP(form)) /* if found match */
+ break;
+ }
+ else if (XEQL(keyform, form) == T) {
+ result = CDR(code);
+ break;
+ }
+ }
+
+ for (body = result; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ return (result);
+}
+
+LispObj *
+Lisp_Catch(LispBuiltin *builtin)
+/*
+ catch tag &rest body
+ */
+{
+ int did_jump, *pdid_jump = &did_jump;
+ LispObj *res, **pres = &res;
+ LispBlock *block;
+
+ LispObj *tag, *body, **pbody;
+
+ body = ARGUMENT(1);
+ tag = ARGUMENT(0);
+
+ pbody = &body;
+ *pres = NIL;
+ *pdid_jump = 1;
+ block = LispBeginBlock(tag, LispBlockCatch);
+ if (setjmp(block->jmp) == 0) {
+ for (; CONSP(body); body = CDR(body))
+ res = EVAL(CAR(body));
+ *pdid_jump = 0;
+ }
+ LispEndBlock(block);
+ if (*pdid_jump)
+ *pres = lisp__data.block.block_ret;
+
+ return (res);
+}
+
+LispObj *
+Lisp_Coerce(LispBuiltin *builtin)
+/*
+ coerce object result-type
+ */
+{
+ LispObj *object, *result_type;
+
+ result_type = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ return (LispCoerce(builtin, object, result_type));
+}
+
+LispObj *
+Lisp_Cdr(LispBuiltin *builtin)
+/*
+ cdr list
+ */
+{
+ LispObj *list, *result = NULL;
+
+ list = ARGUMENT(0);
+
+ if (list == NIL)
+ result = NIL;
+ else {
+ CHECK_CONS(list);
+ result = CDR(list);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_C_r(LispBuiltin *builtin)
+/*
+ c[ad]{2,4}r list
+ */
+{
+ char *desc;
+
+ LispObj *list, *result = NULL;
+
+ list = ARGUMENT(0);
+
+ result = list;
+ desc = STRFUN(builtin);
+ while (desc[1] != 'R')
+ ++desc;
+ while (*desc != 'C') {
+ if (result == NIL)
+ break;
+ CHECK_CONS(result);
+ result = *desc == 'A' ? CAR(result) : CDR(result);
+ --desc;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Cond(LispBuiltin *builtin)
+/*
+ cond &rest body
+ */
+{
+ LispObj *result, *code, *body;
+
+ body = ARGUMENT(0);
+
+ result = NIL;
+ for (; CONSP(body); body = CDR(body)) {
+ code = CAR(body);
+
+ CHECK_CONS(code);
+ result = EVAL(CAR(code));
+ if (result == NIL)
+ continue;
+ for (code = CDR(code); CONSP(code); code = CDR(code))
+ result = EVAL(CAR(code));
+ break;
+ }
+
+ return (result);
+}
+
+static LispObj *
+LispCopyList(LispBuiltin *builtin, LispObj *list, int function)
+{
+ GC_ENTER();
+ LispObj *result, *cons;
+
+ if (list == NIL)
+ return (list);
+ CHECK_CONS(list);
+
+ result = cons = CONS(NIL, NIL);
+ GC_PROTECT(result);
+ if (CONSP(CAR(list))) {
+ switch (function) {
+ case COPY_LIST:
+ RPLACA(result, CAR(list));
+ break;
+ case COPY_ALIST:
+ RPLACA(result, CONS(CAR(CAR(list)), CDR(CAR(list))));
+ break;
+ case COPY_TREE:
+ RPLACA(result, LispCopyList(builtin, CAR(list), COPY_TREE));
+ break;
+ }
+ }
+ else
+ RPLACA(result, CAR(list));
+
+ for (list = CDR(list); CONSP(list); list = CDR(list)) {
+ CDR(cons) = CONS(NIL, NIL);
+ cons = CDR(cons);
+ if (CONSP(CAR(list))) {
+ switch (function) {
+ case COPY_LIST:
+ RPLACA(cons, CAR(list));
+ break;
+ case COPY_ALIST:
+ RPLACA(cons, CONS(CAR(CAR(list)), CDR(CAR(list))));
+ break;
+ case COPY_TREE:
+ RPLACA(cons, LispCopyList(builtin, CAR(list), COPY_TREE));
+ break;
+ }
+ }
+ else
+ RPLACA(cons, CAR(list));
+ }
+ /* in case list is dotted */
+ RPLACD(cons, list);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_CopyAlist(LispBuiltin *builtin)
+/*
+ copy-alist list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ return (LispCopyList(builtin, list, COPY_ALIST));
+}
+
+LispObj *
+Lisp_CopyList(LispBuiltin *builtin)
+/*
+ copy-list list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ return (LispCopyList(builtin, list, COPY_LIST));
+}
+
+LispObj *
+Lisp_CopyTree(LispBuiltin *builtin)
+/*
+ copy-tree list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ return (LispCopyList(builtin, list, COPY_TREE));
+}
+
+LispObj *
+Lisp_Cons(LispBuiltin *builtin)
+/*
+ cons car cdr
+ */
+{
+ LispObj *car, *cdr;
+
+ cdr = ARGUMENT(1);
+ car = ARGUMENT(0);
+
+ return (CONS(car, cdr));
+}
+
+LispObj *
+Lisp_Consp(LispBuiltin *builtin)
+/*
+ consp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (CONSP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Constantp(LispBuiltin *builtin)
+/*
+ constantp form &optional environment
+ */
+{
+ LispObj *form, *environment;
+
+ environment = ARGUMENT(1);
+ form = ARGUMENT(0);
+
+ /* not all self-evaluating objects are considered constants */
+ if (!POINTERP(form) ||
+ NUMBERP(form) ||
+ XQUOTEP(form) ||
+ (XCONSP(form) && CAR(form) == Oquote) ||
+ (XSYMBOLP(form) && form->data.atom->constant) ||
+ XSTRINGP(form) ||
+ XARRAYP(form))
+ return (T);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Defconstant(LispBuiltin *builtin)
+/*
+ defconstant name initial-value &optional documentation
+ */
+{
+ LispObj *name, *initial_value, *documentation;
+
+ documentation = ARGUMENT(2);
+ initial_value = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ if (documentation != UNSPEC) {
+ CHECK_STRING(documentation);
+ }
+ else
+ documentation = NIL;
+ LispDefconstant(name, EVAL(initial_value), documentation);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Defmacro(LispBuiltin *builtin)
+/*
+ defmacro name lambda-list &rest body
+ */
+{
+ LispArgList *alist;
+
+ LispObj *lambda, *name, *lambda_list, *body;
+
+ body = ARGUMENT(2);
+ lambda_list = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ alist = LispCheckArguments(LispMacro, lambda_list, ATOMID(name), 0);
+
+ if (CONSP(body) && STRINGP(CAR(body))) {
+ LispAddDocumentation(name, CAR(body), LispDocFunction);
+ body = CDR(body);
+ }
+
+ lambda_list = LispListProtectedArguments(alist);
+ lambda = LispNewLambda(name, body, lambda_list, LispMacro);
+
+ if (name->data.atom->a_builtin || name->data.atom->a_compiled) {
+ if (name->data.atom->a_builtin) {
+ ERROR_CHECK_SPECIAL_FORM(name->data.atom);
+ }
+ /* redefining these may cause surprises if bytecode
+ * compiled functions references them */
+ LispWarning("%s: %s is being redefined", STRFUN(builtin), ATOMID(name));
+
+ LispRemAtomBuiltinProperty(name->data.atom);
+ }
+
+ LispSetAtomFunctionProperty(name->data.atom, lambda, alist);
+ LispUseArgList(alist);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Defun(LispBuiltin *builtin)
+/*
+ defun name lambda-list &rest body
+ */
+{
+ LispArgList *alist;
+
+ LispObj *lambda, *name, *lambda_list, *body;
+
+ body = ARGUMENT(2);
+ lambda_list = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ alist = LispCheckArguments(LispFunction, lambda_list, ATOMID(name), 0);
+
+ if (CONSP(body) && STRINGP(CAR(body))) {
+ LispAddDocumentation(name, CAR(body), LispDocFunction);
+ body = CDR(body);
+ }
+
+ lambda_list = LispListProtectedArguments(alist);
+ lambda = LispNewLambda(name, body, lambda_list, LispFunction);
+
+ if (name->data.atom->a_builtin || name->data.atom->a_compiled) {
+ if (name->data.atom->a_builtin) {
+ ERROR_CHECK_SPECIAL_FORM(name->data.atom);
+ }
+ /* redefining these may cause surprises if bytecode
+ * compiled functions references them */
+ LispWarning("%s: %s is being redefined", STRFUN(builtin), ATOMID(name));
+
+ LispRemAtomBuiltinProperty(name->data.atom);
+ }
+ LispSetAtomFunctionProperty(name->data.atom, lambda, alist);
+ LispUseArgList(alist);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Defsetf(LispBuiltin *builtin)
+/*
+ defsetf function lambda-list &rest body
+ */
+{
+ LispArgList *alist;
+ LispObj *obj;
+ LispObj *lambda, *function, *lambda_list, *store, *body;
+
+ body = ARGUMENT(2);
+ lambda_list = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ CHECK_SYMBOL(function);
+
+ if (body == NIL || (CONSP(body) && STRINGP(CAR(body)))) {
+ if (!SYMBOLP(lambda_list))
+ LispDestroy("%s: syntax error %s %s",
+ STRFUN(builtin), STROBJ(function), STROBJ(lambda_list));
+ if (body != NIL)
+ LispAddDocumentation(function, CAR(body), LispDocSetf);
+
+ LispSetAtomSetfProperty(function->data.atom, lambda_list, NULL);
+
+ return (function);
+ }
+
+ alist = LispCheckArguments(LispSetf, lambda_list, ATOMID(function), 0);
+
+ store = CAR(body);
+ if (!CONSP(store))
+ LispDestroy("%s: %s is a bad store value",
+ STRFUN(builtin), STROBJ(store));
+ for (obj = store; CONSP(obj); obj = CDR(obj)) {
+ CHECK_SYMBOL(CAR(obj));
+ }
+
+ body = CDR(body);
+ if (CONSP(body) && STRINGP(CAR(body))) {
+ LispAddDocumentation(function, CAR(body), LispDocSetf);
+ body = CDR(body);
+ }
+
+ lambda = LispNewLambda(function, body, store, LispSetf);
+ LispSetAtomSetfProperty(function->data.atom, lambda, alist);
+ LispUseArgList(alist);
+
+ return (function);
+}
+
+LispObj *
+Lisp_Defparameter(LispBuiltin *builtin)
+/*
+ defparameter name initial-value &optional documentation
+ */
+{
+ LispObj *name, *initial_value, *documentation;
+
+ documentation = ARGUMENT(2);
+ initial_value = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ if (documentation != UNSPEC) {
+ CHECK_STRING(documentation);
+ }
+ else
+ documentation = NIL;
+
+ LispProclaimSpecial(name, EVAL(initial_value), documentation);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Defvar(LispBuiltin *builtin)
+/*
+ defvar name &optional initial-value documentation
+ */
+{
+ LispObj *name, *initial_value, *documentation;
+
+ documentation = ARGUMENT(2);
+ initial_value = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_SYMBOL(name);
+ if (documentation != UNSPEC) {
+ CHECK_STRING(documentation);
+ }
+ else
+ documentation = NIL;
+
+ LispProclaimSpecial(name,
+ initial_value != UNSPEC ? EVAL(initial_value) : NULL,
+ documentation);
+
+ return (name);
+}
+
+LispObj *
+Lisp_Delete(LispBuiltin *builtin)
+/*
+ delete item sequence &key from-end test test-not start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, DELETE, NONE));
+}
+
+LispObj *
+Lisp_DeleteIf(LispBuiltin *builtin)
+/*
+ delete-if predicate sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, DELETE, IF));
+}
+
+LispObj *
+Lisp_DeleteIfNot(LispBuiltin *builtin)
+/*
+ delete-if-not predicate sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, DELETE, IFNOT));
+}
+
+LispObj *
+Lisp_DeleteDuplicates(LispBuiltin *builtin)
+/*
+ delete-duplicates sequence &key from-end test test-not start end key
+ */
+{
+ return (LispDeleteOrRemoveDuplicates(builtin, DELETE));
+}
+
+LispObj *
+Lisp_Do(LispBuiltin *builtin)
+/*
+ do init test &rest body
+ */
+{
+ return (LispDo(builtin, 0));
+}
+
+LispObj *
+Lisp_DoP(LispBuiltin *builtin)
+/*
+ do* init test &rest body
+ */
+{
+ return (LispDo(builtin, 1));
+}
+
+static LispDocType_t
+LispDocumentationType(LispBuiltin *builtin, LispObj *type)
+{
+ Atom_id atom;
+ LispDocType_t doc_type = LispDocVariable;
+
+ CHECK_SYMBOL(type);
+ atom = ATOMID(type);
+
+ if (atom == Svariable)
+ doc_type = LispDocVariable;
+ else if (atom == Sfunction)
+ doc_type = LispDocFunction;
+ else if (atom == Sstructure)
+ doc_type = LispDocStructure;
+ else if (atom == Stype)
+ doc_type = LispDocType;
+ else if (atom == Ssetf)
+ doc_type = LispDocSetf;
+ else {
+ LispDestroy("%s: unknown documentation type %s",
+ STRFUN(builtin), STROBJ(type));
+ /*NOTREACHED*/
+ }
+
+ return (doc_type);
+}
+
+LispObj *
+Lisp_Documentation(LispBuiltin *builtin)
+/*
+ documentation symbol type
+ */
+{
+ LispObj *symbol, *type;
+
+ type = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ /* type is checked in LispDocumentationType() */
+
+ return (LispGetDocumentation(symbol, LispDocumentationType(builtin, type)));
+}
+
+LispObj *
+Lisp_DoList(LispBuiltin *builtin)
+{
+ return (LispDoListTimes(builtin, 0));
+}
+
+LispObj *
+Lisp_DoTimes(LispBuiltin *builtin)
+{
+ return (LispDoListTimes(builtin, 1));
+}
+
+LispObj *
+Lisp_Elt(LispBuiltin *builtin)
+/*
+ elt sequence index
+ svref sequence index
+ */
+{
+ long offset, length;
+ LispObj *result, *sequence, *oindex;
+
+ oindex = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ length = LispLength(sequence);
+
+ CHECK_INDEX(oindex);
+ offset = FIXNUM_VALUE(oindex);
+
+ if (offset >= length)
+ LispDestroy("%s: index %ld too large for sequence length %ld",
+ STRFUN(builtin), offset, length);
+
+ if (STRINGP(sequence))
+ result = SCHAR(THESTR(sequence)[offset]);
+ else {
+ if (ARRAYP(sequence))
+ sequence = sequence->data.array.list;
+
+ for (; offset > 0; offset--, sequence = CDR(sequence))
+ ;
+ result = CAR(sequence);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Endp(LispBuiltin *builtin)
+/*
+ endp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ if (object == NIL)
+ return (T);
+ CHECK_CONS(object);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Eq(LispBuiltin *builtin)
+/*
+ eq left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ return (XEQ(left, right));
+}
+
+LispObj *
+Lisp_Eql(LispBuiltin *builtin)
+/*
+ eql left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ return (XEQL(left, right));
+}
+
+LispObj *
+Lisp_Equal(LispBuiltin *builtin)
+/*
+ equal left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ return (XEQUAL(left, right));
+}
+
+LispObj *
+Lisp_Equalp(LispBuiltin *builtin)
+/*
+ equalp left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ return (XEQUALP(left, right));
+}
+
+LispObj *
+Lisp_Error(LispBuiltin *builtin)
+/*
+ error control-string &rest arguments
+ */
+{
+ LispObj *string, *arglist;
+
+ LispObj *control_string, *arguments;
+
+ arguments = ARGUMENT(1);
+ control_string = ARGUMENT(0);
+
+ arglist = CONS(NIL, CONS(control_string, arguments));
+ GC_PROTECT(arglist);
+ string = APPLY(Oformat, arglist);
+ LispDestroy("%s", THESTR(string));
+ /*NOTREACHED*/
+
+ /* No need to call GC_ENTER() and GC_LEAVE() macros */
+ return (NIL);
+}
+
+LispObj *
+Lisp_Eval(LispBuiltin *builtin)
+/*
+ eval form
+ */
+{
+ int lex;
+ LispObj *form, *result;
+
+ form = ARGUMENT(0);
+
+ /* make sure eval form will not access local variables */
+ lex = lisp__data.env.lex;
+ lisp__data.env.lex = lisp__data.env.length;
+ result = EVAL(form);
+ lisp__data.env.lex = lex;
+
+ return (result);
+}
+
+static LispObj *
+LispEverySomeAnyNot(LispBuiltin *builtin, int function)
+/*
+ every predicate sequence &rest more-sequences
+ some predicate sequence &rest more-sequences
+ notevery predicate sequence &rest more-sequences
+ notany predicate sequence &rest more-sequences
+ */
+{
+ GC_ENTER();
+ long i, j, length, count;
+ LispObj *result, *list, *item, *arguments, *acons, *value;
+ SeqInfo stk[8], *seqs;
+
+ LispObj *predicate, *sequence, *more_sequences;
+
+ more_sequences = ARGUMENT(2);
+ sequence = ARGUMENT(1);
+ predicate = ARGUMENT(0);
+
+ count = 1;
+ length = LispLength(sequence);
+ for (list = more_sequences; CONSP(list); list = CDR(list), count++) {
+ i = LispLength(CAR(list));
+ if (i < length)
+ length = i;
+ }
+
+ result = function == EVERY || function == NOTANY ? T : NIL;
+
+ /* if at least one sequence has length zero */
+ if (length == 0)
+ return (result);
+
+ if (count > sizeof(stk) / sizeof(stk[0]))
+ seqs = LispMalloc(count * sizeof(SeqInfo));
+ else
+ seqs = &stk[0];
+
+ /* build information about sequences */
+ SETSEQ(seqs[0], sequence);
+ for (i = 1, list = more_sequences; CONSP(list); list = CDR(list), i++) {
+ item = CAR(list);
+ SETSEQ(seqs[i], item);
+ }
+
+ /* prepare argument list */
+ arguments = acons = CONS(NIL, NIL);
+ GC_PROTECT(arguments);
+ for (i = 1; i < count; i++) {
+ RPLACD(acons, CONS(NIL, NIL));
+ acons = CDR(acons);
+ }
+
+ /* loop applying predicate in sequence elements */
+ for (i = 0; i < length; i++) {
+
+ /* build argument list */
+ for (acons = arguments, j = 0; j < count; acons = CDR(acons), j++) {
+ if (seqs[j].type == LispString_t)
+ item = SCHAR(*seqs[j].data.string++);
+ else {
+ item = CAR(seqs[j].data.list);
+ seqs[j].data.list = CDR(seqs[j].data.list);
+ }
+ RPLACA(acons, item);
+ }
+
+ /* apply predicate */
+ value = APPLY(predicate, arguments);
+
+ /* check if needs to terminate loop */
+ if (value == NIL) {
+ if (function == EVERY) {
+ result = NIL;
+ break;
+ }
+ if (function == NOTEVERY) {
+ result = T;
+ break;
+ }
+ }
+ else {
+ if (function == SOME) {
+ result = value;
+ break;
+ }
+ if (function == NOTANY) {
+ result = NIL;
+ break;
+ }
+ }
+ }
+
+ GC_LEAVE();
+ if (seqs != &stk[0])
+ LispFree(seqs);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Every(LispBuiltin *builtin)
+/*
+ every predicate sequence &rest more-sequences
+ */
+{
+ return (LispEverySomeAnyNot(builtin, EVERY));
+}
+
+LispObj *
+Lisp_Some(LispBuiltin *builtin)
+/*
+ some predicate sequence &rest more-sequences
+ */
+{
+ return (LispEverySomeAnyNot(builtin, SOME));
+}
+
+LispObj *
+Lisp_Notevery(LispBuiltin *builtin)
+/*
+ notevery predicate sequence &rest more-sequences
+ */
+{
+ return (LispEverySomeAnyNot(builtin, NOTEVERY));
+}
+
+LispObj *
+Lisp_Notany(LispBuiltin *builtin)
+/*
+ notany predicate sequence &rest more-sequences
+ */
+{
+ return (LispEverySomeAnyNot(builtin, NOTANY));
+}
+
+LispObj *
+Lisp_Fboundp(LispBuiltin *builtin)
+/*
+ fboundp symbol
+ */
+{
+ LispAtom *atom;
+
+ LispObj *symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ atom = symbol->data.atom;
+ if (atom->a_function || atom->a_builtin || atom->a_compiled)
+ return (T);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Find(LispBuiltin *builtin)
+/*
+ find item sequence &key from-end test test-not start end key
+ */
+{
+ return (LispFindOrPosition(builtin, FIND, NONE));
+}
+
+LispObj *
+Lisp_FindIf(LispBuiltin *builtin)
+/*
+ find-if predicate sequence &key from-end start end key
+ */
+{
+ return (LispFindOrPosition(builtin, FIND, IF));
+}
+
+LispObj *
+Lisp_FindIfNot(LispBuiltin *builtin)
+/*
+ find-if-not predicate sequence &key from-end start end key
+ */
+{
+ return (LispFindOrPosition(builtin, FIND, IFNOT));
+}
+
+LispObj *
+Lisp_Fill(LispBuiltin *builtin)
+/*
+ fill sequence item &key start end
+ */
+{
+ long i, start, end, length;
+
+ LispObj *sequence, *item, *ostart, *oend;
+
+ oend = ARGUMENT(3);
+ ostart = ARGUMENT(2);
+ item = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ if (STRINGP(sequence)) {
+ int ch;
+ char *string = THESTR(sequence);
+
+ CHECK_STRING_WRITABLE(sequence);
+ CHECK_SCHAR(item);
+ ch = SCHAR_VALUE(item);
+ for (i = start; i < end; i++)
+ string[i] = ch;
+ }
+ else {
+ LispObj *list;
+
+ if (CONSP(sequence))
+ list = sequence;
+ else
+ list = sequence->data.array.list;
+
+ for (i = 0; i < start; i++, list = CDR(list))
+ ;
+ for (; i < end; i++, list = CDR(list))
+ RPLACA(list, item);
+ }
+
+ return (sequence);
+}
+
+LispObj *
+Lisp_Fmakunbound(LispBuiltin *builtin)
+/*
+ fmkaunbound symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ if (symbol->data.atom->a_function)
+ LispRemAtomFunctionProperty(symbol->data.atom);
+ else if (symbol->data.atom->a_builtin)
+ LispRemAtomBuiltinProperty(symbol->data.atom);
+ else if (symbol->data.atom->a_compiled)
+ LispRemAtomCompiledProperty(symbol->data.atom);
+
+ return (symbol);
+}
+
+LispObj *
+Lisp_Funcall(LispBuiltin *builtin)
+/*
+ funcall function &rest arguments
+ */
+{
+ LispObj *result;
+
+ LispObj *function, *arguments;
+
+ arguments = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ result = APPLY(function, arguments);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Functionp(LispBuiltin *builtin)
+/*
+ functionp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (FUNCTIONP(object) || LAMBDAP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Get(LispBuiltin *builtin)
+/*
+ get symbol indicator &optional default
+ */
+{
+ LispObj *result;
+
+ LispObj *symbol, *indicator, *defalt;
+
+ defalt = ARGUMENT(2);
+ indicator = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ result = LispGetAtomProperty(symbol->data.atom, indicator);
+
+ if (result != NIL)
+ result = CAR(result);
+ else
+ result = defalt == UNSPEC ? NIL : defalt;
+
+ return (result);
+}
+
+/*
+ * ext::getenv
+ */
+LispObj *
+Lisp_Getenv(LispBuiltin *builtin)
+/*
+ getenv name
+ */
+{
+ char *value;
+
+ LispObj *name;
+
+ name = ARGUMENT(0);
+
+ CHECK_STRING(name);
+ value = getenv(THESTR(name));
+
+ return (value ? STRING(value) : NIL);
+}
+
+LispObj *
+Lisp_Gc(LispBuiltin *builtin)
+/*
+ gc &optional car cdr
+ */
+{
+ LispObj *car, *cdr;
+
+ cdr = ARGUMENT(1);
+ car = ARGUMENT(0);
+
+ LispGC(car, cdr);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Gensym(LispBuiltin *builtin)
+/*
+ gensym &optional arg
+ */
+{
+ char *preffix = "G", name[132];
+ long counter = LONGINT_VALUE(Ogensym_counter->data.atom->property->value);
+ LispObj *symbol;
+
+ LispObj *arg;
+
+ arg = ARGUMENT(0);
+ if (arg != UNSPEC) {
+ if (STRINGP(arg))
+ preffix = THESTR(arg);
+ else {
+ CHECK_INDEX(arg);
+ counter = FIXNUM_VALUE(arg);
+ }
+ }
+ snprintf(name, sizeof(name), "%s%ld", preffix, counter);
+ if (strlen(name) >= 128)
+ LispDestroy("%s: name %s too long", STRFUN(builtin), name);
+ Ogensym_counter->data.atom->property->value = INTEGER(counter + 1);
+
+ symbol = UNINTERNED_ATOM(name);
+ symbol->data.atom->unreadable = !LispCheckAtomString(name);
+
+ return (symbol);
+}
+
+LispObj *
+Lisp_Go(LispBuiltin *builtin)
+/*
+ go tag
+ */
+{
+ unsigned blevel = lisp__data.block.block_level;
+
+ LispObj *tag;
+
+ tag = ARGUMENT(0);
+
+ while (blevel) {
+ LispBlock *block = lisp__data.block.block[--blevel];
+
+ if (block->type == LispBlockClosure)
+ /* if reached a function call */
+ break;
+ if (block->type == LispBlockBody) {
+ lisp__data.block.block_ret = tag;
+ LispBlockUnwind(block);
+ BLOCKJUMP(block);
+ }
+ }
+
+ LispDestroy("%s: no visible tagbody for %s",
+ STRFUN(builtin), STROBJ(tag));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_If(LispBuiltin *builtin)
+/*
+ if test then &optional else
+ */
+{
+ LispObj *result, *test, *then, *oelse;
+
+ oelse = ARGUMENT(2);
+ then = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ test = EVAL(test);
+ if (test != NIL)
+ result = EVAL(then);
+ else if (oelse != UNSPEC)
+ result = EVAL(oelse);
+ else
+ result = NIL;
+
+ return (result);
+}
+
+LispObj *
+Lisp_IgnoreErrors(LispBuiltin *builtin)
+/*
+ ignore-erros &rest body
+ */
+{
+ LispObj *result, **presult, **pbody;
+ int i, jumped, *pjumped;
+ LispBlock *block;
+
+ /* interpreter state */
+ GC_ENTER();
+ int stack, lex, length;
+
+ /* memory allocation */
+ int mem_level;
+ void **mem;
+
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ /* Save environment information */
+ stack = lisp__data.stack.length;
+ lex = lisp__data.env.lex;
+ length = lisp__data.env.length;
+
+ /* Save memory allocation information */
+ mem_level = lisp__data.mem.level;
+ mem = LispMalloc(mem_level * sizeof(void*));
+ memcpy(mem, lisp__data.mem.mem, mem_level * sizeof(void*));
+
+ ++lisp__data.ignore_errors;
+ presult = &result;
+ pjumped = &jumped;
+ pbody = &body;
+ result = NIL;
+ jumped = 1;
+ block = LispBeginBlock(NIL, LispBlockProtect);
+ if (setjmp(block->jmp) == 0) {
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+ jumped = 0;
+ }
+ LispEndBlock(block);
+ if (!lisp__data.destroyed && jumped)
+ result = lisp__data.block.block_ret;
+
+ if (lisp__data.destroyed) {
+ /* Restore environment */
+ lisp__data.stack.length = stack;
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = length;
+ GC_LEAVE();
+
+ /* Check for possible leaks due to ignoring errors */
+ for (i = 0; i < mem_level; i++) {
+ if (lisp__data.mem.mem[i] && mem[i] != lisp__data.mem.mem[i])
+ LispFree(lisp__data.mem.mem[i]);
+ }
+ for (; i < lisp__data.mem.level; i++) {
+ if (lisp__data.mem.mem[i])
+ LispFree(lisp__data.mem.mem[i]);
+ }
+
+ lisp__data.destroyed = 0;
+ result = NIL;
+ RETURN_COUNT = 1;
+ RETURN(0) = lisp__data.error_condition;
+ }
+ LispFree(mem);
+ --lisp__data.ignore_errors;
+
+ return (result);
+}
+
+LispObj *
+Lisp_Intersection(LispBuiltin *builtin)
+/*
+ intersection list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, INTERSECTION));
+}
+
+LispObj *
+Lisp_Nintersection(LispBuiltin *builtin)
+/*
+ nintersection list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, NINTERSECTION));
+}
+
+LispObj *
+Lisp_Keywordp(LispBuiltin *builtin)
+/*
+ keywordp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (KEYWORDP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Lambda(LispBuiltin *builtin)
+/*
+ lambda lambda-list &rest body
+ */
+{
+ GC_ENTER();
+ LispObj *name;
+ LispArgList *alist;
+
+ LispObj *lambda, *lambda_list, *body;
+
+ body = ARGUMENT(1);
+ lambda_list = ARGUMENT(0);
+
+ alist = LispCheckArguments(LispLambda, lambda_list, Snil, 0);
+
+ name = OPAQUE(alist, LispArgList_t);
+ lambda_list = LispListProtectedArguments(alist);
+ GC_PROTECT(name);
+ GC_PROTECT(lambda_list);
+ lambda = LispNewLambda(name, body, lambda_list, LispLambda);
+ LispUseArgList(alist);
+ GC_LEAVE();
+
+ return (lambda);
+}
+
+LispObj *
+Lisp_Last(LispBuiltin *builtin)
+/*
+ last list &optional count
+ */
+{
+ long count, length;
+ LispObj *list, *ocount;
+
+ ocount = ARGUMENT(1);
+ list = ARGUMENT(0);
+
+ if (!CONSP(list))
+ return (list);
+
+ length = LispLength(list);
+
+ if (ocount == UNSPEC)
+ count = 1;
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+
+ if (count >= length)
+ return (list);
+
+ length -= count;
+ for (; length > 0; length--)
+ list = CDR(list);
+
+ return (list);
+}
+
+LispObj *
+Lisp_Length(LispBuiltin *builtin)
+/*
+ length sequence
+ */
+{
+ LispObj *sequence;
+
+ sequence = ARGUMENT(0);
+
+ return (FIXNUM(LispLength(sequence)));
+}
+
+LispObj *
+Lisp_Let(LispBuiltin *builtin)
+/*
+ let init &rest body
+ */
+{
+ GC_ENTER();
+ int head = lisp__data.env.length;
+ LispObj *init, *body, *pair, *result, *list, *cons = NIL;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ CHECK_LIST(init);
+ for (list = NIL; CONSP(init); init = CDR(init)) {
+ LispObj *symbol, *value;
+
+ pair = CAR(init);
+ if (SYMBOLP(pair)) {
+ symbol = pair;
+ value = NIL;
+ }
+ else {
+ CHECK_CONS(pair);
+ symbol = CAR(pair);
+ CHECK_SYMBOL(symbol);
+ pair = CDR(pair);
+ if (CONSP(pair)) {
+ value = CAR(pair);
+ if (CDR(pair) != NIL)
+ LispDestroy("%s: too much arguments to initialize %s",
+ STRFUN(builtin), STROBJ(symbol));
+ value = EVAL(value);
+ }
+ else
+ value = NIL;
+ }
+ pair = CONS(symbol, value);
+ if (list == NIL) {
+ list = cons = CONS(pair, NIL);
+ GC_PROTECT(list);
+ }
+ else {
+ RPLACD(cons, CONS(pair, NIL));
+ cons = CDR(cons);
+ }
+ }
+ /* Add variables */
+ for (; CONSP(list); list = CDR(list)) {
+ pair = CAR(list);
+ CHECK_CONSTANT(CAR(pair));
+ LispAddVar(CAR(pair), CDR(pair));
+ ++lisp__data.env.head;
+ }
+ /* Values of symbols are now protected */
+ GC_LEAVE();
+
+ /* execute body */
+ for (result = NIL; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (result);
+}
+
+LispObj *
+Lisp_LetP(LispBuiltin *builtin)
+/*
+ let* init &rest body
+ */
+{
+ int head = lisp__data.env.length;
+ LispObj *init, *body, *pair, *result;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ CHECK_LIST(init);
+ for (; CONSP(init); init = CDR(init)) {
+ LispObj *symbol, *value;
+
+ pair = CAR(init);
+ if (SYMBOLP(pair)) {
+ symbol = pair;
+ value = NIL;
+ }
+ else {
+ CHECK_CONS(pair);
+ symbol = CAR(pair);
+ CHECK_SYMBOL(symbol);
+ pair = CDR(pair);
+ if (CONSP(pair)) {
+ value = CAR(pair);
+ if (CDR(pair) != NIL)
+ LispDestroy("%s: too much arguments to initialize %s",
+ STRFUN(builtin), STROBJ(symbol));
+ value = EVAL(value);
+ }
+ else
+ value = NIL;
+ }
+
+ CHECK_CONSTANT(symbol);
+ LispAddVar(symbol, value);
+ ++lisp__data.env.head;
+ }
+
+ /* execute body */
+ for (result = NIL; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (result);
+}
+
+LispObj *
+Lisp_List(LispBuiltin *builtin)
+/*
+ list &rest args
+ */
+{
+ LispObj *args;
+
+ args = ARGUMENT(0);
+
+ return (args);
+}
+
+LispObj *
+Lisp_ListP(LispBuiltin *builtin)
+/*
+ list* object &rest more-objects
+ */
+{
+ GC_ENTER();
+ LispObj *result, *cons;
+
+ LispObj *object, *more_objects;
+
+ more_objects = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (!CONSP(more_objects))
+ return (object);
+
+ result = cons = CONS(object, CAR(more_objects));
+ GC_PROTECT(result);
+ for (more_objects = CDR(more_objects); CONSP(more_objects);
+ more_objects = CDR(more_objects)) {
+ object = CAR(more_objects);
+ RPLACD(cons, CONS(CDR(cons), object));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+/* "classic" list-length */
+LispObj *
+Lisp_ListLength(LispBuiltin *builtin)
+/*
+ list-length list
+ */
+{
+ long length;
+ LispObj *fast, *slow;
+
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ CHECK_LIST(list);
+ for (fast = slow = list, length = 0;
+ CONSP(slow);
+ slow = CDR(slow), length += 2) {
+ if (fast == NIL)
+ break;
+ CHECK_CONS(fast);
+ fast = CDR(fast);
+ if (fast == NIL) {
+ ++length;
+ break;
+ }
+ CHECK_CONS(fast);
+ fast = CDR(fast);
+ if (slow == fast)
+ /* circular list */
+ return (NIL);
+ }
+
+ return (FIXNUM(length));
+}
+
+LispObj *
+Lisp_Listp(LispBuiltin *builtin)
+/*
+ listp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (object == NIL || CONSP(object) ? T : NIL);
+}
+
+static LispObj *
+LispListSet(LispBuiltin *builtin, int function)
+/*
+ intersection list1 list2 &key test test-not key
+ nintersection list1 list2 &key test test-not key
+ set-difference list1 list2 &key test test-not key
+ nset-difference list1 list2 &key test test-not key
+ set-exclusive-or list1 list2 &key test test-not key
+ nset-exclusive-or list1 list2 &key test test-not key
+ subsetp list1 list2 &key test test-not key
+ union list1 list2 &key test test-not key
+ nunion list1 list2 &key test test-not key
+ */
+{
+ GC_ENTER();
+ int code, expect, value, inplace, check_list2,
+ intersection, setdifference, xunion, setexclusiveor;
+ LispObj *lambda, *result, *cmp, *cmp1, *cmp2,
+ *item, *clist1, *clist2, *cons, *cdr;
+
+ LispObj *list1, *list2, *test, *test_not, *key;
+
+ key = ARGUMENT(4);
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ list2 = ARGUMENT(1);
+ list1 = ARGUMENT(0);
+
+ /* Check if arguments are valid lists */
+ CHECK_LIST(list1);
+ CHECK_LIST(list2);
+
+ setdifference = intersection = xunion = setexclusiveor = inplace = 0;
+ switch (function) {
+ case NSETDIFFERENCE:
+ inplace = 1;
+ case SETDIFFERENCE:
+ setdifference = 1;
+ break;
+ case NINTERSECTION:
+ inplace = 1;
+ case INTERSECTION:
+ intersection = 1;
+ break;
+ case NUNION:
+ inplace = 1;
+ case UNION:
+ xunion = 1;
+ break;
+ case NSETEXCLUSIVEOR:
+ inplace = 1;
+ case SETEXCLUSIVEOR:
+ setexclusiveor = 1;
+ break;
+ }
+
+ /* Check for fast return */
+ if (list1 == NIL)
+ return (setdifference || intersection ?
+ NIL : function == SUBSETP ? T : list2);
+ if (list2 == NIL)
+ return (intersection || xunion || function == SUBSETP ? NIL : list1);
+
+ CHECK_TEST();
+ clist1 = cdr = NIL;
+
+ /* Make a copy of list2 with the key predicate applied */
+ if (key != UNSPEC) {
+ result = cons = CONS(APPLY1(key, CAR(list2)), NIL);
+ GC_PROTECT(result);
+ for (cmp2 = CDR(list2); CONSP(cmp2); cmp2 = CDR(cmp2)) {
+ item = APPLY1(key, CAR(cmp2));
+ RPLACD(cons, CONS(APPLY1(key, CAR(cmp2)), NIL));
+ cons = CDR(cons);
+ }
+ /* check if list2 is a proper list */
+ CHECK_LIST(cmp2);
+ clist2 = result;
+ check_list2 = 0;
+ }
+ else {
+ clist2 = list2;
+ check_list2 = 1;
+ }
+ result = cons = NIL;
+
+ /* Compare elements of lists
+ * Logic:
+ * UNION
+ * 1) Walk list1 and if CAR(list1) not in list2, add it to result
+ * 2) Add list2 to result
+ * INTERSECTION
+ * 1) Walk list1 and if CAR(list1) in list2, add it to result
+ * SET-DIFFERENCE
+ * 1) Walk list1 and if CAR(list1) not in list2, add it to result
+ * SET-EXCLUSIVE-OR
+ * 1) Walk list1 and if CAR(list1) not in list2, add it to result
+ * 2) Walk list2 and if CAR(list2) not in list1, add it to result
+ * SUBSETP
+ * 1) Walk list1 and if CAR(list1) not in list2, return NIL
+ * 2) Return T
+ */
+ value = 0;
+ for (cmp1 = list1; CONSP(cmp1); cmp1 = CDR(cmp1)) {
+ item = CAR(cmp1);
+
+ /* Apply key predicate if required */
+ if (key != UNSPEC) {
+ cmp = APPLY1(key, item);
+ if (setexclusiveor) {
+ if (clist1 == NIL) {
+ clist1 = cdr = CONS(cmp, NIL);
+ GC_PROTECT(clist1);
+ }
+ else {
+ RPLACD(cdr, CONS(cmp, NIL));
+ cdr = CDR(cdr);
+ }
+ }
+ }
+ else
+ cmp = item;
+
+ /* Compare against list2 */
+ for (cmp2 = clist2; CONSP(cmp2); cmp2 = CDR(cmp2)) {
+ value = FCOMPARE(lambda, cmp, CAR(cmp2), code);
+ if (value == expect)
+ break;
+ }
+ if (check_list2 && value != expect) {
+ /* check if list2 is a proper list */
+ CHECK_LIST(cmp2);
+ check_list2 = 0;
+ }
+
+ if (function == SUBSETP) {
+ /* Element of list1 not in list2? */
+ if (value != expect) {
+ GC_LEAVE();
+
+ return (NIL);
+ }
+ }
+ /* If need to add item to result */
+ else if (((setdifference || xunion || setexclusiveor) &&
+ value != expect) ||
+ (intersection && value == expect)) {
+ if (inplace) {
+ if (result == NIL)
+ result = cons = cmp1;
+ else {
+ if (setexclusiveor) {
+ /* don't remove elements yet, will need
+ * to check agains't list2 later */
+ for (cmp2 = cons; CDR(cmp2) != cmp1; cmp2 = CDR(cmp2))
+ ;
+ if (cmp2 != cons) {
+ RPLACD(cmp2, list1);
+ list1 = cmp2;
+ }
+ }
+ RPLACD(cons, cmp1);
+ cons = cmp1;
+ }
+ }
+ else {
+ if (result == NIL) {
+ result = cons = CONS(item, NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(item, NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+ /* check if list1 is a proper list */
+ CHECK_LIST(cmp1);
+
+ if (function == SUBSETP) {
+ GC_LEAVE();
+
+ return (T);
+ }
+ else if (xunion) {
+ /* Add list2 to tail of result */
+ if (result == NIL)
+ result = list2;
+ else
+ RPLACD(cons, list2);
+ }
+ else if (setexclusiveor) {
+ LispObj *result2, *cons2;
+
+ result2 = cons2 = NIL;
+ for (cmp2 = list2; CONSP(cmp2); cmp2 = CDR(cmp2)) {
+ item = CAR(cmp2);
+
+ if (key != UNSPEC) {
+ cmp = CAR(clist2);
+ /* XXX changing clist2 */
+ clist2 = CDR(clist2);
+ cmp1 = clist1;
+ }
+ else {
+ cmp = item;
+ cmp1 = list1;
+ }
+
+ /* Compare against list1 */
+ for (; CONSP(cmp1); cmp1 = CDR(cmp1)) {
+ value = FCOMPARE(lambda, cmp, CAR(cmp1), code);
+ if (value == expect)
+ break;
+ }
+
+ if (value != expect) {
+ if (inplace) {
+ if (result2 == NIL)
+ result2 = cons2 = cmp2;
+ else {
+ RPLACD(cons2, cmp2);
+ cons2 = cmp2;
+ }
+ }
+ else {
+ if (result == NIL) {
+ result = cons = CONS(item, NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(item, NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+ if (inplace) {
+ if (CONSP(cons2))
+ RPLACD(cons2, NIL);
+ if (result == NIL)
+ result = result2;
+ else
+ RPLACD(cons, result2);
+ }
+ }
+ else if ((function == NSETDIFFERENCE || function == NINTERSECTION) &&
+ CONSP(cons))
+ RPLACD(cons, NIL);
+
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Loop(LispBuiltin *builtin)
+/*
+ loop &rest body
+ */
+{
+ LispObj *code, *result;
+ LispBlock *block;
+
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ result = NIL;
+ block = LispBeginBlock(NIL, LispBlockTag);
+ if (setjmp(block->jmp) == 0) {
+ for (;;)
+ for (code = body; CONSP(code); code = CDR(code))
+ (void)EVAL(CAR(code));
+ }
+ LispEndBlock(block);
+ result = lisp__data.block.block_ret;
+
+ return (result);
+}
+
+/* XXX This function is broken, needs a review
+ (being delayed until true array/vectors be implemented) */
+LispObj *
+Lisp_MakeArray(LispBuiltin *builtin)
+/*
+ make-array dimensions &key element-type initial-element initial-contents
+ adjustable fill-pointer displaced-to
+ displaced-index-offset
+ */
+{
+ long rank = 0, count = 1, offset, zero, c;
+ LispObj *obj, *dim, *array;
+ LispType type;
+
+ LispObj *dimensions, *element_type, *initial_element, *initial_contents,
+ *adjustable, *fill_pointer, *displaced_to,
+ *displaced_index_offset;
+
+ dim = array = NIL;
+ type = LispNil_t;
+
+ displaced_index_offset = ARGUMENT(7);
+ displaced_to = ARGUMENT(6);
+ fill_pointer = ARGUMENT(5);
+ adjustable = ARGUMENT(4);
+ initial_contents = ARGUMENT(3);
+ initial_element = ARGUMENT(2);
+ element_type = ARGUMENT(1);
+ dimensions = ARGUMENT(0);
+
+ if (INDEXP(dimensions)) {
+ dim = CONS(dimensions, NIL);
+ rank = 1;
+ count = FIXNUM_VALUE(dimensions);
+ }
+ else if (CONSP(dimensions)) {
+ dim = dimensions;
+
+ for (rank = 0; CONSP(dim); rank++, dim = CDR(dim)) {
+ obj = CAR(dim);
+ CHECK_INDEX(obj);
+ count *= FIXNUM_VALUE(obj);
+ }
+ dim = dimensions;
+ }
+ else if (dimensions == NIL) {
+ dim = NIL;
+ rank = count = 0;
+ }
+ else
+ LispDestroy("%s: %s is a bad array dimension",
+ STRFUN(builtin), STROBJ(dimensions));
+
+ /* check element-type */
+ if (element_type != UNSPEC) {
+ if (element_type == T)
+ type = LispNil_t;
+ else if (!SYMBOLP(element_type))
+ LispDestroy("%s: unsupported element type %s",
+ STRFUN(builtin), STROBJ(element_type));
+ else {
+ Atom_id atom = ATOMID(element_type);
+
+ if (atom == Satom)
+ type = LispAtom_t;
+ else if (atom == Sinteger)
+ type = LispInteger_t;
+ else if (atom == Scharacter)
+ type = LispSChar_t;
+ else if (atom == Sstring)
+ type = LispString_t;
+ else if (atom == Slist)
+ type = LispCons_t;
+ else if (atom == Sopaque)
+ type = LispOpaque_t;
+ else
+ LispDestroy("%s: unsupported element type %s",
+ STRFUN(builtin), ATOMID(element_type));
+ }
+ }
+
+ /* check initial-contents */
+ if (rank) {
+ CHECK_LIST(initial_contents);
+ }
+
+ /* check displaced-to */
+ if (displaced_to != UNSPEC) {
+ CHECK_ARRAY(displaced_to);
+ }
+
+ /* check displaced-index-offset */
+ offset = -1;
+ if (displaced_index_offset != UNSPEC) {
+ CHECK_INDEX(displaced_index_offset);
+ offset = FIXNUM_VALUE(displaced_index_offset);
+ }
+
+ c = 0;
+ if (initial_element != UNSPEC)
+ ++c;
+ if (initial_contents != UNSPEC)
+ ++c;
+ if (displaced_to != UNSPEC || offset >= 0)
+ ++c;
+ if (c > 1)
+ LispDestroy("%s: more than one initialization specified",
+ STRFUN(builtin));
+ if (initial_element == UNSPEC)
+ initial_element = NIL;
+
+ zero = count == 0;
+ if (displaced_to != UNSPEC) {
+ CHECK_ARRAY(displaced_to);
+ if (offset < 0)
+ offset = 0;
+ for (c = 1, obj = displaced_to->data.array.dim; obj != NIL;
+ obj = CDR(obj))
+ c *= FIXNUM_VALUE(CAR(obj));
+ if (c < count + offset)
+ LispDestroy("%s: array-total-size + displaced-index-offset "
+ "exceeds total size", STRFUN(builtin));
+ for (c = 0, array = displaced_to->data.array.list; c < offset; c++)
+ array = CDR(array);
+ }
+ else if (initial_contents != UNSPEC) {
+ CHECK_CONS(initial_contents);
+ if (rank == 0)
+ array = initial_contents;
+ else if (rank == 1) {
+ for (array = initial_contents, c = 0; c < count;
+ array = CDR(array), c++)
+ if (!CONSP(array))
+ LispDestroy("%s: bad argument or size %s",
+ STRFUN(builtin), STROBJ(array));
+ if (array != NIL)
+ LispDestroy("%s: bad argument or size %s",
+ STRFUN(builtin), STROBJ(array));
+ array = initial_contents;
+ }
+ else {
+ LispObj *err = NIL;
+ /* check if list matches */
+ int i, j, k, *dims, *loop;
+
+ /* create iteration variables */
+ dims = LispMalloc(sizeof(int) * rank);
+ loop = LispCalloc(1, sizeof(int) * (rank - 1));
+ for (i = 0, obj = dim; CONSP(obj); i++, obj = CDR(obj))
+ dims[i] = FIXNUM_VALUE(CAR(obj));
+
+ /* check if list matches specified dimensions */
+ while (loop[0] < dims[0]) {
+ for (obj = initial_contents, i = 0; i < rank - 1; i++) {
+ for (j = 0; j < loop[i]; j++)
+ obj = CDR(obj);
+ err = obj;
+ if (!CONSP(obj = CAR(obj)))
+ goto make_array_error;
+ err = obj;
+ }
+ --i;
+ for (;;) {
+ ++loop[i];
+ if (i && loop[i] >= dims[i])
+ loop[i] = 0;
+ else
+ break;
+ --i;
+ }
+ for (k = 0; k < dims[rank - 1]; obj = CDR(obj), k++) {
+ if (!CONSP(obj))
+ goto make_array_error;
+ }
+ if (obj == NIL)
+ continue;
+make_array_error:
+ LispFree(dims);
+ LispFree(loop);
+ LispDestroy("%s: bad argument or size %s",
+ STRFUN(builtin), STROBJ(err));
+ }
+
+ /* list is correct, use it to fill initial values */
+
+ /* reset loop */
+ memset(loop, 0, sizeof(int) * (rank - 1));
+
+ GCDisable();
+ /* fill array with supplied values */
+ array = NIL;
+ while (loop[0] < dims[0]) {
+ for (obj = initial_contents, i = 0; i < rank - 1; i++) {
+ for (j = 0; j < loop[i]; j++)
+ obj = CDR(obj);
+ obj = CAR(obj);
+ }
+ --i;
+ for (;;) {
+ ++loop[i];
+ if (i && loop[i] >= dims[i])
+ loop[i] = 0;
+ else
+ break;
+ --i;
+ }
+ for (k = 0; k < dims[rank - 1]; obj = CDR(obj), k++) {
+ if (array == NIL)
+ array = CONS(CAR(obj), NIL);
+ else {
+ RPLACD(array, CONS(CAR(array), CDR(array)));
+ RPLACA(array, CAR(obj));
+ }
+ }
+ }
+ LispFree(dims);
+ LispFree(loop);
+ array = LispReverse(array);
+ GCEnable();
+ }
+ }
+ else {
+ GCDisable();
+ /* allocate array */
+ if (count) {
+ --count;
+ array = CONS(initial_element, NIL);
+ while (count) {
+ RPLACD(array, CONS(CAR(array), CDR(array)));
+ RPLACA(array, initial_element);
+ count--;
+ }
+ }
+ GCEnable();
+ }
+
+ obj = LispNew(array, dim);
+ obj->type = LispArray_t;
+ obj->data.array.list = array;
+ obj->data.array.dim = dim;
+ obj->data.array.rank = rank;
+ obj->data.array.type = type;
+ obj->data.array.zero = zero;
+
+ return (obj);
+}
+
+LispObj *
+Lisp_MakeList(LispBuiltin *builtin)
+/*
+ make-list size &key initial-element
+ */
+{
+ GC_ENTER();
+ long count;
+ LispObj *result, *cons;
+
+ LispObj *size, *initial_element;
+
+ initial_element = ARGUMENT(1);
+ size = ARGUMENT(0);
+
+ CHECK_INDEX(size);
+ count = FIXNUM_VALUE(size);
+
+ if (count == 0)
+ return (NIL);
+ if (initial_element == UNSPEC)
+ initial_element = NIL;
+
+ result = cons = CONS(initial_element, NIL);
+ GC_PROTECT(result);
+ for (; count > 1; count--) {
+ RPLACD(cons, CONS(initial_element, NIL));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_MakeSymbol(LispBuiltin *builtin)
+/*
+ make-symbol name
+ */
+{
+ LispObj *name, *symbol;
+
+ name = ARGUMENT(0);
+ CHECK_STRING(name);
+
+ symbol = UNINTERNED_ATOM(THESTR(name));
+ symbol->data.atom->unreadable = !LispCheckAtomString(THESTR(name));
+
+ return (symbol);
+}
+
+LispObj *
+Lisp_Makunbound(LispBuiltin *builtin)
+/*
+ makunbound symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ LispUnsetVar(symbol);
+
+ return (symbol);
+}
+
+LispObj *
+Lisp_Mapc(LispBuiltin *builtin)
+/*
+ mapc function list &rest more-lists
+ */
+{
+ return (LispMapc(builtin, 0));
+}
+
+LispObj *
+Lisp_Mapcar(LispBuiltin *builtin)
+/*
+ mapcar function list &rest more-lists
+ */
+{
+ return (LispMapc(builtin, 1));
+}
+
+/* Like nconc but ignore non list arguments */
+LispObj *
+LispMapnconc(LispObj *list)
+{
+ LispObj *result = NIL;
+
+ if (CONSP(list)) {
+ LispObj *cons, *head, *tail;
+
+ cons = NIL;
+ for (; CONSP(CDR(list)); list = CDR(list)) {
+ head = CAR(list);
+ if (CONSP(head)) {
+ for (tail = head; CONSP(CDR(tail)); tail = CDR(tail))
+ ;
+ if (cons != NIL)
+ RPLACD(cons, head);
+ else
+ result = head;
+ cons = tail;
+ }
+ }
+ head = CAR(list);
+ if (CONSP(head)) {
+ if (cons != NIL)
+ RPLACD(cons, head);
+ else
+ result = head;
+ }
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Mapcan(LispBuiltin *builtin)
+/*
+ mapcan function list &rest more-lists
+ */
+{
+ return (LispMapnconc(LispMapc(builtin, 1)));
+}
+
+static LispObj *
+LispMapc(LispBuiltin *builtin, int mapcar)
+{
+ GC_ENTER();
+ long i, offset, count, length;
+ LispObj *result = NIL, *cons, *arguments, *acons, *rest, *alist, *value;
+ LispObj *stk[8], **cdrs;
+
+ LispObj *function, *list, *more_lists;
+
+ more_lists = ARGUMENT(2);
+ list = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ /* Result will be no longer than this */
+ for (length = 0, alist = list; CONSP(alist); length++, alist = CDR(alist))
+ ;
+
+ /* If first argument is not a list... */
+ if (length == 0)
+ return (NIL);
+
+ /* At least one argument will be passed to function, count how many
+ * extra arguments will be used, and calculate result length. */
+ count = 0;
+ for (rest = more_lists; CONSP(rest); rest = CDR(rest), count++) {
+
+ /* Check if extra list is really a list, and if it is smaller
+ * than the first list */
+ for (i = 0, alist = CAR(rest);
+ i < length && CONSP(alist);
+ i++, alist = CDR(alist))
+ ;
+
+ /* If it is not a true list */
+ if (i == 0)
+ return (NIL);
+
+ /* If it is smaller than the currently calculated result length */
+ if (i < length)
+ length = i;
+ }
+
+ if (mapcar) {
+ /* Initialize gc protected object cells for resulting list */
+ result = cons = CONS(NIL, NIL);
+ GC_PROTECT(result);
+ }
+ else
+ result = cons = list;
+
+ if (count >= sizeof(stk) / sizeof(stk[0]))
+ cdrs = LispMalloc(count * sizeof(LispObj*));
+ else
+ cdrs = &stk[0];
+ for (i = 0, rest = more_lists; i < count; i++, rest = CDR(rest))
+ cdrs[i] = CAR(rest);
+
+ /* Initialize gc protected object cells for argument list */
+ arguments = acons = CONS(NIL, NIL);
+ GC_PROTECT(arguments);
+
+ /* Allocate space for extra arguments */
+ for (i = 0; i < count; i++) {
+ RPLACD(acons, CONS(NIL, NIL));
+ acons = CDR(acons);
+ }
+
+ /* For every element of the list that will be used */
+ for (offset = 0;; list = CDR(list)) {
+ acons = arguments;
+
+ /* Add first argument */
+ RPLACA(acons, CAR(list));
+ acons = CDR(acons);
+
+ /* For every extra list argument */
+ for (i = 0; i < count; i++) {
+ alist = cdrs[i];
+ cdrs[i] = CDR(cdrs[i]);
+
+ /* Add element to argument list */
+ RPLACA(acons, CAR(alist));
+ acons = CDR(acons);
+ }
+
+ value = APPLY(function, arguments);
+
+ if (mapcar) {
+ /* Store result */
+ RPLACA(cons, value);
+
+ /* Allocate new result cell */
+ if (++offset < length) {
+ RPLACD(cons, CONS(NIL, NIL));
+ cons = CDR(cons);
+ }
+ else
+ break;
+ }
+ else if (++offset >= length)
+ break;
+ }
+
+ /* Unprotect argument and result list */
+ GC_LEAVE();
+ if (cdrs != &stk[0])
+ LispFree(cdrs);
+
+ return (result);
+}
+
+static LispObj *
+LispMapl(LispBuiltin *builtin, int maplist)
+{
+ GC_ENTER();
+ long i, offset, count, length;
+ LispObj *result = NIL, *cons, *arguments, *acons, *rest, *alist, *value;
+ LispObj *stk[8], **cdrs;
+
+ LispObj *function, *list, *more_lists;
+
+ more_lists = ARGUMENT(2);
+ list = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ /* count is the number of lists, length is the length of the result */
+ for (length = 0, alist = list; CONSP(alist); length++, alist = CDR(alist))
+ ;
+
+ /* first argument is not a list */
+ if (length == 0)
+ return (NIL);
+
+ /* check remaining arguments */
+ for (count = 0, rest = more_lists; CONSP(rest); rest = CDR(rest), count++) {
+ for (i = 0, alist = CAR(rest);
+ i < length && CONSP(alist);
+ i++, alist = CDR(alist))
+ ;
+ /* argument is not a list */
+ if (i == 0)
+ return (NIL);
+ /* result will have the length of the smallest list */
+ if (i < length)
+ length = i;
+ }
+
+ /* result will be a list */
+ if (maplist) {
+ result = cons = CONS(NIL, NIL);
+ GC_PROTECT(result);
+ }
+ else
+ result = cons = list;
+
+ if (count >= sizeof(stk) / sizeof(stk[0]))
+ cdrs = LispMalloc(count * sizeof(LispObj*));
+ else
+ cdrs = &stk[0];
+ for (i = 0, rest = more_lists; i < count; i++, rest = CDR(rest))
+ cdrs[i] = CAR(rest);
+
+ /* initialize argument list */
+ arguments = acons = CONS(NIL, NIL);
+ GC_PROTECT(arguments);
+ for (i = 0; i < count; i++) {
+ RPLACD(acons, CONS(NIL, NIL));
+ acons = CDR(acons);
+ }
+
+ /* for every used list element */
+ for (offset = 0;; list = CDR(list)) {
+ acons = arguments;
+
+ /* first argument */
+ RPLACA(acons, list);
+ acons = CDR(acons);
+
+ /* for every extra list */
+ for (i = 0; i < count; i++) {
+ RPLACA(acons, cdrs[i]);
+ cdrs[i] = CDR(cdrs[i]);
+ acons = CDR(acons);
+ }
+
+ value = APPLY(function, arguments);
+
+ if (maplist) {
+ /* store result */
+ RPLACA(cons, value);
+
+ /* allocate new cell */
+ if (++offset < length) {
+ RPLACD(cons, CONS(NIL, NIL));
+ cons = CDR(cons);
+ }
+ else
+ break;
+ }
+ else if (++offset >= length)
+ break;
+ }
+
+ GC_LEAVE();
+ if (cdrs != &stk[0])
+ LispFree(cdrs);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Mapl(LispBuiltin *builtin)
+/*
+ mapl function list &rest more-lists
+ */
+{
+ return (LispMapl(builtin, 0));
+}
+
+LispObj *
+Lisp_Maplist(LispBuiltin *builtin)
+/*
+ maplist function list &rest more-lists
+ */
+{
+ return (LispMapl(builtin, 1));
+}
+
+LispObj *
+Lisp_Mapcon(LispBuiltin *builtin)
+/*
+ mapcon function list &rest more-lists
+ */
+{
+ return (LispMapnconc(LispMapl(builtin, 1)));
+}
+
+LispObj *
+Lisp_Member(LispBuiltin *builtin)
+/*
+ member item list &key test test-not key
+ */
+{
+ int code, expect;
+ LispObj *compare, *lambda;
+ LispObj *item, *list, *test, *test_not, *key;
+
+ key = ARGUMENT(4);
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ list = ARGUMENT(1);
+ item = ARGUMENT(0);
+
+ if (list == NIL)
+ return (NIL);
+ CHECK_CONS(list);
+
+ CHECK_TEST();
+ if (key == UNSPEC) {
+ if (code == FEQ) {
+ for (; CONSP(list); list = CDR(list))
+ if (item == CAR(list))
+ return (list);
+ }
+ else {
+ for (; CONSP(list); list = CDR(list))
+ if (FCOMPARE(lambda, item, CAR(list), code) == expect)
+ return (list);
+ }
+ }
+ else {
+ if (code == FEQ) {
+ for (; CONSP(list); list = CDR(list))
+ if (item == APPLY1(key, CAR(list)))
+ return (list);
+ }
+ else {
+ for (; CONSP(list); list = CDR(list)) {
+ compare = APPLY1(key, CAR(list));
+ if (FCOMPARE(lambda, item, compare, code) == expect)
+ return (list);
+ }
+ }
+ }
+ /* check if is a proper list */
+ CHECK_LIST(list);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_MemberIf(LispBuiltin *builtin)
+/*
+ member-if predicate list &key key
+ */
+{
+ return (LispAssocOrMember(builtin, MEMBER, IF));
+}
+
+LispObj *
+Lisp_MemberIfNot(LispBuiltin *builtin)
+/*
+ member-if-not predicate list &key key
+ */
+{
+ return (LispAssocOrMember(builtin, MEMBER, IFNOT));
+}
+
+LispObj *
+Lisp_MultipleValueBind(LispBuiltin *builtin)
+/*
+ multiple-value-bind symbols values &rest body
+ */
+{
+ int i, head = lisp__data.env.length;
+ LispObj *result, *symbol, *value;
+
+ LispObj *symbols, *values, *body;
+
+ body = ARGUMENT(2);
+ values = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ result = EVAL(values);
+ for (i = -1; CONSP(symbols); symbols = CDR(symbols), i++) {
+ symbol = CAR(symbols);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ if (i >= 0 && i < RETURN_COUNT)
+ value = RETURN(i);
+ else if (i < 0)
+ value = result;
+ else
+ value = NIL;
+ LispAddVar(symbol, value);
+ ++lisp__data.env.head;
+ }
+
+ /* Execute code with binded variables (if any) */
+ for (result = NIL; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (result);
+}
+
+LispObj *
+Lisp_MultipleValueCall(LispBuiltin *builtin)
+/*
+ multiple-value-call function &rest form
+ */
+{
+ GC_ENTER();
+ int i;
+ LispObj *arguments, *cons, *result;
+
+ LispObj *function, *form;
+
+ form = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ /* build argument list */
+ arguments = cons = NIL;
+ for (; CONSP(form); form = CDR(form)) {
+ RETURN_COUNT = 0;
+ result = EVAL(CAR(form));
+ if (RETURN_COUNT >= 0) {
+ if (arguments == NIL) {
+ arguments = cons = CONS(result, NIL);
+ GC_PROTECT(arguments);
+ }
+ else {
+ RPLACD(cons, CONS(result, NIL));
+ cons = CDR(cons);
+ }
+ for (i = 0; i < RETURN_COUNT; i++) {
+ RPLACD(cons, CONS(RETURN(i), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ /* apply function */
+ if (POINTERP(function) && !XSYMBOLP(function) && !XFUNCTIONP(function)) {
+ function = EVAL(function);
+ GC_PROTECT(function);
+ }
+ result = APPLY(function, arguments);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_MultipleValueProg1(LispBuiltin *builtin)
+/*
+ multiple-value-prog1 first-form &rest form
+ */
+{
+ GC_ENTER();
+ int i, count;
+ LispObj *values, *cons;
+
+ LispObj *first_form, *form;
+
+ form = ARGUMENT(1);
+ first_form = ARGUMENT(0);
+
+ values = EVAL(first_form);
+ if (!CONSP(form))
+ return (values);
+
+ cons = NIL;
+ count = RETURN_COUNT;
+ if (count < 0)
+ values = NIL;
+ else if (count == 0) {
+ GC_PROTECT(values);
+ }
+ else {
+ values = cons = CONS(values, NIL);
+ GC_PROTECT(values);
+ for (i = 0; i < count; i++) {
+ RPLACD(cons, CONS(RETURN(i), NIL));
+ cons = CDR(cons);
+ }
+ }
+
+ for (; CONSP(form); form = CDR(form))
+ EVAL(CAR(form));
+
+ RETURN_COUNT = count;
+ if (count > 0) {
+ for (i = 0, cons = CDR(values); CONSP(cons); cons = CDR(cons), i++)
+ RETURN(i) = CAR(cons);
+ values = CAR(values);
+ }
+ GC_LEAVE();
+
+ return (values);
+}
+
+LispObj *
+Lisp_MultipleValueList(LispBuiltin *builtin)
+/*
+ multiple-value-list form
+ */
+{
+ int i;
+ GC_ENTER();
+ LispObj *form, *result, *cons;
+
+ form = ARGUMENT(0);
+
+ result = EVAL(form);
+
+ if (RETURN_COUNT < 0)
+ return (NIL);
+
+ result = cons = CONS(result, NIL);
+ GC_PROTECT(result);
+ for (i = 0; i < RETURN_COUNT; i++) {
+ RPLACD(cons, CONS(RETURN(i), NIL));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_MultipleValueSetq(LispBuiltin *builtin)
+/*
+ multiple-value-setq symbols form
+ */
+{
+ int i;
+ LispObj *result, *symbol, *value;
+
+ LispObj *symbols, *form;
+
+ form = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ CHECK_LIST(symbols);
+ result = EVAL(form);
+ if (CONSP(symbols)) {
+ symbol = CAR(symbols);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ LispSetVar(symbol, result);
+ symbols = CDR(symbols);
+ }
+ for (i = 0; CONSP(symbols); symbols = CDR(symbols), i++) {
+ symbol = CAR(symbols);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ if (i < RETURN_COUNT && RETURN_COUNT > 0)
+ value = RETURN(i);
+ else
+ value = NIL;
+ LispSetVar(symbol, value);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Nconc(LispBuiltin *builtin)
+/*
+ nconc &rest lists
+ */
+{
+ LispObj *list, *lists, *cons, *head, *tail;
+
+ lists = ARGUMENT(0);
+
+ /* skip any initial empty lists */
+ for (; CONSP(lists); lists = CDR(lists))
+ if (CAR(lists) != NIL)
+ break;
+
+ /* don't check if a proper list */
+ if (!CONSP(lists))
+ return (lists);
+
+ /* setup to concatenate lists */
+ list = CAR(lists);
+ CHECK_CONS(list);
+ for (cons = list; CONSP(CDR(cons)); cons = CDR(cons))
+ ;
+
+ /* if only two lists */
+ lists = CDR(lists);
+ if (!CONSP(lists)) {
+ RPLACD(cons, lists);
+
+ return (list);
+ }
+
+ /* concatenate */
+ for (; CONSP(CDR(lists)); lists = CDR(lists)) {
+ head = CAR(lists);
+ if (head == NIL)
+ continue;
+ CHECK_CONS(head);
+ for (tail = head; CONSP(CDR(tail)); tail = CDR(tail))
+ ;
+ RPLACD(cons, head);
+ cons = tail;
+ }
+ /* add last list */
+ RPLACD(cons, CAR(lists));
+
+ return (list);
+}
+
+LispObj *
+Lisp_Nreverse(LispBuiltin *builtin)
+/*
+ nreverse sequence
+ */
+{
+ return (LispXReverse(builtin, 1));
+}
+
+LispObj *
+Lisp_NsetDifference(LispBuiltin *builtin)
+/*
+ nset-difference list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, NSETDIFFERENCE));
+}
+
+LispObj *
+Lisp_Nsubstitute(LispBuiltin *builtin)
+/*
+ nsubstitute newitem olditem sequence &key from-end test test-not start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, NONE));
+}
+
+LispObj *
+Lisp_NsubstituteIf(LispBuiltin *builtin)
+/*
+ nsubstitute-if newitem test sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, IF));
+}
+
+LispObj *
+Lisp_NsubstituteIfNot(LispBuiltin *builtin)
+/*
+ nsubstitute-if-not newitem test sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, NSUBSTITUTE, IFNOT));
+}
+
+LispObj *
+Lisp_Nth(LispBuiltin *builtin)
+/*
+ nth index list
+ */
+{
+ long position;
+ LispObj *oindex, *list;
+
+ list = ARGUMENT(1);
+ oindex = ARGUMENT(0);
+
+ CHECK_INDEX(oindex);
+ position = FIXNUM_VALUE(oindex);
+
+ if (list == NIL)
+ return (NIL);
+
+ CHECK_CONS(list);
+ for (; position > 0; position--) {
+ if (!CONSP(list))
+ return (NIL);
+ list = CDR(list);
+ }
+
+ return (CONSP(list) ? CAR(list) : NIL);
+}
+
+LispObj *
+Lisp_Nthcdr(LispBuiltin *builtin)
+/*
+ nthcdr index list
+ */
+{
+ long position;
+ LispObj *oindex, *list;
+
+ list = ARGUMENT(1);
+ oindex = ARGUMENT(0);
+
+ CHECK_INDEX(oindex);
+ position = FIXNUM_VALUE(oindex);
+
+ if (list == NIL)
+ return (NIL);
+ CHECK_CONS(list);
+
+ for (; position > 0; position--) {
+ if (!CONSP(list))
+ return (NIL);
+ list = CDR(list);
+ }
+
+ return (list);
+}
+
+LispObj *
+Lisp_NthValue(LispBuiltin *builtin)
+/*
+ nth-value index form
+ */
+{
+ long i;
+ LispObj *oindex, *form, *result;
+
+ form = ARGUMENT(1);
+ oindex = ARGUMENT(0);
+
+ oindex = EVAL(oindex);
+ CHECK_INDEX(oindex);
+ i = FIXNUM_VALUE(oindex) - 1;
+
+ result = EVAL(form);
+ if (RETURN_COUNT < 0 || i >= RETURN_COUNT)
+ result = NIL;
+ else if (i >= 0)
+ result = RETURN(i);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Null(LispBuiltin *builtin)
+/*
+ null list
+ */
+{
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ return (list == NIL ? T : NIL);
+}
+
+LispObj *
+Lisp_Or(LispBuiltin *builtin)
+/*
+ or &rest args
+ */
+{
+ LispObj *result = NIL, *args;
+
+ args = ARGUMENT(0);
+
+ for (; CONSP(args); args = CDR(args)) {
+ result = EVAL(CAR(args));
+ if (result != NIL)
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Pairlis(LispBuiltin *builtin)
+/*
+ pairlis key data &optional alist
+ */
+{
+ LispObj *result, *cons;
+
+ LispObj *key, *data, *alist;
+
+ alist = ARGUMENT(2);
+ data = ARGUMENT(1);
+ key = ARGUMENT(0);
+
+ if (CONSP(key) && CONSP(data)) {
+ GC_ENTER();
+
+ result = cons = CONS(CONS(CAR(key), CAR(data)), NIL);
+ GC_PROTECT(result);
+ key = CDR(key);
+ data = CDR(data);
+ for (; CONSP(key) && CONSP(data); key = CDR(key), data = CDR(data)) {
+ RPLACD(cons, CONS(CONS(CAR(key), CAR(data)), NIL));
+ cons = CDR(cons);
+ }
+ if (CONSP(key) || CONSP(data))
+ LispDestroy("%s: different length lists", STRFUN(builtin));
+ GC_LEAVE();
+ if (alist != UNSPEC)
+ RPLACD(cons, alist);
+ }
+ else
+ result = alist == UNSPEC ? NIL : alist;
+
+ return (result);
+}
+
+static LispObj *
+LispFindOrPosition(LispBuiltin *builtin,
+ int function, int comparison)
+/*
+ find item sequence &key from-end test test-not start end key
+ find-if predicate sequence &key from-end start end key
+ find-if-not predicate sequence &key from-end start end key
+ position item sequence &key from-end test test-not start end key
+ position-if predicate sequence &key from-end start end key
+ position-if-not predicate sequence &key from-end start end key
+ */
+{
+ int code = 0, istring, expect, value;
+ char *string = NULL;
+ long offset = -1, start, end, length, i = comparison == NONE ? 7 : 5;
+ LispObj *cmp, *element, **objects = NULL;
+
+ LispObj *item, *predicate, *sequence, *from_end,
+ *test, *test_not, *ostart, *oend, *key;
+
+ key = ARGUMENT(i); --i;
+ oend = ARGUMENT(i); --i;
+ ostart = ARGUMENT(i); --i;
+ if (comparison == NONE) {
+ test_not = ARGUMENT(i); --i;
+ test = ARGUMENT(i); --i;
+ }
+ else
+ test_not = test = UNSPEC;
+ from_end = ARGUMENT(i); --i;
+ if (from_end == UNSPEC)
+ from_end = NIL;
+ sequence = ARGUMENT(i); --i;
+ if (comparison == NONE) {
+ item = ARGUMENT(i);
+ predicate = Oeql;
+ }
+ else {
+ predicate = ARGUMENT(i);
+ item = NIL;
+ }
+
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ if (sequence == NIL)
+ return (NIL);
+
+ /* Cannot specify both :test and :test-not */
+ if (test != UNSPEC && test_not != UNSPEC)
+ LispDestroy("%s: specify either :TEST or :TEST-NOT", STRFUN(builtin));
+
+ expect = 1;
+ if (comparison == NONE) {
+ if (test != UNSPEC)
+ predicate = test;
+ else if (test_not != UNSPEC) {
+ predicate = test_not;
+ expect = 0;
+ }
+ FUNCTION_CHECK(predicate);
+ code = FCODE(predicate);
+ }
+
+ cmp = element = NIL;
+ istring = STRINGP(sequence);
+ if (istring)
+ string = THESTR(sequence);
+ else {
+ if (!CONSP(sequence))
+ sequence = sequence->data.array.list;
+ for (i = 0; i < start; i++)
+ sequence = CDR(sequence);
+ }
+
+ if ((length = end - start) == 0)
+ return (NIL);
+
+ if (from_end != NIL && !istring) {
+ objects = LispMalloc(sizeof(LispObj*) * length);
+ for (i = length - 1; i >= 0; i--, sequence = CDR(sequence))
+ objects[i] = CAR(sequence);
+ }
+
+ for (i = 0; i < length; i++) {
+ if (istring)
+ element = SCHAR(string[from_end == NIL ? i + start : end - i - 1]);
+ else
+ element = from_end == NIL ? CAR(sequence) : objects[i];
+
+ if (key != UNSPEC)
+ cmp = APPLY1(key, element);
+ else
+ cmp = element;
+
+ /* Update list */
+ if (!istring && from_end == NIL)
+ sequence = CDR(sequence);
+
+ if (comparison == NONE)
+ value = FCOMPARE(predicate, item, cmp, code);
+ else
+ value = APPLY1(predicate, cmp) != NIL;
+
+ if ((!value &&
+ (comparison == IFNOT ||
+ (comparison == NONE && !expect))) ||
+ (value &&
+ (comparison == IF ||
+ (comparison == NONE && expect)))) {
+ offset = from_end == NIL ? i + start : end - i - 1;
+ break;
+ }
+ }
+
+ if (from_end != NIL && !istring)
+ LispFree(objects);
+
+ return (offset == -1 ? NIL : function == FIND ? element : FIXNUM(offset));
+}
+
+LispObj *
+Lisp_Pop(LispBuiltin *builtin)
+/*
+ pop place
+ */
+{
+ LispObj *result, *value;
+
+ LispObj *place;
+
+ place = ARGUMENT(0);
+
+ if (SYMBOLP(place)) {
+ result = LispGetVar(place);
+ if (result == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
+ CHECK_CONSTANT(place);
+ if (result != NIL) {
+ CHECK_CONS(result);
+ value = CDR(result);
+ result = CAR(result);
+ }
+ else
+ value = NIL;
+ LispSetVar(place, value);
+ }
+ else {
+ GC_ENTER();
+ LispObj quote;
+
+ result = EVAL(place);
+ if (result != NIL) {
+ CHECK_CONS(result);
+ value = CDR(result);
+ GC_PROTECT(value);
+ result = CAR(result);
+ }
+ else
+ value = NIL;
+ quote.type = LispQuote_t;
+ quote.data.quote = value;
+ APPLY2(Osetf, place, &quote);
+ GC_LEAVE();
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Position(LispBuiltin *builtin)
+/*
+ position item sequence &key from-end test test-not start end key
+ */
+{
+ return (LispFindOrPosition(builtin, POSITION, NONE));
+}
+
+LispObj *
+Lisp_PositionIf(LispBuiltin *builtin)
+/*
+ position-if predicate sequence &key from-end start end key
+ */
+{
+ return (LispFindOrPosition(builtin, POSITION, IF));
+}
+
+LispObj *
+Lisp_PositionIfNot(LispBuiltin *builtin)
+/*
+ position-if-not predicate sequence &key from-end start end key
+ */
+{
+ return (LispFindOrPosition(builtin, POSITION, IFNOT));
+}
+
+LispObj *
+Lisp_Proclaim(LispBuiltin *builtin)
+/*
+ proclaim declaration
+ */
+{
+ LispObj *arguments, *object;
+ char *operation;
+
+ LispObj *declaration;
+
+ declaration = ARGUMENT(0);
+
+ CHECK_CONS(declaration);
+
+ arguments = declaration;
+ object = CAR(arguments);
+ CHECK_SYMBOL(object);
+
+ operation = ATOMID(object);
+ if (strcmp(operation, "SPECIAL") == 0) {
+ for (arguments = CDR(arguments); CONSP(arguments);
+ arguments = CDR(arguments)) {
+ object = CAR(arguments);
+ CHECK_SYMBOL(object);
+ LispProclaimSpecial(object, NULL, NIL);
+ }
+ }
+ else if (strcmp(operation, "TYPE") == 0) {
+ /* XXX no type checking yet, but should be added */
+ }
+ /* else do nothing */
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Prog1(LispBuiltin *builtin)
+/*
+ prog1 first &rest body
+ */
+{
+ GC_ENTER();
+ LispObj *result;
+
+ LispObj *first, *body;
+
+ body = ARGUMENT(1);
+ first = ARGUMENT(0);
+
+ result = EVAL(first);
+
+ GC_PROTECT(result);
+ for (; CONSP(body); body = CDR(body))
+ (void)EVAL(CAR(body));
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Prog2(LispBuiltin *builtin)
+/*
+ prog2 first second &rest body
+ */
+{
+ GC_ENTER();
+ LispObj *result;
+
+ LispObj *first, *second, *body;
+
+ body = ARGUMENT(2);
+ second = ARGUMENT(1);
+ first = ARGUMENT(0);
+
+ (void)EVAL(first);
+ result = EVAL(second);
+ GC_PROTECT(result);
+ for (; CONSP(body); body = CDR(body))
+ (void)EVAL(CAR(body));
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Progn(LispBuiltin *builtin)
+/*
+ progn &rest body
+ */
+{
+ LispObj *result = NIL;
+
+ LispObj *body;
+
+ body = ARGUMENT(0);
+
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+
+ return (result);
+}
+
+/*
+ * This does what I believe is the expected behaviour (or at least
+ * acceptable for the the interpreter), if the code being executed
+ * ever tries to change/bind a progv symbol, the symbol state will
+ * be restored when exiting the progv block, so, code like:
+ * (progv '(*x*) '(1) (defvar *x* 10))
+ * when exiting the block, will have *x* unbound, and not a dynamic
+ * symbol; if it was already bound, will have the old value.
+ * Symbols already dynamic can be freely changed, even unbounded in
+ * the progv block.
+ */
+LispObj *
+Lisp_Progv(LispBuiltin *builtin)
+/*
+ progv symbols values &rest body
+ */
+{
+ GC_ENTER();
+ int head = lisp__data.env.length, i, count, ostk[32], *offsets;
+ LispObj *result, *list, *symbol, *value, **presult, **psymbols, **pbody;
+ int jumped, *pjumped, *pcount, **poffsets;
+ char fstk[32], *flags, **pflags;
+ LispBlock *block;
+ LispAtom *atom;
+
+ LispObj *symbols, *values, *body;
+
+ /* Possible states */
+#define DYNAMIC_SYMBOL 1
+#define GLOBAL_SYMBOL 2
+#define UNBOUND_SYMBOL 3
+
+ body = ARGUMENT(2);
+ values = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ /* get symbol names */
+ symbols = EVAL(symbols);
+ GC_PROTECT(symbols);
+
+ /* get symbol values */
+ values = EVAL(values);
+ GC_PROTECT(values);
+
+ /* use variables */
+ pbody = &body;
+ psymbols = &symbols;
+ presult = &result;
+ pjumped = &jumped;
+ poffsets = &offsets;
+ pcount = &count;
+ pflags = &flags;
+
+ /* count/check symbols and allocate space to remember symbol state */
+ for (count = 0, list = symbols; CONSP(list); count++, list = CDR(list)) {
+ symbol = CAR(list);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONSTANT(symbol);
+ }
+ if (count > sizeof(fstk)) {
+ flags = LispMalloc(count);
+ offsets = LispMalloc(count * sizeof(int));
+ }
+ else {
+ flags = &fstk[0];
+ offsets = &ostk[0];
+ }
+
+ /* store flags and save old value if required */
+ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
+ atom = CAR(list)->data.atom;
+ if (atom->dyn)
+ flags[i] = DYNAMIC_SYMBOL;
+ else if (atom->a_object) {
+ flags[i] = GLOBAL_SYMBOL;
+ offsets[i] = lisp__data.protect.length;
+ GC_PROTECT(atom->property->value);
+ }
+ else
+ flags[i] = UNBOUND_SYMBOL;
+ }
+
+ /* bind the symbols */
+ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
+ symbol = CAR(list);
+ atom = symbol->data.atom;
+ if (CONSP(values)) {
+ value = CAR(values);
+ values = CDR(values);
+ }
+ else
+ value = NIL;
+ if (flags[i] != DYNAMIC_SYMBOL) {
+ if (!atom->a_object)
+ LispSetAtomObjectProperty(atom, value);
+ else
+ SETVALUE(atom, value);
+ }
+ else
+ LispAddVar(symbol, value);
+ }
+ /* bind dynamic symbols */
+ lisp__data.env.head = lisp__data.env.length;
+
+ jumped = 0;
+ result = NIL;
+ block = LispBeginBlock(NIL, LispBlockProtect);
+ if (setjmp(block->jmp) == 0) {
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+ }
+
+ /* restore symbols */
+ for (i = 0, list = symbols; i < count; i++, list = CDR(list)) {
+ symbol = CAR(list);
+ atom = symbol->data.atom;
+ if (flags[i] != DYNAMIC_SYMBOL) {
+ if (flags[i] == UNBOUND_SYMBOL)
+ LispUnsetVar(symbol);
+ else {
+ /* restore global symbol value */
+ LispSetAtomObjectProperty(atom, lisp__data.protect.objects
+ [offsets[i]]);
+ atom->dyn = 0;
+ }
+ }
+ }
+ /* unbind dynamic symbols */
+ lisp__data.env.head = lisp__data.env.length = head;
+ GC_LEAVE();
+
+ if (count > sizeof(fstk)) {
+ LispFree(flags);
+ LispFree(offsets);
+ }
+
+ LispEndBlock(block);
+ if (!lisp__data.destroyed) {
+ if (jumped)
+ result = lisp__data.block.block_ret;
+ }
+ else {
+ /* check if there is an unwind-protect block */
+ LispBlockUnwind(NULL);
+
+ /* no unwind-protect block, return to the toplevel */
+ LispDestroy(".");
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Provide(LispBuiltin *builtin)
+/*
+ provide module
+ */
+{
+ LispObj *module, *obj;
+
+ module = ARGUMENT(0);
+
+ CHECK_STRING(module);
+ for (obj = MOD; obj != NIL; obj = CDR(obj)) {
+ if (STRLEN(CAR(obj)) == STRLEN(module) &&
+ memcmp(THESTR(CAR(obj)), THESTR(module), STRLEN(module)) == 0)
+ return (module);
+ }
+
+ if (MOD == NIL)
+ MOD = CONS(module, NIL);
+ else {
+ RPLACD(MOD, CONS(CAR(MOD), CDR(MOD)));
+ RPLACA(MOD, module);
+ }
+
+ LispSetVar(lisp__data.modules, MOD);
+
+ return (MOD);
+}
+
+LispObj *
+Lisp_Push(LispBuiltin *builtin)
+/*
+ push item place
+ */
+{
+ LispObj *result, *list;
+
+ LispObj *item, *place;
+
+ place = ARGUMENT(1);
+ item = ARGUMENT(0);
+
+ item = EVAL(item);
+
+ if (SYMBOLP(place)) {
+ list = LispGetVar(place);
+ if (list == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
+ CHECK_CONSTANT(place);
+ LispSetVar(place, result = CONS(item, list));
+ }
+ else {
+ GC_ENTER();
+ LispObj quote;
+
+ list = EVAL(place);
+ result = CONS(item, list);
+ GC_PROTECT(result);
+ quote.type = LispQuote_t;
+ quote.data.quote = result;
+ APPLY2(Osetf, place, &quote);
+ GC_LEAVE();
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Pushnew(LispBuiltin *builtin)
+/*
+ pushnew item place &key key test test-not
+ */
+{
+ GC_ENTER();
+ LispObj *result, *list;
+
+ LispObj *item, *place, *key, *test, *test_not;
+
+ test_not = ARGUMENT(4);
+ test = ARGUMENT(3);
+ key = ARGUMENT(2);
+ place = ARGUMENT(1);
+ item = ARGUMENT(0);
+
+ /* Evaluate place */
+ if (SYMBOLP(place)) {
+ list = LispGetVar(place);
+ if (list == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
+ /* Do error checking now. */
+ CHECK_CONSTANT(place);
+ }
+ else
+ /* It is possible that list is not gc protected? */
+ list = EVAL(place);
+
+ item = EVAL(item);
+ GC_PROTECT(item);
+ if (key != UNSPEC) {
+ key = EVAL(key);
+ GC_PROTECT(key);
+ }
+ if (test != UNSPEC) {
+ test = EVAL(test);
+ GC_PROTECT(test);
+ }
+ else if (test_not != UNSPEC) {
+ test_not = EVAL(test_not);
+ GC_PROTECT(test_not);
+ }
+
+ result = LispAdjoin(builtin, item, list, key, test, test_not);
+
+ /* Item already in list */
+ if (result == list) {
+ GC_LEAVE();
+
+ return (result);
+ }
+
+ if (SYMBOLP(place)) {
+ CHECK_CONSTANT(place);
+ LispSetVar(place, result);
+ }
+ else {
+ LispObj quote;
+
+ GC_PROTECT(result);
+ quote.type = LispQuote_t;
+ quote.data.quote = result;
+ APPLY2(Osetf, place, &quote);
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Quit(LispBuiltin *builtin)
+/*
+ quit &optional status
+ */
+{
+ int status = 0;
+ LispObj *ostatus;
+
+ ostatus = ARGUMENT(0);
+
+ if (FIXNUMP(ostatus))
+ status = (int)FIXNUM_VALUE(ostatus);
+ else if (ostatus != UNSPEC)
+ LispDestroy("%s: bad exit status argument %s",
+ STRFUN(builtin), STROBJ(ostatus));
+
+ exit(status);
+}
+
+LispObj *
+Lisp_Quote(LispBuiltin *builtin)
+/*
+ quote object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (object);
+}
+
+LispObj *
+Lisp_Replace(LispBuiltin *builtin)
+/*
+ replace sequence1 sequence2 &key start1 end1 start2 end2
+ */
+{
+ long length, length1, length2, start1, end1, start2, end2;
+ LispObj *sequence1, *sequence2, *ostart1, *oend1, *ostart2, *oend2;
+
+ oend2 = ARGUMENT(5);
+ ostart2 = ARGUMENT(4);
+ oend1 = ARGUMENT(3);
+ ostart1 = ARGUMENT(2);
+ sequence2 = ARGUMENT(1);
+ sequence1 = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence1, ostart1, oend1,
+ &start1, &end1, &length1);
+ LispCheckSequenceStartEnd(builtin, sequence2, ostart2, oend2,
+ &start2, &end2, &length2);
+
+ if (start1 == end1 || start2 == end2)
+ return (sequence1);
+
+ length = end1 - start1;
+ if (length > end2 - start2)
+ length = end2 - start2;
+
+ if (STRINGP(sequence1)) {
+ CHECK_STRING_WRITABLE(sequence1);
+ if (!STRINGP(sequence2))
+ LispDestroy("%s: cannot store %s in %s",
+ STRFUN(builtin), STROBJ(sequence2), THESTR(sequence1));
+
+ memmove(THESTR(sequence1) + start1, THESTR(sequence2) + start2, length);
+ }
+ else {
+ int i;
+ LispObj *from, *to;
+
+ if (ARRAYP(sequence1))
+ sequence1 = sequence1->data.array.list;
+ if (ARRAYP(sequence2))
+ sequence2 = sequence2->data.array.list;
+
+ /* adjust pointers */
+ for (i = 0, from = sequence2; i < start2; i++, from = CDR(from))
+ ;
+ for (i = 0, to = sequence1; i < start1; i++, to = CDR(to))
+ ;
+
+ /* copy data */
+ for (i = 0; i < length; i++, from = CDR(from), to = CDR(to))
+ RPLACA(to, CAR(from));
+ }
+
+ return (sequence1);
+}
+
+static LispObj *
+LispDeleteOrRemoveDuplicates(LispBuiltin *builtin, int function)
+/*
+ delete-duplicates sequence &key from-end test test-not start end key
+ remove-duplicates sequence &key from-end test test-not start end key
+ */
+{
+ GC_ENTER();
+ int code, expect, value = 0;
+ long i, j, start, end, length, count;
+ LispObj *lambda, *result, *cons, *compare;
+
+ LispObj *sequence, *from_end, *test, *test_not, *ostart, *oend, *key;
+
+ key = ARGUMENT(6);
+ oend = ARGUMENT(5);
+ ostart = ARGUMENT(4);
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ from_end = ARGUMENT(1);
+ if (from_end == UNSPEC)
+ from_end = NIL;
+ sequence = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ /* Check if need to do something */
+ if (start == end)
+ return (sequence);
+
+ CHECK_TEST();
+
+ /* Initialize */
+ count = 0;
+
+ result = cons = NIL;
+ if (STRINGP(sequence)) {
+ char *ptr, *string, *buffer = LispMalloc(length + 1);
+
+ /* Use same code, update start/end offsets */
+ if (from_end != NIL) {
+ i = length - start;
+ start = length - end;
+ end = i;
+ }
+
+ if (from_end == NIL)
+ string = THESTR(sequence);
+ else {
+ /* Make a reversed copy of the sequence */
+ string = LispMalloc(length + 1);
+ for (ptr = THESTR(sequence) + length - 1, i = 0; i < length; i++)
+ string[i] = *ptr--;
+ string[i] = '\0';
+ }
+
+ ptr = buffer;
+ /* Copy leading bytes */
+ for (i = 0; i < start; i++)
+ *ptr++ = string[i];
+
+ compare = SCHAR(string[i]);
+ if (key != UNSPEC)
+ compare = APPLY1(key, compare);
+ result = cons = CONS(compare, NIL);
+ GC_PROTECT(result);
+ for (++i; i < end; i++) {
+ compare = SCHAR(string[i]);
+ if (key != UNSPEC)
+ compare = APPLY1(key, compare);
+ RPLACD(cons, CONS(compare, NIL));
+ cons = CDR(cons);
+ }
+
+ for (i = start; i < end; i++, result = CDR(result)) {
+ compare = CAR(result);
+ for (j = i + 1, cons = CDR(result); j < end; j++, cons = CDR(cons)) {
+ value = FCOMPARE(lambda, compare, CAR(cons), code);
+ if (value == expect)
+ break;
+ }
+ if (value != expect)
+ *ptr++ = string[i];
+ else
+ ++count;
+ }
+
+ if (count) {
+ /* Copy ending bytes */
+ for (; i <= length; i++) /* Also copy the ending nul */
+ *ptr++ = string[i];
+
+ if (from_end == NIL)
+ ptr = buffer;
+ else {
+ for (i = 0, ptr = buffer + strlen(buffer);
+ ptr > buffer;
+ i++)
+ string[i] = *--ptr;
+ string[i] = '\0';
+ ptr = string;
+ LispFree(buffer);
+ }
+ if (function == REMOVE)
+ result = STRING2(ptr);
+ else {
+ CHECK_STRING_WRITABLE(sequence);
+ result = sequence;
+ free(THESTR(result));
+ THESTR(result) = ptr;
+ LispMused(ptr);
+ }
+ }
+ else {
+ result = sequence;
+ if (from_end != NIL)
+ LispFree(string);
+ }
+ }
+ else {
+ long xlength = end - start;
+ LispObj *list, *object, **kobjects = NULL, **xobjects;
+ LispObj **objects = LispMalloc(sizeof(LispObj*) * xlength);
+
+ if (!CONSP(sequence))
+ object = sequence->data.array.list;
+ else
+ object = sequence;
+ list = object;
+
+ for (i = 0; i < start; i++)
+ object = CDR(object);
+
+ /* Put data in a vector */
+ if (from_end == NIL) {
+ for (i = 0; i < xlength; i++, object = CDR(object))
+ objects[i] = CAR(object);
+ }
+ else {
+ for (i = xlength - 1; i >= 0; i--, object = CDR(object))
+ objects[i] = CAR(object);
+ }
+
+ /* Apply key predicate if required */
+ if (key != UNSPEC) {
+ kobjects = LispMalloc(sizeof(LispObj*) * xlength);
+ for (i = 0; i < xlength; i++) {
+ kobjects[i] = APPLY1(key, objects[i]);
+ GC_PROTECT(kobjects[i]);
+ }
+ xobjects = kobjects;
+ }
+ else
+ xobjects = objects;
+
+ /* Check if needs to remove something */
+ for (i = 0; i < xlength; i++) {
+ compare = xobjects[i];
+ for (j = i + 1; j < xlength; j++) {
+ value = FCOMPARE(lambda, compare, xobjects[j], code);
+ if (value == expect) {
+ objects[i] = NULL;
+ ++count;
+ break;
+ }
+ }
+ }
+
+ if (count) {
+ /* Create/set result list */
+ object = list;
+
+ if (start) {
+ /* Skip first elements of resulting list */
+ if (function == REMOVE) {
+ result = cons = CONS(CAR(object), NIL);
+ GC_PROTECT(result);
+ for (i = 1, object = CDR(object);
+ i < start;
+ i++, object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ else {
+ result = cons = object;
+ for (i = 1; i < start; i++, cons = CDR(cons))
+ ;
+ }
+ }
+ else if (function == DELETE)
+ result = list;
+
+ /* Skip initial removed elements */
+ if (function == REMOVE) {
+ for (i = 0; objects[i] == NULL && i < xlength; i++)
+ ;
+ }
+ else
+ i = 0;
+
+ if (i < xlength) {
+ int xstart, xlimit, xinc;
+
+ if (from_end == NIL) {
+ xstart = i;
+ xlimit = xlength;
+ xinc = 1;
+ }
+ else {
+ xstart = xlength - 1;
+ xlimit = i - 1;
+ xinc = -1;
+ }
+
+ if (function == REMOVE) {
+ for (i = xstart; i != xlimit; i += xinc) {
+ if (objects[i] != NULL) {
+ if (result == NIL) {
+ result = cons = CONS(objects[i], NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(objects[i], NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+ else {
+ /* Delete duplicates */
+ for (i = xstart; i != xlimit; i += xinc) {
+ if (objects[i] == NULL) {
+ if (cons == NIL) {
+ if (CONSP(CDR(result))) {
+ RPLACA(result, CADR(result));
+ RPLACD(result, CDDR(result));
+ }
+ else {
+ RPLACA(result, CDR(result));
+ RPLACD(result, NIL);
+ }
+ }
+ else {
+ if (CONSP(CDR(cons)))
+ RPLACD(cons, CDDR(cons));
+ else
+ RPLACD(cons, NIL);
+ }
+ }
+ else {
+ if (cons == NIL)
+ cons = result;
+ else
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+ if (end < length && function == REMOVE) {
+ for (i = start; i < end; i++, object = CDR(object))
+ ;
+ if (result == NIL) {
+ result = cons = CONS(CAR(object), NIL);
+ GC_PROTECT(result);
+ ++i;
+ object = CDR(object);
+ }
+ for (; i < length; i++, object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ else
+ result = sequence;
+ LispFree(objects);
+ if (key != UNSPEC)
+ LispFree(kobjects);
+
+ if (count && !CONSP(sequence)) {
+ if (function == REMOVE)
+ result = VECTOR(result);
+ else {
+ length = FIXNUM_VALUE(CAR(sequence->data.array.dim)) - count;
+ CAR(sequence->data.array.dim) = FIXNUM(length);
+ result = sequence;
+ }
+ }
+ }
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_RemoveDuplicates(LispBuiltin *builtin)
+/*
+ remove-duplicates sequence &key from-end test test-not start end key
+ */
+{
+ return (LispDeleteOrRemoveDuplicates(builtin, REMOVE));
+}
+
+static LispObj *
+LispDeleteRemoveXSubstitute(LispBuiltin *builtin,
+ int function, int comparison)
+/*
+ delete item sequence &key from-end test test-not start end count key
+ delete-if predicate sequence &key from-end start end count key
+ delete-if-not predicate sequence &key from-end start end count key
+ remove item sequence &key from-end test test-not start end count key
+ remove-if predicate sequence &key from-end start end count key
+ remove-if-not predicate sequence &key from-end start end count key
+ substitute newitem olditem sequence &key from-end test test-not start end count key
+ substitute-if newitem test sequence &key from-end start end count key
+ substitute-if-not newitem test sequence &key from-end start end count key
+ nsubstitute newitem olditem sequence &key from-end test test-not start end count key
+ nsubstitute-if newitem test sequence &key from-end start end count key
+ nsubstitute-if-not newitem test sequence &key from-end start end count key
+ */
+{
+ GC_ENTER();
+ int code, expect, value, inplace, substitute;
+ long i, j, start, end, length, copy, count, xstart, xend, xinc, xlength;
+
+ LispObj *result, *compare;
+
+ LispObj *item, *newitem, *lambda, *sequence, *from_end,
+ *test, *test_not, *ostart, *oend, *ocount, *key;
+
+ substitute = function == SUBSTITUTE || function == NSUBSTITUTE;
+ if (!substitute)
+ i = comparison == NONE ? 8 : 6;
+ else /* substitute */
+ i = comparison == NONE ? 9 : 7;
+
+ /* Get function arguments */
+ key = ARGUMENT(i); --i;
+ ocount = ARGUMENT(i); --i;
+ oend = ARGUMENT(i); --i;
+ ostart = ARGUMENT(i); --i;
+ if (comparison == NONE) {
+ test_not = ARGUMENT(i); --i;
+ test = ARGUMENT(i); --i;
+ }
+ else
+ test_not = test = UNSPEC;
+ from_end = ARGUMENT(i); --i;
+ if (from_end == UNSPEC)
+ from_end = NIL;
+ sequence = ARGUMENT(i); --i;
+ if (comparison != NONE) {
+ lambda = ARGUMENT(i); --i;
+ if (substitute)
+ newitem = ARGUMENT(0);
+ else
+ newitem = NIL;
+ item = NIL;
+ }
+ else {
+ lambda = NIL;
+ if (substitute) {
+ item = ARGUMENT(1);
+ newitem = ARGUMENT(0);
+ }
+ else {
+ item = ARGUMENT(0);
+ newitem = NIL;
+ }
+ }
+
+ /* Check if argument is a valid sequence, and if start/end
+ * are correctly specified. */
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ /* Check count argument */
+ if (ocount == UNSPEC) {
+ count = length;
+ /* Doesn't matter, but left to right should be slightly faster */
+ from_end = NIL;
+ }
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+
+ /* Check if need to do something */
+ if (start == end || count == 0)
+ return (sequence);
+
+ CHECK_TEST_0();
+
+ /* Resolve comparison function, and expected result of comparison */
+ if (comparison == NONE) {
+ if (test_not == UNSPEC) {
+ if (test == UNSPEC)
+ lambda = Oeql;
+ else
+ lambda = test;
+ expect = 1;
+ }
+ else {
+ lambda = test_not;
+ expect = 0;
+ }
+ FUNCTION_CHECK(lambda);
+ }
+ else
+ expect = comparison == IFNOT ? 0 : 1;
+
+ /* Check for fast path to comparison function */
+ code = FCODE(lambda);
+
+ /* Initialize for loop */
+ copy = count;
+ result = sequence;
+ inplace = function == DELETE || function == NSUBSTITUTE;
+ xlength = end - start;
+
+ /* String is easier */
+ if (STRINGP(sequence)) {
+ char *buffer, *string;
+
+ if (comparison == NONE) {
+ CHECK_SCHAR(item);
+ }
+ if (substitute) {
+ CHECK_SCHAR(newitem);
+ }
+
+ if (from_end == NIL) {
+ xstart = start;
+ xend = end;
+ xinc = 1;
+ }
+ else {
+ xstart = end - 1;
+ xend = start - 1;
+ xinc = -1;
+ }
+
+ string = THESTR(sequence);
+ buffer = LispMalloc(length + 1);
+
+ /* Copy leading bytes, if any */
+ for (i = 0; i < start; i++)
+ buffer[i] = string[i];
+
+ for (j = xstart; i != xend && count > 0; i += xinc) {
+ compare = SCHAR(string[i]);
+ if (key != UNSPEC) {
+ compare = APPLY1(key, compare);
+ /* Value returned by the key predicate may not be protected */
+ GC_PROTECT(compare);
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ /* Unprotect value returned by the key predicate */
+ GC_LEAVE();
+ }
+ else {
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ }
+
+ if (value != expect) {
+ buffer[j] = string[i];
+ j += xinc;
+ }
+ else {
+ if (substitute) {
+ buffer[j] = SCHAR_VALUE(newitem);
+ j += xinc;
+ }
+ else
+ --count;
+ }
+ }
+
+ if (count != copy && from_end != NIL)
+ memmove(buffer + start, buffer + copy - count, count);
+
+ /* Copy remaining bytes, if any */
+ for (; i < length; i++, j++)
+ buffer[j] = string[i];
+ buffer[j] = '\0';
+
+ xlength = length - (copy - count);
+ if (inplace) {
+ CHECK_STRING_WRITABLE(sequence);
+ /* result is a pointer to sequence */
+ LispFree(THESTR(sequence));
+ LispMused(buffer);
+ THESTR(sequence) = buffer;
+ STRLEN(sequence) = xlength;
+ }
+ else
+ result = LSTRING2(buffer, xlength);
+ }
+
+ /* If inplace, need to update CAR and CDR of sequence */
+ else {
+ LispObj *list, *object;
+ LispObj **objects = LispMalloc(sizeof(LispObj*) * xlength);
+
+ if (!CONSP(sequence))
+ list = sequence->data.array.list;
+ else
+ list = sequence;
+
+ /* Put data in a vector */
+ for (i = 0, object = list; i < start; i++)
+ object = CDR(object);
+
+ for (i = 0; i < xlength; i++, object = CDR(object))
+ objects[i] = CAR(object);
+
+ if (from_end == NIL) {
+ xstart = 0;
+ xend = xlength;
+ xinc = 1;
+ }
+ else {
+ xstart = xlength - 1;
+ xend = -1;
+ xinc = -1;
+ }
+
+ /* Check if needs to remove something */
+ for (i = xstart; i != xend && count > 0; i += xinc) {
+ compare = objects[i];
+ if (key != UNSPEC) {
+ compare = APPLY1(key, compare);
+ GC_PROTECT(compare);
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ GC_LEAVE();
+ }
+ else {
+ if (comparison == NONE)
+ value = FCOMPARE(lambda, item, compare, code);
+ else
+ value = APPLY1(lambda, compare) != NIL;
+ }
+ if (value == expect) {
+ if (substitute)
+ objects[i] = newitem;
+ else
+ objects[i] = NULL;
+ --count;
+ }
+ }
+
+ if (copy != count) {
+ LispObj *cons = NIL;
+
+ i = 0;
+ object = list;
+ if (inplace) {
+ /* While result is NIL, skip initial elements of sequence */
+ result = start ? list : NIL;
+
+ /* Skip initial elements, if any */
+ for (; i < start; i++, cons = object, object = CDR(object))
+ ;
+ }
+ /* Copy initial elements, if any */
+ else {
+ result = NIL;
+ if (start) {
+ result = cons = CONS(CAR(list), NIL);
+ GC_PROTECT(result);
+ for (++i, object = CDR(list);
+ i < start;
+ i++, object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ /* Skip initial removed elements, if any */
+ for (i = 0; objects[i] == NULL && i < xlength; i++)
+ ;
+
+ for (i = 0; i < xlength; i++, object = CDR(object)) {
+ if (objects[i]) {
+ if (inplace) {
+ if (result == NIL)
+ result = cons = object;
+ else {
+ RPLACD(cons, object);
+ cons = CDR(cons);
+ }
+ if (function == NSUBSTITUTE)
+ RPLACA(cons, objects[i]);
+ }
+ else {
+ if (result == NIL) {
+ result = cons = CONS(objects[i], NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(objects[i], NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+
+ if (inplace) {
+ if (result == NIL)
+ result = object;
+ else
+ RPLACD(cons, object);
+
+ if (!CONSP(sequence)) {
+ result = sequence;
+ CAR(result)->data.array.dim =
+ FIXNUM(length - (copy - count));
+ }
+ }
+ else if (end < length) {
+ i = end;
+ /* Copy ending elements, if any */
+ if (result == NIL) {
+ result = cons = CONS(CAR(object), NIL);
+ GC_PROTECT(result);
+ object = CDR(object);
+ i++;
+ }
+ for (; i < length; i++, object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ /* Release comparison vector */
+ LispFree(objects);
+ }
+
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Remove(LispBuiltin *builtin)
+/*
+ remove item sequence &key from-end test test-not start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, REMOVE, NONE));
+}
+
+LispObj *
+Lisp_RemoveIf(LispBuiltin *builtin)
+/*
+ remove-if predicate sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, REMOVE, IF));
+}
+
+LispObj *
+Lisp_RemoveIfNot(LispBuiltin *builtin)
+/*
+ remove-if-not predicate sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, REMOVE, IFNOT));
+}
+
+LispObj *
+Lisp_Remprop(LispBuiltin *builtin)
+/*
+ remprop symbol indicator
+ */
+{
+ LispObj *symbol, *indicator;
+
+ indicator = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ return (LispRemAtomProperty(symbol->data.atom, indicator));
+}
+
+LispObj *
+Lisp_Return(LispBuiltin *builtin)
+/*
+ return &optional result
+ */
+{
+ unsigned blevel = lisp__data.block.block_level;
+
+ LispObj *result;
+
+ result = ARGUMENT(0);
+
+ while (blevel) {
+ LispBlock *block = lisp__data.block.block[--blevel];
+
+ if (block->type == LispBlockClosure)
+ /* if reached a function call */
+ break;
+ if (block->type == LispBlockTag && block->tag == NIL) {
+ lisp__data.block.block_ret = result == UNSPEC ? NIL : EVAL(result);
+ LispBlockUnwind(block);
+ BLOCKJUMP(block);
+ }
+ }
+ LispDestroy("%s: no visible NIL block", STRFUN(builtin));
+
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_ReturnFrom(LispBuiltin *builtin)
+/*
+ return-from name &optional result
+ */
+{
+ unsigned blevel = lisp__data.block.block_level;
+
+ LispObj *name, *result;
+
+ result = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ if (name != NIL && name != T && !SYMBOLP(name))
+ LispDestroy("%s: %s is not a valid block name",
+ STRFUN(builtin), STROBJ(name));
+
+ while (blevel) {
+ LispBlock *block = lisp__data.block.block[--blevel];
+
+ if (name == block->tag &&
+ (block->type == LispBlockTag || block->type == LispBlockClosure)) {
+ lisp__data.block.block_ret = result == UNSPEC ? NIL : EVAL(result);
+ LispBlockUnwind(block);
+ BLOCKJUMP(block);
+ }
+ if (block->type == LispBlockClosure)
+ /* can use return-from only in the current function */
+ break;
+ }
+ LispDestroy("%s: no visible block named %s",
+ STRFUN(builtin), STROBJ(name));
+
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+static LispObj *
+LispXReverse(LispBuiltin *builtin, int inplace)
+/*
+ nreverse sequence
+ reverse sequence
+ */
+{
+ long length;
+ LispObj *list, *result = NIL;
+
+ LispObj *sequence;
+
+ sequence = ARGUMENT(0);
+
+ /* Do error checking for arrays and object type. */
+ length = LispLength(sequence);
+ if (length <= 1)
+ return (sequence);
+
+ switch (XOBJECT_TYPE(sequence)) {
+ case LispString_t: {
+ long i;
+ char *from, *to;
+
+ from = THESTR(sequence) + length - 1;
+ if (inplace) {
+ char temp;
+
+ CHECK_STRING_WRITABLE(sequence);
+ to = THESTR(sequence);
+ for (i = 0; i < length / 2; i++) {
+ temp = to[i];
+ to[i] = from[-i];
+ from[-i] = temp;
+ }
+ result = sequence;
+ }
+ else {
+ to = LispMalloc(length + 1);
+ to[length] = '\0';
+ for (i = 0; i < length; i++)
+ to[i] = from[-i];
+ result = STRING2(to);
+ }
+ } return (result);
+ case LispCons_t:
+ if (inplace) {
+ long i, j;
+ LispObj *temp;
+
+ /* For large lists this can be very slow, but for small
+ * amounts of data, this avoid allocating a buffer to
+ * to store the CAR of the sequence. This is only done
+ * to not destroy the contents of a variable.
+ */
+ for (i = 0, list = sequence;
+ i < (length + 1) / 2;
+ i++, list = CDR(list))
+ ;
+ length /= 2;
+ for (i = 0; i < length; i++, list = CDR(list)) {
+ for (j = length - i - 1, result = sequence;
+ j > 0;
+ j--, result = CDR(result))
+ ;
+ temp = CAR(list);
+ RPLACA(list, CAR(result));
+ RPLACA(result, temp);
+ }
+ return (sequence);
+ }
+ list = sequence;
+ break;
+ case LispArray_t:
+ if (inplace) {
+ sequence->data.array.list =
+ LispReverse(sequence->data.array.list);
+ return (sequence);
+ }
+ list = sequence->data.array.list;
+ break;
+ default: /* LispNil_t */
+ return (result);
+ }
+
+ {
+ GC_ENTER();
+ LispObj *cons;
+
+ result = cons = CONS(CAR(list), NIL);
+ GC_PROTECT(result);
+ for (list = CDR(list); CONSP(list); list = CDR(list)) {
+ RPLACD(cons, CONS(CAR(list), NIL));
+ cons = CDR(cons);
+ }
+ result = LispReverse(result);
+
+ GC_LEAVE();
+ }
+
+ if (ARRAYP(sequence)) {
+ list = result;
+
+ result = LispNew(list, NIL);
+ result->type = LispArray_t;
+ result->data.array.list = list;
+ result->data.array.dim = sequence->data.array.dim;
+ result->data.array.rank = sequence->data.array.rank;
+ result->data.array.type = sequence->data.array.type;
+ result->data.array.zero = sequence->data.array.zero;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Reverse(LispBuiltin *builtin)
+/*
+ reverse sequence
+ */
+{
+ return (LispXReverse(builtin, 0));
+}
+
+LispObj *
+Lisp_Rplaca(LispBuiltin *builtin)
+/*
+ rplaca place value
+ */
+{
+ LispObj *place, *value;
+
+ value = ARGUMENT(1);
+ place = ARGUMENT(0);
+
+ CHECK_CONS(place);
+ RPLACA(place, value);
+
+ return (place);
+}
+
+LispObj *
+Lisp_Rplacd(LispBuiltin *builtin)
+/*
+ rplacd place value
+ */
+{
+ LispObj *place, *value;
+
+ value = ARGUMENT(1);
+ place = ARGUMENT(0);
+
+ CHECK_CONS(place);
+ RPLACD(place, value);
+
+ return (place);
+}
+
+LispObj *
+Lisp_Search(LispBuiltin *builtin)
+/*
+ search sequence1 sequence2 &key from-end test test-not key start1 start2 end1 end2
+ */
+{
+ int code = 0, expect, value;
+ long start1, start2, end1, end2, length1, length2, off1, off2, offset = -1;
+ LispObj *cmp1, *cmp2, *list1 = NIL, *lambda;
+ SeqInfo seq1, seq2;
+
+ LispObj *sequence1, *sequence2, *from_end, *test, *test_not,
+ *key, *ostart1, *ostart2, *oend1, *oend2;
+
+ oend2 = ARGUMENT(9);
+ oend1 = ARGUMENT(8);
+ ostart2 = ARGUMENT(7);
+ ostart1 = ARGUMENT(6);
+ key = ARGUMENT(5);
+ test_not = ARGUMENT(4);
+ test = ARGUMENT(3);
+ from_end = ARGUMENT(2);
+ sequence2 = ARGUMENT(1);
+ sequence1 = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence1, ostart1, oend1,
+ &start1, &end1, &length1);
+ LispCheckSequenceStartEnd(builtin, sequence2, ostart2, oend2,
+ &start2, &end2, &length2);
+
+ /* Check for special conditions */
+ if (start1 == end1)
+ return (FIXNUM(end2));
+ else if (start2 == end2)
+ return (start1 == end1 ? FIXNUM(start2) : NIL);
+
+ CHECK_TEST();
+
+ if (from_end == UNSPEC)
+ from_end = NIL;
+
+ SETSEQ(seq1, sequence1);
+ SETSEQ(seq2, sequence2);
+
+ length1 = end1 - start1;
+ length2 = end2 - start2;
+
+ /* update start of sequences */
+ if (start1) {
+ if (seq1.type == LispString_t)
+ seq1.data.string += start1;
+ else {
+ for (cmp1 = seq1.data.list; start1; cmp1 = CDR(cmp1), --start1)
+ ;
+ seq1.data.list = cmp1;
+ }
+ end1 = length1;
+ }
+ if (start2) {
+ if (seq2.type == LispString_t)
+ seq2.data.string += start2;
+ else {
+ for (cmp2 = seq2.data.list; start2; cmp2 = CDR(cmp2), --start2)
+ ;
+ seq2.data.list = cmp2;
+ }
+ end2 = length2;
+ }
+
+ /* easier case */
+ if (from_end == NIL) {
+ LispObj *list2 = NIL;
+
+ /* while a match is possible */
+ while (end2 - start2 >= length1) {
+
+ /* prepare to search */
+ off1 = 0;
+ off2 = start2;
+ if (seq1.type != LispString_t)
+ list1 = seq1.data.list;
+ if (seq2.type != LispString_t)
+ list2 = seq2.data.list;
+
+ /* for every element that must match in sequence1 */
+ while (off1 < length1) {
+ if (seq1.type == LispString_t)
+ cmp1 = SCHAR(seq1.data.string[off1]);
+ else
+ cmp1 = CAR(list1);
+ if (seq2.type == LispString_t)
+ cmp2 = SCHAR(seq2.data.string[off2]);
+ else
+ cmp2 = CAR(list2);
+ if (key != UNSPEC) {
+ cmp1 = APPLY1(key, cmp1);
+ cmp2 = APPLY1(key, cmp2);
+ }
+
+ /* compare elements */
+ value = FCOMPARE(lambda, cmp1, cmp2, code);
+ if (value != expect)
+ break;
+
+ /* update offsets/sequence pointers */
+ ++off1;
+ ++off2;
+ if (seq1.type != LispString_t)
+ list1 = CDR(list1);
+ if (seq2.type != LispString_t)
+ list2 = CDR(list2);
+ }
+
+ /* if everything matched */
+ if (off1 == end1) {
+ offset = off2 - length1;
+ break;
+ }
+
+ /* update offset/sequence2 pointer */
+ ++start2;
+ if (seq2.type != LispString_t)
+ seq2.data.list = CDR(seq2.data.list);
+ }
+ }
+ else {
+ /* allocate vector if required, only list2 requires it.
+ * list1 can be traversed forward */
+ if (seq2.type != LispString_t) {
+ cmp2 = seq2.data.list;
+ seq2.data.vector = LispMalloc(sizeof(LispObj*) * length2);
+ for (off2 = 0; off2 < end2; off2++, cmp2 = CDR(cmp2))
+ seq2.data.vector[off2] = CAR(cmp2);
+ }
+
+ /* while a match is possible */
+ while (end2 >= length1) {
+
+ /* prepare to search */
+ off1 = 0;
+ off2 = end2 - length1;
+ if (seq1.type != LispString_t)
+ list1 = seq1.data.list;
+
+ /* for every element that must match in sequence1 */
+ while (off1 < end1) {
+ if (seq1.type == LispString_t)
+ cmp1 = SCHAR(seq1.data.string[off1]);
+ else
+ cmp1 = CAR(list1);
+ if (seq2.type == LispString_t)
+ cmp2 = SCHAR(seq2.data.string[off2]);
+ else
+ cmp2 = seq2.data.vector[off2];
+ if (key != UNSPEC) {
+ cmp1 = APPLY1(key, cmp1);
+ cmp2 = APPLY1(key, cmp2);
+ }
+
+ /* Compare elements */
+ value = FCOMPARE(lambda, cmp1, cmp2, code);
+ if (value != expect)
+ break;
+
+ /* Update offsets */
+ ++off1;
+ ++off2;
+ if (seq1.type != LispString_t)
+ list1 = CDR(list1);
+ }
+
+ /* If all elements matched */
+ if (off1 == end1) {
+ offset = off2 - length1;
+ break;
+ }
+
+ /* Update offset */
+ --end2;
+ }
+
+ if (seq2.type != LispString_t)
+ LispFree(seq2.data.vector);
+ }
+
+ return (offset == -1 ? NIL : FIXNUM(offset));
+}
+
+/*
+ * ext::getenv
+ */
+LispObj *
+Lisp_Setenv(LispBuiltin *builtin)
+/*
+ setenv name value &optional overwrite
+ */
+{
+ char *name, *value;
+
+ LispObj *oname, *ovalue, *overwrite;
+
+ overwrite = ARGUMENT(2);
+ ovalue = ARGUMENT(1);
+ oname = ARGUMENT(0);
+
+ CHECK_STRING(oname);
+ name = THESTR(oname);
+
+ CHECK_STRING(ovalue);
+ value = THESTR(ovalue);
+
+ setenv(name, value, overwrite != UNSPEC && overwrite != NIL);
+ value = getenv(name);
+
+ return (value ? STRING(value) : NIL);
+}
+
+LispObj *
+Lisp_Set(LispBuiltin *builtin)
+/*
+ set symbol value
+ */
+{
+ LispAtom *atom;
+ LispObj *symbol, *value;
+
+ value = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ atom = symbol->data.atom;
+ if (atom->dyn)
+ LispSetVar(symbol, value);
+ else if (atom->watch || !atom->a_object)
+ LispSetAtomObjectProperty(atom, value);
+ else {
+ CHECK_CONSTANT(symbol);
+ SETVALUE(atom, value);
+ }
+
+ return (value);
+}
+
+LispObj *
+Lisp_SetDifference(LispBuiltin *builtin)
+/*
+ set-difference list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, SETDIFFERENCE));
+}
+
+LispObj *
+Lisp_SetExclusiveOr(LispBuiltin *builtin)
+/*
+ set-exclusive-or list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, SETEXCLUSIVEOR));
+}
+
+LispObj *
+Lisp_NsetExclusiveOr(LispBuiltin *builtin)
+/*
+ nset-exclusive-or list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, NSETEXCLUSIVEOR));
+}
+
+LispObj *
+Lisp_SetQ(LispBuiltin *builtin)
+/*
+ setq &rest form
+ */
+{
+ LispObj *result, *variable, *form;
+
+ form = ARGUMENT(0);
+
+ result = NIL;
+ for (; CONSP(form); form = CDR(form)) {
+ variable = CAR(form);
+ CHECK_SYMBOL(variable);
+ CHECK_CONSTANT(variable);
+ form = CDR(form);
+ if (!CONSP(form))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ result = EVAL(CAR(form));
+ LispSetVar(variable, result);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Psetq(LispBuiltin *builtin)
+/*
+ psetq &rest form
+ */
+{
+ GC_ENTER();
+ int base = gc__protect;
+ LispObj *value, *symbol, *list, *form;
+
+ form = ARGUMENT(0);
+
+ /* parallel setq, first pass evaluate values and basic error checking */
+ for (list = form; CONSP(list); list = CDR(list)) {
+ symbol = CAR(list);
+ CHECK_SYMBOL(symbol);
+ list = CDR(list);
+ if (!CONSP(list))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ value = EVAL(CAR(list));
+ GC_PROTECT(value);
+ }
+
+ /* second pass, assign values */
+ for (; CONSP(form); form = CDDR(form)) {
+ symbol = CAR(form);
+ CHECK_CONSTANT(symbol);
+ LispSetVar(symbol, lisp__data.protect.objects[base++]);
+ }
+ GC_LEAVE();
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Setf(LispBuiltin *builtin)
+/*
+ setf &rest form
+ */
+{
+ LispAtom *atom;
+ LispObj *setf, *place, *value, *result = NIL, *data;
+
+ LispObj *form;
+
+ form = ARGUMENT(0);
+
+ for (; CONSP(form); form = CDR(form)) {
+ place = CAR(form);
+ form = CDR(form);
+ if (!CONSP(form))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ value = CAR(form);
+
+ if (!POINTERP(place))
+ goto invalid_place;
+ if (XSYMBOLP(place)) {
+ CHECK_CONSTANT(place);
+ result = EVAL(value);
+ (void)LispSetVar(place, result);
+ }
+ else if (XCONSP(place)) {
+ /* it really should not be required to protect any object
+ * evaluated here, but is done for safety in case one of
+ * the evaluated forms returns data not gc protected, what
+ * could cause surprises if the object is garbage collected
+ * before finishing setf. */
+ GC_ENTER();
+
+ setf = CAR(place);
+ if (!SYMBOLP(setf))
+ goto invalid_place;
+ if (!CONSP(CDR(place)))
+ goto invalid_place;
+
+ value = EVAL(value);
+ GC_PROTECT(value);
+
+ atom = setf->data.atom;
+ if (atom->a_defsetf == 0) {
+ if (atom->a_defstruct &&
+ atom->property->structure.function >= 0) {
+ /* Use a default setf method for the structure field, as
+ * if this definition have been done
+ * (defsetf THE-STRUCT-FIELD (struct) (value)
+ * `(lisp::struct-store 'THE-STRUCT-FIELD ,struct ,value))
+ */
+ place = CDR(place);
+ data = CAR(place);
+ if (CONSP(CDR(place)))
+ goto invalid_place;
+ data = EVAL(data);
+ GC_PROTECT(data);
+ result = APPLY3(Ostruct_store, setf, data, value);
+ GC_LEAVE();
+ continue;
+ }
+ /* Must also expand macros */
+ else if (atom->a_function &&
+ atom->property->fun.function->funtype == LispMacro) {
+ result = LispRunSetfMacro(atom, CDR(place), value);
+ continue;
+ }
+ goto invalid_place;
+ }
+
+ place = CDR(place);
+ setf = setf->data.atom->property->setf;
+ if (SYMBOLP(setf)) {
+ LispObj *arguments, *cons;
+
+ if (!CONSP(CDR(place))) {
+ arguments = EVAL(CAR(place));
+ GC_PROTECT(arguments);
+ result = APPLY2(setf, arguments, value);
+ }
+ else if (!CONSP(CDDR(place))) {
+ arguments = EVAL(CAR(place));
+ GC_PROTECT(arguments);
+ cons = EVAL(CADR(place));
+ GC_PROTECT(cons);
+ result = APPLY3(setf, arguments, cons, value);
+ }
+ else {
+ arguments = cons = CONS(EVAL(CAR(place)), NIL);
+ GC_PROTECT(arguments);
+ for (place = CDR(place); CONSP(place); place = CDR(place)) {
+ RPLACD(cons, CONS(EVAL(CAR(place)), NIL));
+ cons = CDR(cons);
+ }
+ RPLACD(cons, CONS(value, NIL));
+ result = APPLY(setf, arguments);
+ }
+ }
+ else
+ result = LispRunSetf(atom->property->salist, setf, place, value);
+ GC_LEAVE();
+ }
+ else
+ goto invalid_place;
+ }
+
+ return (result);
+invalid_place:
+ LispDestroy("%s: %s is an invalid place", STRFUN(builtin), STROBJ(place));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_Psetf(LispBuiltin *builtin)
+/*
+ psetf &rest form
+ */
+{
+ int base;
+ GC_ENTER();
+ LispAtom *atom;
+ LispObj *setf, *place = NIL, *value, *data;
+
+ LispObj *form;
+
+ form = ARGUMENT(0);
+
+ /* parallel setf, first pass evaluate values and basic error checking */
+ base = gc__protect;
+ for (setf = form; CONSP(setf); setf = CDR(setf)) {
+ if (!POINTERP(CAR(setf)))
+ goto invalid_place;
+ setf = CDR(setf);
+ if (!CONSP(setf))
+ LispDestroy("%s: odd number of arguments", STRFUN(builtin));
+ value = EVAL(CAR(setf));
+ GC_PROTECT(value);
+ }
+
+ /* second pass, assign values */
+ for (; CONSP(form); form = CDDR(form)) {
+ place = CAR(form);
+ value = lisp__data.protect.objects[base++];
+
+ if (XSYMBOLP(place)) {
+ CHECK_CONSTANT(place);
+ (void)LispSetVar(place, value);
+ }
+ else if (XCONSP(place)) {
+ LispObj *arguments, *cons;
+ int xbase = lisp__data.protect.length;
+
+ setf = CAR(place);
+ if (!SYMBOLP(setf))
+ goto invalid_place;
+ if (!CONSP(CDR(place)))
+ goto invalid_place;
+
+ atom = setf->data.atom;
+ if (atom->a_defsetf == 0) {
+ if (atom->a_defstruct &&
+ atom->property->structure.function >= 0) {
+ place = CDR(place);
+ data = CAR(place);
+ if (CONSP(CDR(place)))
+ goto invalid_place;
+ data = EVAL(data);
+ GC_PROTECT(data);
+ (void)APPLY3(Ostruct_store, setf, data, value);
+ lisp__data.protect.length = xbase;
+ continue;
+ }
+ else if (atom->a_function &&
+ atom->property->fun.function->funtype == LispMacro) {
+ (void)LispRunSetfMacro(atom, CDR(place), value);
+ lisp__data.protect.length = xbase;
+ continue;
+ }
+ goto invalid_place;
+ }
+
+ place = CDR(place);
+ setf = setf->data.atom->property->setf;
+ if (SYMBOLP(setf)) {
+ if (!CONSP(CDR(place))) {
+ arguments = EVAL(CAR(place));
+ GC_PROTECT(arguments);
+ (void)APPLY2(setf, arguments, value);
+ }
+ else if (!CONSP(CDDR(place))) {
+ arguments = EVAL(CAR(place));
+ GC_PROTECT(arguments);
+ cons = EVAL(CADR(place));
+ GC_PROTECT(cons);
+ (void)APPLY3(setf, arguments, cons, value);
+ }
+ else {
+ arguments = cons = CONS(EVAL(CAR(place)), NIL);
+ GC_PROTECT(arguments);
+ for (place = CDR(place); CONSP(place); place = CDR(place)) {
+ RPLACD(cons, CONS(EVAL(CAR(place)), NIL));
+ cons = CDR(cons);
+ }
+ RPLACD(cons, CONS(value, NIL));
+ (void)APPLY(setf, arguments);
+ }
+ lisp__data.protect.length = xbase;
+ }
+ else
+ (void)LispRunSetf(atom->property->salist, setf, place, value);
+ }
+ else
+ goto invalid_place;
+ }
+ GC_LEAVE();
+
+ return (NIL);
+invalid_place:
+ LispDestroy("%s: %s is an invalid place", STRFUN(builtin), STROBJ(place));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_Sleep(LispBuiltin *builtin)
+/*
+ sleep seconds
+ */
+{
+ long sec, msec;
+ double value, dsec;
+
+ LispObj *seconds;
+
+ seconds = ARGUMENT(0);
+
+ value = -1.0;
+ switch (OBJECT_TYPE(seconds)) {
+ case LispFixnum_t:
+ value = FIXNUM_VALUE(seconds);
+ break;
+ case LispDFloat_t:
+ value = DFLOAT_VALUE(seconds);
+ break;
+ default:
+ break;
+ }
+
+ if (value < 0.0 || value > MOST_POSITIVE_FIXNUM)
+ LispDestroy("%s: %s is not a positive fixnum",
+ STRFUN(builtin), STROBJ(seconds));
+
+ msec = modf(value, &dsec) * 1e6;
+ sec = dsec;
+
+ if (sec)
+ sleep(sec);
+ if (msec)
+ usleep(msec);
+
+ return (NIL);
+}
+
+/*
+ * This function is called recursively, but the contents of "list2" are
+ * kept gc protected until it returns to LispSort. This is required partly
+ * because the "gc protection logic" protects an object, not the contents
+ * of the c pointer.
+ */
+static LispObj *
+LispMergeSort(LispObj *list, LispObj *predicate, LispObj *key, int code)
+{
+ int protect;
+ LispObj *list1, *list2, *left, *right, *result, *cons;
+
+ /* Check if list length is larger than 1 */
+ if (!CONSP(list) || !CONSP(CDR(list)))
+ return (list);
+
+ list1 = list2 = list;
+ for (;;) {
+ list = CDR(list);
+ if (!CONSP(list))
+ break;
+ list = CDR(list);
+ if (!CONSP(list))
+ break;
+ list2 = CDR(list2);
+ }
+ cons = list2;
+ list2 = CDR(list2);
+ RPLACD(cons, NIL);
+
+ protect = 0;
+ if (lisp__data.protect.length + 2 >= lisp__data.protect.space)
+ LispMoreProtects();
+ lisp__data.protect.objects[lisp__data.protect.length++] = list2;
+ list1 = LispMergeSort(list1, predicate, key, code);
+ list2 = LispMergeSort(list2, predicate, key, code);
+
+ left = CAR(list1);
+ right = CAR(list2);
+ if (key != UNSPEC) {
+ protect = lisp__data.protect.length;
+ left = APPLY1(key, left);
+ lisp__data.protect.objects[protect] = left;
+ right = APPLY1(key, right);
+ lisp__data.protect.objects[protect + 1] = right;
+ }
+
+ result = NIL;
+ for (;;) {
+ if ((FCOMPARE(predicate, left, right, code)) == 0 &&
+ (FCOMPARE(predicate, right, left, code)) == 1) {
+ /* right is "smaller" */
+ if (result == NIL)
+ result = list2;
+ else
+ RPLACD(cons, list2);
+ cons = list2;
+ list2 = CDR(list2);
+ if (!CONSP(list2)) {
+ RPLACD(cons, list1);
+ break;
+ }
+ right = CAR(list2);
+ if (key != UNSPEC) {
+ right = APPLY1(key, right);
+ lisp__data.protect.objects[protect + 1] = right;
+ }
+ }
+ else {
+ /* left is "smaller" */
+ if (result == NIL)
+ result = list1;
+ else
+ RPLACD(cons, list1);
+ cons = list1;
+ list1 = CDR(list1);
+ if (!CONSP(list1)) {
+ RPLACD(cons, list2);
+ break;
+ }
+ left = CAR(list1);
+ if (key != UNSPEC) {
+ left = APPLY1(key, left);
+ lisp__data.protect.objects[protect] = left;
+ }
+ }
+ }
+ if (key != UNSPEC)
+ lisp__data.protect.length = protect;
+
+ return (result);
+}
+
+/* XXX The first version made a copy of the list and then adjusted
+ * the CARs of the list. To minimize GC time now it is now doing
+ * the sort inplace. So, instead of writing just (sort variable)
+ * now it is required to write (setq variable (sort variable))
+ * if the variable should always keep all elements.
+ */
+LispObj *
+Lisp_Sort(LispBuiltin *builtin)
+/*
+ sort sequence predicate &key key
+ */
+{
+ GC_ENTER();
+ int istring, code;
+ long length;
+ char *string;
+
+ LispObj *list, *work, *cons = NULL;
+
+ LispObj *sequence, *predicate, *key;
+
+ key = ARGUMENT(2);
+ predicate = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ length = LispLength(sequence);
+ if (length < 2)
+ return (sequence);
+
+ list = sequence;
+ istring = XSTRINGP(sequence);
+ if (istring) {
+ CHECK_STRING_WRITABLE(sequence);
+ /* Convert string to list */
+ string = THESTR(sequence);
+ work = cons = CONS(SCHAR(string[0]), NIL);
+ GC_PROTECT(work);
+ for (++string; *string; ++string) {
+ RPLACD(cons, CONS(SCHAR(*string), NIL));
+ cons = CDR(cons);
+ }
+ }
+ else if (ARRAYP(list))
+ work = list->data.array.list;
+ else
+ work = list;
+
+ FUNCTION_CHECK(predicate);
+ code = FCODE(predicate);
+ work = LispMergeSort(work, predicate, key, code);
+
+ if (istring) {
+ /* Convert list to string */
+ string = THESTR(sequence);
+ for (; CONSP(work); ++string, work = CDR(work))
+ *string = SCHAR_VALUE(CAR(work));
+ }
+ else if (ARRAYP(list))
+ list->data.array.list = work;
+ else
+ sequence = work;
+ GC_LEAVE();
+
+ return (sequence);
+}
+
+LispObj *
+Lisp_Subseq(LispBuiltin *builtin)
+/*
+ subseq sequence start &optional end
+ */
+{
+ long start, end, length, seqlength;
+
+ LispObj *sequence, *ostart, *oend, *result;
+
+ oend = ARGUMENT(2);
+ ostart = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ LispCheckSequenceStartEnd(builtin, sequence, ostart, oend,
+ &start, &end, &length);
+
+ seqlength = end - start;
+
+ if (sequence == NIL)
+ result = NIL;
+ else if (XSTRINGP(sequence)) {
+ char *string = LispMalloc(seqlength + 1);
+
+ memcpy(string, THESTR(sequence) + start, seqlength);
+ string[seqlength] = '\0';
+ result = STRING2(string);
+ }
+ else {
+ GC_ENTER();
+ LispObj *object;
+
+ if (end > start) {
+ /* list or array */
+ int count;
+ LispObj *cons;
+
+ if (ARRAYP(sequence))
+ object = sequence->data.array.list;
+ else
+ object = sequence;
+ /* goto first element to copy */
+ for (count = 0; count < start; count++, object = CDR(object))
+ ;
+ result = cons = CONS(CAR(object), NIL);
+ GC_PROTECT(result);
+ for (++count, object = CDR(object); count < end; count++,
+ object = CDR(object)) {
+ RPLACD(cons, CONS(CAR(object), NIL));
+ cons = CDR(cons);
+ }
+ }
+ else
+ result = NIL;
+
+ if (ARRAYP(sequence)) {
+ object = LispNew(NIL, NIL);
+ GC_PROTECT(object);
+ object->type = LispArray_t;
+ object->data.array.list = result;
+ object->data.array.dim = CONS(FIXNUM(seqlength), NIL);
+ object->data.array.rank = 1;
+ object->data.array.type = sequence->data.array.type;
+ object->data.array.zero = length == 0;
+ result = object;
+ }
+ GC_LEAVE();
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Subsetp(LispBuiltin *builtin)
+/*
+ subsetp list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, SUBSETP));
+}
+
+
+LispObj *
+Lisp_Substitute(LispBuiltin *builtin)
+/*
+ substitute newitem olditem sequence &key from-end test test-not start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, NONE));
+}
+
+LispObj *
+Lisp_SubstituteIf(LispBuiltin *builtin)
+/*
+ substitute-if newitem test sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, IF));
+}
+
+LispObj *
+Lisp_SubstituteIfNot(LispBuiltin *builtin)
+/*
+ substitute-if-not newitem test sequence &key from-end start end count key
+ */
+{
+ return (LispDeleteRemoveXSubstitute(builtin, SUBSTITUTE, IFNOT));
+}
+
+LispObj *
+Lisp_Symbolp(LispBuiltin *builtin)
+/*
+ symbolp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (SYMBOLP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_SymbolFunction(LispBuiltin *builtin)
+/*
+ symbol-function symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+ CHECK_SYMBOL(symbol);
+
+ return (LispSymbolFunction(symbol));
+}
+
+LispObj *
+Lisp_SymbolName(LispBuiltin *builtin)
+/*
+ symbol-name symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+ CHECK_SYMBOL(symbol);
+
+ return (LispSymbolName(symbol));
+}
+
+LispObj *
+Lisp_SymbolPackage(LispBuiltin *builtin)
+/*
+ symbol-package symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+ CHECK_SYMBOL(symbol);
+
+ symbol = symbol->data.atom->package;
+
+ return (symbol ? symbol : NIL);
+}
+
+LispObj *
+Lisp_SymbolPlist(LispBuiltin *builtin)
+/*
+ symbol-plist symbol
+ */
+{
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ return (symbol->data.atom->a_property ?
+ symbol->data.atom->property->properties : NIL);
+}
+
+LispObj *
+Lisp_SymbolValue(LispBuiltin *builtin)
+/*
+ symbol-value symbol
+ */
+{
+ LispAtom *atom;
+ LispObj *symbol;
+
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+ atom = symbol->data.atom;
+ if (!atom->a_object || atom->property->value == UNBOUND) {
+ if (atom->package == lisp__data.keyword)
+ return (symbol);
+ LispDestroy("%s: the symbol %s has no value",
+ STRFUN(builtin), STROBJ(symbol));
+ }
+
+ return (atom->dyn ? LispGetVar(symbol) : atom->property->value);
+}
+
+LispObj *
+Lisp_Tagbody(LispBuiltin *builtin)
+/*
+ tagbody &rest body
+ */
+{
+ GC_ENTER();
+ int stack, lex, length;
+ LispObj *list, *body, *ptr, *tag, *labels, *map,
+ **p_list, **p_body, **p_labels;
+ LispBlock *block;
+
+ body = ARGUMENT(0);
+
+ /* Save environment information */
+ stack = lisp__data.stack.length;
+ lex = lisp__data.env.lex;
+ length = lisp__data.env.length;
+
+ /* Since the body may be large, and the code may iterate several
+ * thousand times, it is not a bad idea to avoid checking all
+ * elements of the body to verify if it is a tag. */
+ for (labels = map = NIL, ptr = body; CONSP(ptr); ptr = CDR(ptr)) {
+ tag = CAR(ptr);
+ switch (OBJECT_TYPE(tag)) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ /* Don't allow duplicated labels */
+ for (list = labels; CONSP(list); list = CDDR(list)) {
+ if (CAR(list) == tag)
+ LispDestroy("%s: tag %s specified more than once",
+ STRFUN(builtin), STROBJ(tag));
+ }
+ if (labels == NIL) {
+ labels = CONS(tag, CONS(NIL, NIL));
+ map = CDR(labels);
+ GC_PROTECT(labels);
+ }
+ else {
+ RPLACD(map, CONS(tag, CONS(NIL, NIL)));
+ map = CDDR(map);
+ }
+ break;
+ case LispCons_t:
+ /* Restart point for tag */
+ if (map != NIL && CAR(map) == NIL)
+ RPLACA(map, ptr);
+ break;
+ default:
+ break;
+ }
+ }
+ /* Check for consecutive labels without code between them */
+ for (ptr = labels; CONSP(ptr); ptr = CDDR(ptr)) {
+ if (CADR(ptr) == NIL) {
+ for (map = CDDR(ptr); CONSP(map); map = CDDR(map)) {
+ if (CADR(map) != NIL) {
+ RPLACA(CDR(ptr), CADR(map));
+ break;
+ }
+ }
+ }
+ }
+
+ /* Initialize */
+ list = body;
+ p_list = &list;
+ p_body = &body;
+ p_labels = &labels;
+ block = LispBeginBlock(NIL, LispBlockBody);
+
+ /* Loop */
+ if (setjmp(block->jmp) != 0) {
+ /* Restore environment */
+ lisp__data.stack.length = stack;
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = length;
+
+ tag = lisp__data.block.block_ret;
+ for (ptr = labels; CONSP(ptr); ptr = CDDR(ptr)) {
+ map = CAR(ptr);
+ if (map == tag)
+ break;
+ }
+
+ if (!CONSP(ptr))
+ LispDestroy("%s: no such tag %s", STRFUN(builtin), STROBJ(tag));
+
+ *p_body = CADR(ptr);
+ }
+
+ /* Execute code */
+ for (; CONSP(body); body = CDR(body)) {
+ LispObj *form = CAR(body);
+
+ if (CONSP(form))
+ EVAL(form);
+ }
+ /* If got here, (go) not called, else, labels will be candidate to gc
+ * when GC_LEAVE() be called by the code in the bottom of the stack. */
+ GC_LEAVE();
+
+ /* Finished */
+ LispEndBlock(block);
+
+ /* Always return NIL */
+ return (NIL);
+}
+
+LispObj *
+Lisp_The(LispBuiltin *builtin)
+/*
+ the value-type form
+ */
+{
+ LispObj *value_type, *form;
+
+ form = ARGUMENT(1);
+ value_type = ARGUMENT(0);
+
+ form = EVAL(form);
+
+ return (LispCoerce(builtin, form, value_type));
+}
+
+LispObj *
+Lisp_Throw(LispBuiltin *builtin)
+/*
+ throw tag result
+ */
+{
+ unsigned blevel = lisp__data.block.block_level;
+
+ LispObj *tag, *result;
+
+ result = ARGUMENT(1);
+ tag = ARGUMENT(0);
+
+ tag = EVAL(tag);
+
+ if (blevel == 0)
+ LispDestroy("%s: not within a block", STRFUN(builtin));
+
+ while (blevel) {
+ LispBlock *block = lisp__data.block.block[--blevel];
+
+ if (block->type == LispBlockCatch && tag == block->tag) {
+ lisp__data.block.block_ret = EVAL(result);
+ LispBlockUnwind(block);
+ BLOCKJUMP(block);
+ }
+ }
+ LispDestroy("%s: %s is not a valid tag", STRFUN(builtin), STROBJ(tag));
+
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+static LispObj *
+LispTreeEqual(LispObj *left, LispObj *right, LispObj *test, int expect)
+{
+ LispObj *cmp_left, *cmp_right;
+
+ if ((OBJECT_TYPE(left)) ^ (OBJECT_TYPE(right)))
+ return (NIL);
+ if (CONSP(left)) {
+ for (; CONSP(left) && CONSP(right);
+ left = CDR(left), right = CDR(right)) {
+ cmp_left = CAR(left);
+ cmp_right = CAR(right);
+ if ((OBJECT_TYPE(cmp_left)) ^ (OBJECT_TYPE(cmp_right)))
+ return (NIL);
+ if (CONSP(cmp_left)) {
+ if (LispTreeEqual(cmp_left, cmp_right, test, expect) == NIL)
+ return (NIL);
+ }
+ else {
+ if (POINTERP(cmp_left) &&
+ (XQUOTEP(cmp_left) || XBACKQUOTEP(cmp_left))) {
+ cmp_left = cmp_left->data.quote;
+ cmp_right = cmp_right->data.quote;
+ }
+ else if (COMMAP(cmp_left)) {
+ cmp_left = cmp_left->data.comma.eval;
+ cmp_right = cmp_right->data.comma.eval;
+ }
+ if ((APPLY2(test, cmp_left, cmp_right) != NIL) != expect)
+ return (NIL);
+ }
+ }
+ if ((OBJECT_TYPE(left)) ^ (OBJECT_TYPE(right)))
+ return (NIL);
+ }
+
+ if (POINTERP(left) && (XQUOTEP(left) || XBACKQUOTEP(left))) {
+ left = left->data.quote;
+ right = right->data.quote;
+ }
+ else if (COMMAP(left)) {
+ left = left->data.comma.eval;
+ right = right->data.comma.eval;
+ }
+
+ return ((APPLY2(test, left, right) != NIL) == expect ? T : NIL);
+}
+
+LispObj *
+Lisp_TreeEqual(LispBuiltin *builtin)
+/*
+ tree-equal tree-1 tree-2 &key test test-not
+ */
+{
+ int expect;
+ LispObj *compare;
+
+ LispObj *tree_1, *tree_2, *test, *test_not;
+
+ test_not = ARGUMENT(3);
+ test = ARGUMENT(2);
+ tree_2 = ARGUMENT(1);
+ tree_1 = ARGUMENT(0);
+
+ CHECK_TEST_0();
+ if (test_not != UNSPEC) {
+ expect = 0;
+ compare = test_not;
+ }
+ else {
+ if (test == UNSPEC)
+ test = Oeql;
+ expect = 1;
+ compare = test;
+ }
+
+ return (LispTreeEqual(tree_1, tree_2, compare, expect));
+}
+
+LispObj *
+Lisp_Typep(LispBuiltin *builtin)
+/*
+ typep object type
+ */
+{
+ LispObj *result = NULL;
+
+ LispObj *object, *type;
+
+ type = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (SYMBOLP(type)) {
+ Atom_id atom = ATOMID(type);
+
+ if (OBJECT_TYPE(object) == LispStruct_t)
+ result = ATOMID(CAR(object->data.struc.def)) == atom ? T : NIL;
+ else if (type->data.atom->a_defstruct &&
+ type->data.atom->property->structure.function == STRUCT_NAME)
+ result = NIL;
+ else if (atom == Snil)
+ result = object == NIL ? T : NIL;
+ else if (atom == St)
+ result = object == T ? T : NIL;
+ else if (atom == Satom)
+ result = !CONSP(object) ? T : NIL;
+ else if (atom == Ssymbol)
+ result = SYMBOLP(object) || object == NIL || object == T ? T : NIL;
+ else if (atom == Sinteger)
+ result = INTEGERP(object) ? T : NIL;
+ else if (atom == Srational)
+ result = RATIONALP(object) ? T : NIL;
+ else if (atom == Scons || atom == Slist)
+ result = CONSP(object) ? T : NIL;
+ else if (atom == Sstring)
+ result = STRINGP(object) ? T : NIL;
+ else if (atom == Scharacter)
+ result = SCHARP(object) ? T : NIL;
+ else if (atom == Scomplex)
+ result = COMPLEXP(object) ? T : NIL;
+ else if (atom == Svector || atom == Sarray)
+ result = ARRAYP(object) ? T : NIL;
+ else if (atom == Skeyword)
+ result = KEYWORDP(object) ? T : NIL;
+ else if (atom == Sfunction)
+ result = LAMBDAP(object) ? T : NIL;
+ else if (atom == Spathname)
+ result = PATHNAMEP(object) ? T : NIL;
+ else if (atom == Sopaque)
+ result = OPAQUEP(object) ? T : NIL;
+ }
+ else if (CONSP(type)) {
+ if (OBJECT_TYPE(object) == LispStruct_t &&
+ SYMBOLP(CAR(type)) && ATOMID(CAR(type)) == Sstruct &&
+ SYMBOLP(CAR(CDR(type))) && CDR(CDR(type)) == NIL) {
+ result = ATOMID(CAR(object->data.struc.def)) ==
+ ATOMID(CAR(CDR(type))) ? T : NIL;
+ }
+ }
+ else if (type == NIL)
+ result = object == NIL ? T : NIL;
+ else if (type == T)
+ result = object == T ? T : NIL;
+ if (result == NULL)
+ LispDestroy("%s: bad type specification %s",
+ STRFUN(builtin), STROBJ(type));
+
+ return (result);
+}
+
+LispObj *
+Lisp_Union(LispBuiltin *builtin)
+/*
+ union list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, UNION));
+}
+
+LispObj *
+Lisp_Nunion(LispBuiltin *builtin)
+/*
+ nunion list1 list2 &key test test-not key
+ */
+{
+ return (LispListSet(builtin, NUNION));
+}
+
+LispObj *
+Lisp_Unless(LispBuiltin *builtin)
+/*
+ unless test &rest body
+ */
+{
+ LispObj *result, *test, *body;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ result = NIL;
+ test = EVAL(test);
+ RETURN_COUNT = 0;
+ if (test == NIL) {
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+ }
+
+ return (result);
+}
+
+/*
+ * ext::until
+ */
+LispObj *
+Lisp_Until(LispBuiltin *builtin)
+/*
+ until test &rest body
+ */
+{
+ LispObj *result, *test, *body, *prog;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ result = NIL;
+ for (;;) {
+ if ((result = EVAL(test)) == NIL) {
+ for (prog = body; CONSP(prog); prog = CDR(prog))
+ (void)EVAL(CAR(prog));
+ }
+ else
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_UnwindProtect(LispBuiltin *builtin)
+/*
+ unwind-protect protect &rest cleanup
+ */
+{
+ LispObj *result, **presult = &result;
+ int did_jump, *pdid_jump = &did_jump, destroyed;
+ LispBlock *block;
+
+ LispObj *protect, *cleanup, **pcleanup = &cleanup;
+
+ cleanup = ARGUMENT(1);
+ protect = ARGUMENT(0);
+
+ /* run protected code */
+ *presult = NIL;
+ *pdid_jump = 1;
+ block = LispBeginBlock(NIL, LispBlockProtect);
+ if (setjmp(block->jmp) == 0) {
+ *presult = EVAL(protect);
+ *pdid_jump = 0;
+ }
+ LispEndBlock(block);
+ if (!lisp__data.destroyed && *pdid_jump)
+ *presult = lisp__data.block.block_ret;
+
+ destroyed = lisp__data.destroyed;
+ lisp__data.destroyed = 0;
+
+ /* run cleanup, unprotected code */
+ if (CONSP(*pcleanup))
+ for (; CONSP(cleanup); cleanup = CDR(cleanup))
+ (void)EVAL(CAR(cleanup));
+
+ if (destroyed) {
+ /* in case there is another unwind-protect */
+ LispBlockUnwind(NULL);
+ /* if not, just return to the toplevel */
+ lisp__data.destroyed = 1;
+ LispDestroy(".");
+ }
+
+ return (result);
+}
+
+static LispObj *
+LispValuesList(LispBuiltin *builtin, int check_list)
+{
+ long i, count;
+ LispObj *result;
+
+ LispObj *list;
+
+ list = ARGUMENT(0);
+
+ count = LispLength(list) - 1;
+
+ if (count >= 0) {
+ result = CAR(list);
+ if ((RETURN_CHECK(count)) != count)
+ LispDestroy("%s: too many values", STRFUN(builtin));
+ RETURN_COUNT = count;
+ for (i = 0, list = CDR(list); count && CONSP(list);
+ count--, i++, list = CDR(list))
+ RETURN(i) = CAR(list);
+ if (check_list) {
+ CHECK_LIST(list);
+ }
+ }
+ else {
+ RETURN_COUNT = -1;
+ result = NIL;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Values(LispBuiltin *builtin)
+/*
+ values &rest objects
+ */
+{
+ return (LispValuesList(builtin, 0));
+}
+
+LispObj *
+Lisp_ValuesList(LispBuiltin *builtin)
+/*
+ values-list list
+ */
+{
+ return (LispValuesList(builtin, 1));
+}
+
+LispObj *
+Lisp_Vector(LispBuiltin *builtin)
+/*
+ vector &rest objects
+ */
+{
+ LispObj *objects;
+
+ objects = ARGUMENT(0);
+
+ return (VECTOR(objects));
+}
+
+LispObj *
+Lisp_When(LispBuiltin *builtin)
+/*
+ when test &rest body
+ */
+{
+ LispObj *result, *test, *body;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ result = NIL;
+ test = EVAL(test);
+ RETURN_COUNT = 0;
+ if (test != NIL) {
+ for (; CONSP(body); body = CDR(body))
+ result = EVAL(CAR(body));
+ }
+
+ return (result);
+}
+
+/*
+ * ext::while
+ */
+LispObj *
+Lisp_While(LispBuiltin *builtin)
+/*
+ while test &rest body
+ */
+{
+ LispObj *result, *test, *body, *prog;
+
+ body = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ result = NIL;
+ for (;;) {
+ if (EVAL(test) != NIL) {
+ for (prog = body; CONSP(prog); prog = CDR(prog))
+ (void)EVAL(CAR(prog));
+ }
+ else
+ break;
+ }
+
+ return (NIL);
+}
+
+/*
+ * ext::unsetenv
+ */
+LispObj *
+Lisp_Unsetenv(LispBuiltin *builtin)
+/*
+ unsetenv name
+ */
+{
+ char *name;
+
+ LispObj *oname;
+
+ oname = ARGUMENT(0);
+
+ CHECK_STRING(oname);
+ name = THESTR(oname);
+
+ unsetenv(name);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_XeditEltStore(LispBuiltin *builtin)
+/*
+ lisp::elt-store sequence index value
+ */
+{
+ int length, offset;
+
+ LispObj *sequence, *oindex, *value;
+
+ value = ARGUMENT(2);
+ oindex = ARGUMENT(1);
+ sequence = ARGUMENT(0);
+
+ CHECK_INDEX(oindex);
+ offset = FIXNUM_VALUE(oindex);
+ length = LispLength(sequence);
+
+ if (offset >= length)
+ LispDestroy("%s: index %d too large for sequence length %d",
+ STRFUN(builtin), offset, length);
+
+ if (STRINGP(sequence)) {
+ int ch;
+
+ CHECK_STRING_WRITABLE(sequence);
+ CHECK_SCHAR(value);
+ ch = SCHAR_VALUE(value);
+ if (ch < 0 || ch > 255)
+ LispDestroy("%s: cannot represent character %d",
+ STRFUN(builtin), ch);
+ THESTR(sequence)[offset] = ch;
+ }
+ else {
+ if (ARRAYP(sequence))
+ sequence = sequence->data.array.list;
+
+ for (; offset > 0; offset--, sequence = CDR(sequence))
+ ;
+ RPLACA(sequence, value);
+ }
+
+ return (value);
+}
+
+LispObj *
+Lisp_XeditPut(LispBuiltin *builtin)
+/*
+ lisp::put symbol indicator value
+ */
+{
+ LispObj *symbol, *indicator, *value;
+
+ value = ARGUMENT(2);
+ indicator = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ return (CAR(LispPutAtomProperty(symbol->data.atom, indicator, value)));
+}
+
+LispObj *
+Lisp_XeditSetSymbolPlist(LispBuiltin *builtin)
+/*
+ lisp::set-symbol-plist symbol list
+ */
+{
+ LispObj *symbol, *list;
+
+ list = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ return (LispReplaceAtomPropertyList(symbol->data.atom, list));
+}
+
+LispObj *
+Lisp_XeditVectorStore(LispBuiltin *builtin)
+/*
+ lisp::vector-store array &rest values
+ */
+{
+ LispObj *value, *list, *object;
+ long rank, count, sequence, offset, accum;
+
+ LispObj *array, *values;
+
+ values = ARGUMENT(1);
+ array = ARGUMENT(0);
+
+ /* check for errors */
+ for (rank = 0, list = values;
+ CONSP(list) && CONSP(CDR(list));
+ list = CDR(list), rank++) {
+ CHECK_INDEX(CAR(values));
+ }
+
+ if (rank == 0)
+ LispDestroy("%s: too few subscripts", STRFUN(builtin));
+ value = CAR(list);
+
+ if (STRINGP(array) && rank == 1) {
+ long ch;
+ long length = STRLEN(array);
+ long offset = FIXNUM_VALUE(CAR(values));
+
+ CHECK_SCHAR(value);
+ CHECK_STRING_WRITABLE(array);
+ ch = SCHAR_VALUE(value);
+ if (offset >= length)
+ LispDestroy("%s: index %ld too large for sequence length %ld",
+ STRFUN(builtin), offset, length);
+
+ if (ch < 0 || ch > 255)
+ LispDestroy("%s: cannot represent character %ld",
+ STRFUN(builtin), ch);
+ THESTR(array)[offset] = ch;
+
+ return (value);
+ }
+
+ CHECK_ARRAY(array);
+ if (rank != array->data.array.rank)
+ LispDestroy("%s: too %s subscripts", STRFUN(builtin),
+ rank < array->data.array.rank ? "few" : "many");
+
+ for (list = values, object = array->data.array.dim;
+ CONSP(CDR(list));
+ list = CDR(list), object = CDR(object)) {
+ if (FIXNUM_VALUE(CAR(list)) >= FIXNUM_VALUE(CAR(object)))
+ LispDestroy("%s: %ld is out of range, index %ld",
+ STRFUN(builtin),
+ FIXNUM_VALUE(CAR(list)),
+ FIXNUM_VALUE(CAR(object)));
+ }
+
+ for (count = sequence = 0, list = values;
+ CONSP(CDR(list));
+ list = CDR(list), sequence++) {
+ for (offset = 0, object = array->data.array.dim;
+ offset < sequence; object = CDR(object), offset++)
+ ;
+ for (accum = 1, object = CDR(object); CONSP(object);
+ object = CDR(object))
+ accum *= FIXNUM_VALUE(CAR(object));
+ count += accum * FIXNUM_VALUE(CAR(list));
+ }
+
+ for (array = array->data.array.list; count > 0; array = CDR(array), count--)
+ ;
+
+ RPLACA(array, value);
+
+ return (value);
+}
+
+LispObj *
+Lisp_XeditDocumentationStore(LispBuiltin *builtin)
+/*
+ lisp::documentation-store symbol type string
+ */
+{
+ LispDocType_t doc_type;
+
+ LispObj *symbol, *type, *string;
+
+ string = ARGUMENT(2);
+ type = ARGUMENT(1);
+ symbol = ARGUMENT(0);
+
+ CHECK_SYMBOL(symbol);
+
+ /* type is checked in LispDocumentationType() */
+ doc_type = LispDocumentationType(builtin, type);
+
+ if (string == NIL)
+ /* allow explicitly releasing memory used for documentation */
+ LispRemDocumentation(symbol, doc_type);
+ else {
+ CHECK_STRING(string);
+ LispAddDocumentation(symbol, string, doc_type);
+ }
+
+ return (string);
+}
diff --git a/lisp/core.h b/lisp/core.h
new file mode 100644
index 0000000..403a75a
--- /dev/null
+++ b/lisp/core.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/core.h,v 1.35 2002/12/20 04:32:46 paulo Exp $ */
+
+#ifndef Lisp_core_h
+#define Lisp_core_h
+
+#include "internal.h"
+
+void LispCoreInit(void);
+
+LispObj *Lisp_Acons(LispBuiltin*);
+LispObj *Lisp_Adjoin(LispBuiltin*);
+LispObj *Lisp_Append(LispBuiltin*);
+LispObj *Lisp_And(LispBuiltin*);
+LispObj *Lisp_Aref(LispBuiltin*);
+LispObj *Lisp_Assoc(LispBuiltin*);
+LispObj *Lisp_AssocIf(LispBuiltin*);
+LispObj *Lisp_AssocIfNot(LispBuiltin*);
+LispObj *Lisp_Apply(LispBuiltin*);
+LispObj *Lisp_Atom(LispBuiltin*);
+LispObj *Lisp_Block(LispBuiltin*);
+LispObj *Lisp_Boundp(LispBuiltin*);
+LispObj *Lisp_Butlast(LispBuiltin*);
+LispObj *Lisp_Nbutlast(LispBuiltin*);
+LispObj *Lisp_Car(LispBuiltin*);
+LispObj *Lisp_Case(LispBuiltin*);
+LispObj *Lisp_Catch(LispBuiltin*);
+LispObj *Lisp_Cdr(LispBuiltin*);
+LispObj *Lisp_C_r(LispBuiltin*);
+LispObj *Lisp_Coerce(LispBuiltin*);
+LispObj *Lisp_Cond(LispBuiltin*);
+LispObj *Lisp_Cons(LispBuiltin*);
+LispObj *Lisp_Consp(LispBuiltin*);
+LispObj *Lisp_Constantp(LispBuiltin*);
+LispObj *Lisp_CopyAlist(LispBuiltin*);
+LispObj *Lisp_CopyList(LispBuiltin*);
+LispObj *Lisp_CopyTree(LispBuiltin*);
+LispObj *Lisp_Defconstant(LispBuiltin*);
+LispObj *Lisp_Defmacro(LispBuiltin*);
+LispObj *Lisp_Defun(LispBuiltin*);
+LispObj *Lisp_Defsetf(LispBuiltin*);
+LispObj *Lisp_Defparameter(LispBuiltin*);
+LispObj *Lisp_Defvar(LispBuiltin*);
+LispObj *Lisp_Delete(LispBuiltin*);
+LispObj *Lisp_DeleteDuplicates(LispBuiltin*);
+LispObj *Lisp_DeleteIf(LispBuiltin*);
+LispObj *Lisp_DeleteIfNot(LispBuiltin*);
+LispObj *Lisp_Do(LispBuiltin*);
+LispObj *Lisp_DoP(LispBuiltin*);
+LispObj *Lisp_Documentation(LispBuiltin*);
+LispObj *Lisp_DoList(LispBuiltin*);
+LispObj *Lisp_DoTimes(LispBuiltin*);
+LispObj *Lisp_Elt(LispBuiltin*);
+LispObj *Lisp_Endp(LispBuiltin*);
+LispObj *Lisp_Eq(LispBuiltin*);
+LispObj *Lisp_Eql(LispBuiltin*);
+LispObj *Lisp_Equal(LispBuiltin*);
+LispObj *Lisp_Equalp(LispBuiltin*);
+LispObj *Lisp_Error(LispBuiltin*);
+LispObj *Lisp_Eval(LispBuiltin*);
+LispObj *Lisp_Every(LispBuiltin*);
+LispObj *Lisp_Some(LispBuiltin*);
+LispObj *Lisp_Notevery(LispBuiltin*);
+LispObj *Lisp_Notany(LispBuiltin*);
+LispObj *Lisp_Fboundp(LispBuiltin*);
+LispObj *Lisp_Find(LispBuiltin*);
+LispObj *Lisp_FindIf(LispBuiltin*);
+LispObj *Lisp_FindIfNot(LispBuiltin*);
+LispObj *Lisp_Fill(LispBuiltin*);
+LispObj *Lisp_Fmakunbound(LispBuiltin*);
+LispObj *Lisp_Functionp(LispBuiltin*);
+LispObj *Lisp_Funcall(LispBuiltin*);
+LispObj *Lisp_Gc(LispBuiltin*);
+LispObj *Lisp_Gensym(LispBuiltin*);
+LispObj *Lisp_Get(LispBuiltin*);
+LispObj *Lisp_Getenv(LispBuiltin*);
+LispObj *Lisp_Go(LispBuiltin*);
+LispObj *Lisp_If(LispBuiltin*);
+LispObj *Lisp_IgnoreErrors(LispBuiltin*);
+LispObj *Lisp_Intersection(LispBuiltin*);
+LispObj *Lisp_Nintersection(LispBuiltin*);
+LispObj *Lisp_Keywordp(LispBuiltin*);
+LispObj *Lisp_Lambda(LispBuiltin*);
+LispObj *Lisp_Last(LispBuiltin*);
+LispObj *Lisp_Let(LispBuiltin*);
+LispObj *Lisp_Length(LispBuiltin*);
+LispObj *Lisp_LetP(LispBuiltin*);
+LispObj *Lisp_List(LispBuiltin*);
+LispObj *Lisp_ListLength(LispBuiltin*);
+LispObj *Lisp_ListP(LispBuiltin*);
+LispObj *Lisp_Listp(LispBuiltin*);
+LispObj *Lisp_Loop(LispBuiltin*);
+LispObj *Lisp_MakeArray(LispBuiltin*);
+LispObj *Lisp_MakeList(LispBuiltin*);
+LispObj *Lisp_MakeSymbol(LispBuiltin*);
+LispObj *Lisp_Makunbound(LispBuiltin*);
+LispObj *Lisp_Mapc(LispBuiltin*);
+LispObj *Lisp_Mapcar(LispBuiltin*);
+LispObj *Lisp_Mapcan(LispBuiltin*);
+LispObj *Lisp_Mapl(LispBuiltin*);
+LispObj *Lisp_Maplist(LispBuiltin*);
+LispObj *Lisp_Mapcon(LispBuiltin*);
+LispObj *Lisp_Member(LispBuiltin*);
+LispObj *Lisp_MemberIf(LispBuiltin*);
+LispObj *Lisp_MemberIfNot(LispBuiltin*);
+LispObj *Lisp_MultipleValueBind(LispBuiltin*);
+LispObj *Lisp_MultipleValueCall(LispBuiltin*);
+LispObj *Lisp_MultipleValueProg1(LispBuiltin*);
+LispObj *Lisp_MultipleValueList(LispBuiltin*);
+LispObj *Lisp_MultipleValueSetq(LispBuiltin*);
+LispObj *Lisp_Nconc(LispBuiltin*);
+LispObj *Lisp_Nreverse(LispBuiltin*);
+LispObj *Lisp_NsetDifference(LispBuiltin*);
+LispObj *Lisp_Nsubstitute(LispBuiltin*);
+LispObj *Lisp_NsubstituteIf(LispBuiltin*);
+LispObj *Lisp_NsubstituteIfNot(LispBuiltin*);
+LispObj *Lisp_Nth(LispBuiltin*);
+LispObj *Lisp_Nthcdr(LispBuiltin*);
+LispObj *Lisp_NthValue(LispBuiltin*);
+LispObj *Lisp_Null(LispBuiltin*);
+LispObj *Lisp_Or(LispBuiltin*);
+LispObj *Lisp_Pairlis(LispBuiltin*);
+LispObj *Lisp_Pop(LispBuiltin*);
+LispObj *Lisp_Position(LispBuiltin*);
+LispObj *Lisp_PositionIf(LispBuiltin*);
+LispObj *Lisp_PositionIfNot(LispBuiltin*);
+LispObj *Lisp_Proclaim(LispBuiltin*);
+LispObj *Lisp_Prog1(LispBuiltin*);
+LispObj *Lisp_Prog2(LispBuiltin*);
+LispObj *Lisp_Progn(LispBuiltin*);
+LispObj *Lisp_Progv(LispBuiltin*);
+LispObj *Lisp_Provide(LispBuiltin*);
+LispObj *Lisp_Push(LispBuiltin*);
+LispObj *Lisp_Pushnew(LispBuiltin*);
+LispObj *Lisp_Quit(LispBuiltin*);
+LispObj *Lisp_Quote(LispBuiltin*);
+LispObj *Lisp_Remove(LispBuiltin*);
+LispObj *Lisp_RemoveDuplicates(LispBuiltin*);
+LispObj *Lisp_RemoveIf(LispBuiltin*);
+LispObj *Lisp_RemoveIfNot(LispBuiltin*);
+LispObj *Lisp_Remprop(LispBuiltin*);
+LispObj *Lisp_Replace(LispBuiltin*);
+LispObj *Lisp_Return(LispBuiltin*);
+LispObj *Lisp_ReturnFrom(LispBuiltin*);
+LispObj *Lisp_Reverse(LispBuiltin*);
+LispObj *Lisp_Rplaca(LispBuiltin*);
+LispObj *Lisp_Rplacd(LispBuiltin*);
+LispObj *Lisp_Search(LispBuiltin*);
+LispObj *Lisp_Setenv(LispBuiltin*);
+LispObj *Lisp_Set(LispBuiltin*);
+LispObj *Lisp_SetDifference(LispBuiltin*);
+LispObj *Lisp_SetExclusiveOr(LispBuiltin*);
+LispObj *Lisp_NsetExclusiveOr(LispBuiltin*);
+LispObj *Lisp_Setf(LispBuiltin*);
+LispObj *Lisp_Psetf(LispBuiltin*);
+LispObj *Lisp_SetQ(LispBuiltin*);
+LispObj *Lisp_Psetq(LispBuiltin*);
+LispObj *Lisp_Sleep(LispBuiltin*);
+LispObj *Lisp_Sort(LispBuiltin*);
+LispObj *Lisp_Subseq(LispBuiltin*);
+LispObj *Lisp_Subsetp(LispBuiltin*);
+LispObj *Lisp_Substitute(LispBuiltin*);
+LispObj *Lisp_SubstituteIf(LispBuiltin*);
+LispObj *Lisp_SubstituteIfNot(LispBuiltin*);
+LispObj *Lisp_Symbolp(LispBuiltin*);
+LispObj *Lisp_SymbolFunction(LispBuiltin*);
+LispObj *Lisp_SymbolName(LispBuiltin*);
+LispObj *Lisp_SymbolPackage(LispBuiltin*);
+LispObj *Lisp_SymbolPlist(LispBuiltin*);
+LispObj *Lisp_SymbolValue(LispBuiltin*);
+LispObj *Lisp_Tagbody(LispBuiltin*);
+LispObj *Lisp_Throw(LispBuiltin*);
+LispObj *Lisp_The(LispBuiltin*);
+LispObj *Lisp_TreeEqual(LispBuiltin*);
+LispObj *Lisp_Typep(LispBuiltin*);
+LispObj *Lisp_Union(LispBuiltin*);
+LispObj *Lisp_Nunion(LispBuiltin*);
+LispObj *Lisp_Unless(LispBuiltin*);
+LispObj *Lisp_Until(LispBuiltin*);
+LispObj *Lisp_Unsetenv(LispBuiltin*);
+LispObj *Lisp_UnwindProtect(LispBuiltin*);
+LispObj *Lisp_Values(LispBuiltin*);
+LispObj *Lisp_ValuesList(LispBuiltin*);
+LispObj *Lisp_Vector(LispBuiltin*);
+LispObj *Lisp_When(LispBuiltin*);
+LispObj *Lisp_While(LispBuiltin*);
+LispObj *Lisp_XeditEltStore(LispBuiltin*);
+LispObj *Lisp_XeditPut(LispBuiltin*);
+LispObj *Lisp_XeditSetSymbolPlist(LispBuiltin*);
+LispObj *Lisp_XeditVectorStore(LispBuiltin*);
+LispObj *Lisp_XeditDocumentationStore(LispBuiltin*);
+
+#endif
diff --git a/lisp/debugger.c b/lisp/debugger.c
new file mode 100644
index 0000000..4716699
--- /dev/null
+++ b/lisp/debugger.c
@@ -0,0 +1,828 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/debugger.c,v 1.24 2002/11/12 06:05:07 paulo Exp $ */
+
+#include <ctype.h>
+#include "io.h"
+#include "debugger.h"
+#include "write.h"
+
+#ifdef DEBUGGER
+#define DebuggerHelp 0
+#define DebuggerAbort 1
+#define DebuggerBacktrace 2
+#define DebuggerContinue 3
+#define DebuggerFinish 4
+#define DebuggerFrame 5
+#define DebuggerNext 6
+#define DebuggerPrint 7
+#define DebuggerStep 8
+#define DebuggerBreak 9
+#define DebuggerDelete 10
+#define DebuggerDown 11
+#define DebuggerUp 12
+#define DebuggerInfo 13
+#define DebuggerWatch 14
+
+#define DebuggerInfoBreakpoints 0
+#define DebuggerInfoBacktrace 1
+
+/*
+ * Prototypes
+ */
+static char *format_integer(int);
+static void LispDebuggerCommand(LispObj *obj);
+
+/*
+ * Initialization
+ */
+static struct {
+ char *name;
+ int action;
+} commands[] = {
+ {"help", DebuggerHelp},
+ {"abort", DebuggerAbort},
+ {"backtrace", DebuggerBacktrace},
+ {"b", DebuggerBreak},
+ {"break", DebuggerBreak},
+ {"bt", DebuggerBacktrace},
+ {"continue", DebuggerContinue},
+ {"d", DebuggerDelete},
+ {"delete", DebuggerDelete},
+ {"down", DebuggerDown},
+ {"finish", DebuggerFinish},
+ {"frame", DebuggerFrame},
+ {"info", DebuggerInfo},
+ {"n", DebuggerNext},
+ {"next", DebuggerNext},
+ {"print", DebuggerPrint},
+ {"run", DebuggerContinue},
+ {"s", DebuggerStep},
+ {"step", DebuggerStep},
+ {"up", DebuggerUp},
+ {"watch", DebuggerWatch},
+};
+
+static struct {
+ char *name;
+ int subaction;
+} info_commands[] = {
+ {"breakpoints", DebuggerInfoBreakpoints},
+ {"stack", DebuggerInfoBacktrace},
+ {"watchpoints", DebuggerInfoBreakpoints},
+};
+
+static char debugger_help[] =
+"Available commands are:\n\
+\n\
+help - This message.\n\
+abort - Abort the current execution, and return to toplevel.\n\
+backtrace, bt - Print backtrace.\n\
+b, break - Set breakpoint at function name argument.\n\
+continue - Continue execution.\n\
+d, delete - Delete breakpoint(s), all breakpoint if no arguments given.\n\
+down - Set environment to frame called by the current one.\n\
+finish - Executes until current form is finished.\n\
+frame - Set environment to selected frame.\n\
+info - Prints information about the debugger state.\n\
+n, next - Evaluate next form.\n\
+print - Print value of variable name argument.\n\
+run - Continue execution.\n\
+s, step - Evaluate next form, stopping on any subforms.\n\
+up - Set environment to frame that called the current one.\n\
+\n\
+Commands may be abbreviated.\n";
+
+static char debugger_info_help[] =
+"Available subcommands are:\n\
+\n\
+breakpoints - List and prints status of breakpoints, and watchpoints.\n\
+stack - Backtrace of stack.\n\
+watchpoints - List and prints status of watchpoints, and breakpoints.\n\
+\n\
+Subcommands may be abbreviated.\n";
+
+/* Debugger variables layout (if you change it, update description):
+ *
+ * DBG
+ * is a macro for lisp__data.dbglist
+ * is a NIL terminated list
+ * every element is a list in the format (NOT NIL terminated):
+ * (list* NAM ARG ENV HED LEX)
+ * where
+ * NAM is an ATOM for the function/macro name
+ * or NIL for lambda expressions
+ * ARG is NAM arguments (a LIST)
+ * ENV is the value of lisp__data.stack.base (a FIXNUM)
+ * LEN is the value of lisp__data.env.length (a FIXNUM)
+ * LEX is the value of lisp__data.env.lex (a FIXNUM)
+ * new elements are added to the beggining of the DBG list
+ *
+ * BRK
+ * is macro for lisp__data.brklist
+ * is a NIL terminated list
+ * every element is a list in the format (NIL terminated):
+ * (list NAM IDX TYP HIT VAR VAL FRM)
+ * where
+ * NAM is an ATOM for the name of the object at
+ * wich the breakpoint was added
+ * IDX is a FIXNUM, the breakpoint number
+ * must be stored, as breakpoints may be deleted
+ * TYP is a FIXNUM that must be an integer of enum LispBreakType
+ * HIT is a FIXNUM, with the number of times this breakpoint was
+ * hitted.
+ * VAR variable to watch a SYMBOL (not needed for breakpoints)
+ * VAL value of watched variable (not needed for breakpoints)
+ * FRM frame where variable started being watched
+ * (not needed for breakpoints)
+ * new elements are added to the end of the list
+ */
+
+/*
+ * Implementation
+ */
+void
+LispDebugger(LispDebugCall call, LispObj *name, LispObj *arg)
+{
+ int force = 0;
+ LispObj *obj, *prev;
+
+ switch (call) {
+ case LispDebugCallBegin:
+ ++lisp__data.debug_level;
+ GCDisable();
+ DBG = CONS(CONS(name, CONS(arg, CONS(FIXNUM(lisp__data.stack.base),
+ CONS(FIXNUM(lisp__data.env.length),
+ FIXNUM(lisp__data.env.lex))))), DBG);
+ GCEnable();
+ for (obj = BRK; obj != NIL; obj = CDR(obj))
+ if (ATOMID(CAR(CAR(obj))) == ATOMID(name) &&
+ FIXNUM_VALUE(CAR(CDR(CDR(CAR(obj))))) ==
+ LispDebugBreakFunction)
+ break;
+ if (obj != NIL) {
+ long counter;
+
+ /* if not at a fresh line */
+ if (LispGetColumn(NIL))
+ LispFputc(Stdout, '\n');
+ LispFputs(Stdout, "BREAK #");
+ LispWriteObject(NIL, CAR(CDR(CAR(obj))));
+ LispFputs(Stdout, "> (");
+ LispWriteObject(NIL, CAR(CAR(DBG)));
+ LispFputc(Stdout, ' ');
+ LispWriteObject(NIL, CAR(CDR(CAR(DBG))));
+ LispFputs(Stdout, ")\n");
+ force = 1;
+ /* update hits counter */
+ counter = FIXNUM_VALUE(CAR(CDR(CDR(CDR(CAR(obj))))));
+ CAR(CDR(CDR(CDR(CAR(obj))))) FIXNUM(counter + 1);
+ }
+ break;
+ case LispDebugCallEnd:
+ DBG = CDR(DBG);
+ if (lisp__data.debug_level < lisp__data.debug_step)
+ lisp__data.debug_step = lisp__data.debug_level;
+ --lisp__data.debug_level;
+ break;
+ case LispDebugCallFatal:
+ LispDebuggerCommand(NIL);
+ return;
+ case LispDebugCallWatch:
+ break;
+ }
+
+ /* didn't return, check watchpoints */
+ if (call == LispDebugCallEnd || call == LispDebugCallWatch) {
+watch_again:
+ for (prev = obj = BRK; obj != NIL; prev = obj, obj = CDR(obj)) {
+ if (FIXNUM_VALUE(CAR(CDR(CDR(CAR(obj))))) ==
+ LispDebugBreakVariable) {
+ /* the variable */
+ LispObj *wat = CAR(CDR(CDR(CDR(CDR(CAR(obj))))));
+ void *sym = LispGetVarAddr(CAAR(obj));
+ LispObj *frm = CAR(CDR(CDR(CDR(CDR(CDR(CDR(CAR(obj))))))));
+
+ if ((sym == NULL && lisp__data.debug_level <= 0) ||
+ (sym != wat->data.opaque.data &&
+ FIXNUM_VALUE(frm) > lisp__data.debug_level)) {
+ LispFputs(Stdout, "WATCH #");
+ LispFputs(Stdout, format_integer(FIXNUM_VALUE(CAR(CDR(CAR(obj))))));
+ LispFputs(Stdout, "> ");
+ LispFputs(Stdout, STRPTR(CAR(CAR(obj))));
+ LispFputs(Stdout, " deleted. Variable does not exist anymore.\n");
+ /* force debugger to stop */
+ force = 1;
+ if (obj == prev) {
+ BRK = CDR(BRK);
+ goto watch_again;
+ }
+ else
+ RPLACD(prev, CDR(obj));
+ obj = prev;
+ }
+ else {
+ /* current value */
+ LispObj *cur = *(LispObj**)wat->data.opaque.data;
+ /* last value */
+ LispObj *val = CAR(CDR(CDR(CDR(CDR(CDR(CAR(obj)))))));
+ if (XEQUAL(val, cur) == NIL) {
+ long counter;
+
+ LispFputs(Stdout, "WATCH #");
+ LispFputs(Stdout, format_integer(FIXNUM_VALUE(CAR(CDR(CAR(obj))))));
+ LispFputs(Stdout, "> ");
+ LispFputs(Stdout, STRPTR(CAR(CAR(obj))));
+ LispFputc(Stdout, '\n');
+
+ LispFputs(Stdout, "OLD: ");
+ LispWriteObject(NIL, val);
+
+ LispFputs(Stdout, "\nNEW: ");
+ LispWriteObject(NIL, cur);
+ LispFputc(Stdout, '\n');
+
+ /* update current value */
+ CAR(CDR(CDR(CDR(CDR(CDR(CAR(obj))))))) = cur;
+ /* update hits counter */
+ counter = FIXNUM_VALUE(CAR(CDR(CDR(CDR(CAR(obj))))));
+ CAR(CDR(CDR(CDR(CAR(obj))))) = FIXNUM(counter + 1);
+ /* force debugger to stop */
+ force = 1;
+ }
+ }
+ }
+ }
+
+ if (call == LispDebugCallWatch)
+ /* special call, just don't keep gc protected variables that may be
+ * using a lot of memory... */
+ return;
+ }
+
+ switch (lisp__data.debug) {
+ case LispDebugUnspec:
+ LispDebuggerCommand(NIL);
+ goto debugger_done;
+ case LispDebugRun:
+ if (force)
+ LispDebuggerCommand(NIL);
+ goto debugger_done;
+ case LispDebugFinish:
+ if (!force &&
+ (call != LispDebugCallEnd ||
+ lisp__data.debug_level != lisp__data.debug_step))
+ goto debugger_done;
+ break;
+ case LispDebugNext:
+ if (call == LispDebugCallBegin) {
+ if (!force && lisp__data.debug_level != lisp__data.debug_step)
+ goto debugger_done;
+ }
+ else if (call == LispDebugCallEnd) {
+ if (!force && lisp__data.debug_level >= lisp__data.debug_step)
+ goto debugger_done;
+ }
+ break;
+ case LispDebugStep:
+ break;
+ }
+
+ if (call == LispDebugCallBegin) {
+ LispFputc(Stdout, '#');
+ LispFputs(Stdout, format_integer(lisp__data.debug_level));
+ LispFputs(Stdout, "> (");
+ LispWriteObject(NIL, CAR(CAR(DBG)));
+ LispFputc(Stdout, ' ');
+ LispWriteObject(NIL, CAR(CDR(CAR(DBG))));
+ LispFputs(Stdout, ")\n");
+ LispDebuggerCommand(NIL);
+ }
+ else if (call == LispDebugCallEnd) {
+ LispFputc(Stdout, '#');
+ LispFputs(Stdout, format_integer(lisp__data.debug_level + 1));
+ LispFputs(Stdout, "= ");
+ LispWriteObject(NIL, arg);
+ LispFputc(Stdout, '\n');
+ LispDebuggerCommand(NIL);
+ }
+ else if (force)
+ LispDebuggerCommand(arg);
+
+debugger_done:
+ return;
+}
+
+static void
+LispDebuggerCommand(LispObj *args)
+{
+ LispObj *obj, *frm, *curframe;
+ int i = 0, frame, matches, action = -1, subaction = 0;
+ char *cmd, *arg, *ptr, line[256];
+
+ int envbase = lisp__data.stack.base,
+ envlen = lisp__data.env.length,
+ envlex = lisp__data.env.lex;
+
+ frame = lisp__data.debug_level;
+ curframe = CAR(DBG);
+
+ line[0] = '\0';
+ arg = line;
+ for (;;) {
+ LispFputs(Stdout, DBGPROMPT);
+ LispFflush(Stdout);
+ if (LispFgets(Stdin, line, sizeof(line)) == NULL) {
+ LispFputc(Stdout, '\n');
+ return;
+ }
+ /* get command */
+ ptr = line;
+ while (*ptr && isspace(*ptr))
+ ++ptr;
+ cmd = ptr;
+ while (*ptr && !isspace(*ptr))
+ ++ptr;
+ if (*ptr)
+ *ptr++ = '\0';
+
+ if (*cmd) { /* if *cmd is nul, then arg may be still set */
+ /* get argument(s) */
+ while (*ptr && isspace(*ptr))
+ ++ptr;
+ arg = ptr;
+ /* goto end of line */
+ if (*ptr) {
+ while (*ptr)
+ ++ptr;
+ --ptr;
+ while (*ptr && isspace(*ptr))
+ --ptr;
+ if (*ptr)
+ *++ptr = '\0';
+ }
+ }
+
+ if (*cmd == '\0') {
+ if (action < 0) {
+ if (lisp__data.debug == LispDebugFinish)
+ action = DebuggerFinish;
+ else if (lisp__data.debug == LispDebugNext)
+ action = DebuggerNext;
+ else if (lisp__data.debug == LispDebugStep)
+ action = DebuggerStep;
+ else if (lisp__data.debug == LispDebugRun)
+ action = DebuggerContinue;
+ else
+ continue;
+ }
+ }
+ else {
+ for (i = matches = 0; i < sizeof(commands) / sizeof(commands[0]);
+ i++) {
+ char *str = commands[i].name;
+
+ ptr = cmd;
+ while (*ptr && *ptr == *str) {
+ ++ptr;
+ ++str;
+ }
+ if (*ptr == '\0') {
+ action = commands[i].action;
+ if (*str == '\0') {
+ matches = 1;
+ break;
+ }
+ ++matches;
+ }
+ }
+ if (matches == 0) {
+ LispFputs(Stdout, "* Command unknown: ");
+ LispFputs(Stdout, cmd);
+ LispFputs(Stdout, ". Type help for help.\n");
+ continue;
+ }
+ else if (matches > 1) {
+ LispFputs(Stdout, "* Command is ambiguous: ");
+ LispFputs(Stdout, cmd);
+ LispFputs(Stdout, ". Type help for help.\n");
+ continue;
+ }
+ }
+
+ switch (action) {
+ case DebuggerHelp:
+ LispFputs(Stdout, debugger_help);
+ break;
+ case DebuggerInfo:
+ if (*arg == '\0') {
+ LispFputs(Stdout, debugger_info_help);
+ break;
+ }
+
+ for (i = matches = 0;
+ i < sizeof(info_commands) / sizeof(info_commands[0]);
+ i++) {
+ char *str = info_commands[i].name;
+
+ ptr = arg;
+ while (*ptr && *ptr == *str) {
+ ++ptr;
+ ++str;
+ }
+ if (*ptr == '\0') {
+ subaction = info_commands[i].subaction;
+ if (*str == '\0') {
+ matches = 1;
+ break;
+ }
+ ++matches;
+ }
+ }
+ if (matches == 0) {
+ LispFputs(Stdout, "* Command unknown: ");
+ LispFputs(Stdout, arg);
+ LispFputs(Stdout, ". Type info for help.\n");
+ continue;
+ }
+ else if (matches > 1) {
+ LispFputs(Stdout, "* Command is ambiguous: ");
+ LispFputs(Stdout, arg);
+ LispFputs(Stdout, ". Type info for help.\n");
+ continue;
+ }
+
+ switch (subaction) {
+ case DebuggerInfoBreakpoints:
+ LispFputs(Stdout, "Num\tHits\tType\t\tWhat\n");
+ for (obj = BRK; obj != NIL; obj = CDR(obj)) {
+ /* breakpoint number */
+ LispFputc(Stdout, '#');
+ LispWriteObject(NIL, CAR(CDR(CAR(obj))));
+
+ /* number of hits */
+ LispFputc(Stdout, '\t');
+ LispWriteObject(NIL, CAR(CDR(CDR(CDR(CAR(obj))))));
+
+ /* breakpoint type */
+ LispFputc(Stdout, '\t');
+ switch ((int)FIXNUM_VALUE(CAR(CDR(CDR(CAR(obj)))))) {
+ case LispDebugBreakFunction:
+ LispFputs(Stdout, "Function");
+ break;
+ case LispDebugBreakVariable:
+ LispFputs(Stdout, "Variable");
+ break;
+ }
+
+ /* breakpoint object */
+ LispFputc(Stdout, '\t');
+ LispWriteObject(NIL, CAR(CAR(obj)));
+ LispFputc(Stdout, '\n');
+ }
+ break;
+ case DebuggerInfoBacktrace:
+ goto debugger_print_backtrace;
+ }
+ break;
+ case DebuggerAbort:
+ while (lisp__data.mem.level) {
+ --lisp__data.mem.level;
+ if (lisp__data.mem.mem[lisp__data.mem.level])
+ free(lisp__data.mem.mem[lisp__data.mem.level]);
+ }
+ lisp__data.mem.index = 0;
+ LispTopLevel();
+ if (!lisp__data.running) {
+ LispMessage("*** Fatal: nowhere to longjmp.");
+ abort();
+ }
+ /* don't need to restore environment */
+ siglongjmp(lisp__data.jmp, 1);
+ /*NOTREACHED*/
+ break;
+ case DebuggerBreak:
+ for (ptr = arg; *ptr; ptr++) {
+ if (isspace(*ptr))
+ break;
+ else
+ *ptr = toupper(*ptr);
+ }
+
+ if (!*arg || *ptr || strchr(arg, '(') || strchr(arg, '(') ||
+ strchr(arg, ';')) {
+ LispFputs(Stdout, "* Bad function name '");
+ LispFputs(Stdout, arg);
+ LispFputs(Stdout, "' specified.\n");
+ }
+ else {
+ for (obj = frm = BRK; obj != NIL; frm = obj, obj = CDR(obj))
+ ;
+ i = lisp__data.debug_break;
+ ++lisp__data.debug_break;
+ GCDisable();
+ obj = CONS(ATOM(arg),
+ CONS(FIXNUM(i),
+ CONS(FIXNUM(LispDebugBreakFunction),
+ CONS(FIXNUM(0), NIL))));
+ if (BRK == NIL)
+ BRK = CONS(obj, NIL);
+ else
+ RPLACD(frm, CONS(obj, NIL));
+ GCEnable();
+ }
+ break;
+ case DebuggerWatch: {
+ void *sym;
+ int vframe;
+ LispObj *val, *atom;
+
+ /* make variable name uppercase, an ATOM */
+ ptr = arg;
+ while (*ptr) {
+ *ptr = toupper(*ptr);
+ ++ptr;
+ }
+ atom = ATOM(arg);
+ val = LispGetVar(atom);
+ if (val == NULL) {
+ LispFputs(Stdout, "* No variable named '");
+ LispFputs(Stdout, arg);
+ LispFputs(Stdout, "' in the selected frame.\n");
+ break;
+ }
+
+ /* variable is available at the current frame */
+ sym = LispGetVarAddr(atom);
+
+ /* find the lowest frame where the variable is visible */
+ vframe = 0;
+ if (frame > 0) {
+ for (; vframe < frame; vframe++) {
+ for (frm = DBG, i = lisp__data.debug_level; i > vframe;
+ frm = CDR(frm), i--)
+ ;
+ obj = CAR(frm);
+ lisp__data.stack.base = FIXNUM_VALUE(CAR(CDR(CDR(obj))));
+ lisp__data.env.length = FIXNUM_VALUE(CAR(CDR(CDR(CDR(obj)))));
+ lisp__data.env.lex = FIXNUM_VALUE(CDR(CDR(CDR(CDR(obj)))));
+
+ if (LispGetVarAddr(atom) == sym)
+ /* got variable initial frame */
+ break;
+ }
+ vframe = i;
+ if (vframe != frame) {
+ /* restore environment */
+ for (frm = DBG, i = lisp__data.debug_level; i > frame;
+ frm = CDR(frm), i--)
+ ;
+ obj = CAR(frm);
+ lisp__data.stack.base = FIXNUM_VALUE(CAR(CDR(CDR(obj))));
+ lisp__data.env.length = FIXNUM_VALUE(CAR(CDR(CDR(CDR(obj)))));
+ lisp__data.env.lex = FIXNUM_VALUE(CDR(CDR(CDR(CDR(obj)))));
+ }
+ }
+
+ i = lisp__data.debug_break;
+ ++lisp__data.debug_break;
+ for (obj = frm = BRK; obj != NIL; frm = obj, obj = CDR(obj))
+ ;
+
+ GCDisable();
+ obj = CONS(atom, /* NAM */
+ CONS(FIXNUM(i), /* IDX */
+ CONS(FIXNUM(LispDebugBreakVariable), /* TYP */
+ CONS(FIXNUM(0), /* HIT */
+ CONS(OPAQUE(sym, 0), /* VAR */
+ CONS(val, /* VAL */
+ CONS(FIXNUM(vframe),/* FRM */
+ NIL)))))));
+
+ /* add watchpoint */
+ if (BRK == NIL)
+ BRK = CONS(obj, NIL);
+ else
+ RPLACD(frm, CONS(obj, NIL));
+ GCEnable();
+ } break;
+ case DebuggerDelete:
+ if (*arg == 0) {
+ int confirm = 0;
+
+ for (;;) {
+ int ch;
+
+ LispFputs(Stdout, "* Delete all breakpoints? (y or n) ");
+ LispFflush(Stdout);
+ if ((ch = LispFgetc(Stdin)) == '\n')
+ continue;
+ while ((i = LispFgetc(Stdin)) != '\n' && i != EOF)
+ ;
+ if (tolower(ch) == 'n')
+ break;
+ else if (tolower(ch) == 'y') {
+ confirm = 1;
+ break;
+ }
+ }
+ if (confirm)
+ BRK = NIL;
+ }
+ else {
+ for (ptr = arg; *ptr;) {
+ while (*ptr && isdigit(*ptr))
+ ++ptr;
+ if (*ptr && !isspace(*ptr)) {
+ *ptr = '\0';
+ LispFputs(Stdout, "* Bad breakpoint number '");
+ LispFputs(Stdout, arg);
+ LispFputs(Stdout, "' specified.\n");
+ break;
+ }
+ i = atoi(arg);
+ for (obj = frm = BRK; frm != NIL;
+ obj = frm, frm = CDR(frm))
+ if (FIXNUM_VALUE(CAR(CDR(CAR(frm)))) == i)
+ break;
+ if (frm == NIL) {
+ LispFputs(Stdout, "* No breakpoint number ");
+ LispFputs(Stdout, arg);
+ LispFputs(Stdout, " available.\n");
+ break;
+ }
+ if (obj == frm)
+ BRK = CDR(BRK);
+ else
+ RPLACD(obj, CDR(frm));
+ while (*ptr && isspace(*ptr))
+ ++ptr;
+ arg = ptr;
+ }
+ }
+ break;
+ case DebuggerFrame:
+ i = -1;
+ ptr = arg;
+ if (*ptr) {
+ i = 0;
+ while (*ptr && isdigit(*ptr)) {
+ i *= 10;
+ i += *ptr - '0';
+ ++ptr;
+ }
+ if (*ptr) {
+ LispFputs(Stdout, "* Frame identifier must "
+ "be a positive number.\n");
+ break;
+ }
+ }
+ else
+ goto debugger_print_frame;
+ if (i >= 0 && i <= lisp__data.debug_level)
+ goto debugger_new_frame;
+ LispFputs(Stdout, "* No such frame ");
+ LispFputs(Stdout, format_integer(i));
+ LispFputs(Stdout, ".\n");
+ break;
+ case DebuggerDown:
+ if (frame + 1 > lisp__data.debug_level) {
+ LispFputs(Stdout, "* Cannot go down.\n");
+ break;
+ }
+ i = frame + 1;
+ goto debugger_new_frame;
+ break;
+ case DebuggerUp:
+ if (frame == 0) {
+ LispFputs(Stdout, "* Cannot go up.\n");
+ break;
+ }
+ i = frame - 1;
+ goto debugger_new_frame;
+ break;
+ case DebuggerPrint:
+ ptr = arg;
+ while (*ptr) {
+ *ptr = toupper(*ptr);
+ ++ptr;
+ }
+ obj = LispGetVar(ATOM(arg));
+ if (obj != NULL) {
+ LispWriteObject(NIL, obj);
+ LispFputc(Stdout, '\n');
+ }
+ else {
+ LispFputs(Stdout, "* No variable named '");
+ LispFputs(Stdout, arg);
+ LispFputs(Stdout, "' in the selected frame.\n");
+ }
+ break;
+ case DebuggerBacktrace:
+debugger_print_backtrace:
+ if (DBG == NIL) {
+ LispFputs(Stdout, "* No stack.\n");
+ break;
+ }
+ DBG = LispReverse(DBG);
+ for (obj = DBG, i = 0; obj != NIL; obj = CDR(obj), i++) {
+ frm = CAR(obj);
+ LispFputc(Stdout, '#');
+ LispFputs(Stdout, format_integer(i));
+ LispFputs(Stdout, "> (");
+ LispWriteObject(NIL, CAR(frm));
+ LispFputc(Stdout, ' ');
+ LispWriteObject(NIL, CAR(CDR(frm)));
+ LispFputs(Stdout, ")\n");
+ }
+ DBG = LispReverse(DBG);
+ break;
+ case DebuggerContinue:
+ lisp__data.debug = LispDebugRun;
+ goto debugger_command_done;
+ case DebuggerFinish:
+ if (lisp__data.debug != LispDebugFinish) {
+ lisp__data.debug_step = lisp__data.debug_level - 2;
+ lisp__data.debug = LispDebugFinish;
+ }
+ else
+ lisp__data.debug_step = lisp__data.debug_level - 1;
+ goto debugger_command_done;
+ case DebuggerNext:
+ if (lisp__data.debug != LispDebugNext) {
+ lisp__data.debug = LispDebugNext;
+ lisp__data.debug_step = lisp__data.debug_level + 1;
+ }
+ goto debugger_command_done;
+ case DebuggerStep:
+ lisp__data.debug = LispDebugStep;
+ goto debugger_command_done;
+ }
+ continue;
+
+debugger_new_frame:
+ /* goto here with i as the new frame value, after error checking */
+ if (i != frame) {
+ frame = i;
+ for (frm = DBG, i = lisp__data.debug_level;
+ i > frame; frm = CDR(frm), i--)
+ ;
+ curframe = CAR(frm);
+ lisp__data.stack.base = FIXNUM_VALUE(CAR(CDR(CDR(curframe))));
+ lisp__data.env.length = FIXNUM_VALUE(CAR(CDR(CDR(CDR(curframe)))));
+ lisp__data.env.lex = FIXNUM_VALUE(CDR(CDR(CDR(CDR(curframe)))));
+ }
+debugger_print_frame:
+ LispFputc(Stdout, '#');
+ LispFputs(Stdout, format_integer(frame));
+ LispFputs(Stdout, "> (");
+ LispWriteObject(NIL, CAR(curframe));
+ LispFputc(Stdout, ' ');
+ LispWriteObject(NIL, CAR(CDR(curframe)));
+ LispFputs(Stdout, ")\n");
+ }
+
+debugger_command_done:
+ lisp__data.stack.base = envbase;
+ lisp__data.env.length = envlen;
+ lisp__data.env.lex = envlex;
+}
+
+static char *
+format_integer(int integer)
+{
+ static char buffer[16];
+
+ sprintf(buffer, "%d", integer);
+
+ return (buffer);
+}
+
+#endif /* DEBUGGER */
diff --git a/lisp/debugger.h b/lisp/debugger.h
new file mode 100644
index 0000000..98bd962
--- /dev/null
+++ b/lisp/debugger.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/debugger.h,v 1.7 2002/11/08 08:00:56 paulo Exp $ */
+
+#ifndef Lisp_debugger_h
+#define Lisp_debugger_h
+
+/*
+ * Definitions
+ */
+#define DBGPROMPT "DEBUG> "
+#ifdef DEBUGGER
+
+/*
+ * Types
+ */
+typedef enum _LispDebugState {
+ LispDebugUnspec, /* initial state */
+ LispDebugRun, /* just run, until breakpoint or error */
+ LispDebugFinish, /* evaluates until selected form is finished */
+ LispDebugNext, /* evaluate form */
+ LispDebugStep /* evaluate form, and step on subforms */
+} LispDebugState;
+
+typedef enum _LispDebugCall {
+ LispDebugCallBegin,
+ LispDebugCallEnd,
+ LispDebugCallFatal,
+ LispDebugCallWatch /* just remove watched variables that lost context */
+} LispDebugCall;
+
+typedef enum _LispDebugBreak {
+ LispDebugBreakFunction,
+ LispDebugBreakVariable
+} LispDebugBreak;
+
+#include "private.h"
+
+/*
+ * Prototypes
+ */
+void LispDebugger(LispDebugCall, LispObj*, LispObj*);
+
+#endif /* DEBUGGER */
+#endif /* Lisp_debugger_h */
diff --git a/lisp/env.c b/lisp/env.c
new file mode 100644
index 0000000..a5da90c
--- /dev/null
+++ b/lisp/env.c
@@ -0,0 +1,151 @@
+/*
+ * Provide setenv() and unsetenv() on platforms that don't have them.
+ * From FreeBSD's libc.
+ */
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/env.c,v 1.1 2002/03/05 03:52:34 dawes Exp $ */
+
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+extern char **environ;
+
+extern int setenv(const char *name, const char *value, int overwrite);
+extern void unsetenv(const char *name);
+
+static char *
+findenv(const char *name, int *offset)
+{
+ int len, i;
+ const char *np;
+ char **p, *cp;
+
+ if (name == NULL || environ == NULL)
+ return NULL;
+
+ for (np = name; *np && *np != '='; ++np)
+ continue;
+ len = np - name;
+ for (p = environ; (cp = *p) != NULL; ++p) {
+ for (np = name, i = len; i && *cp; i--)
+ if (*cp++ != *np++)
+ break;
+ if (i == 0 && *cp++ == '=') {
+ *offset = p - environ;
+ return cp;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * setenv --
+ * Set the value of the environmental variable "name" to be
+ * "value". If overwrite is set, replace any current value.
+ */
+
+int
+setenv(const char *name, const char *value, int overwrite)
+{
+ static char **alloced; /* if allocated space before */
+ char *c;
+ int l_value, offset;
+
+ if (*value == '=') /* no '=' in value */
+ ++value;
+ l_value = strlen(value);
+ if ((c = findenv(name, &offset))) { /* find if already exists */
+ if (!overwrite)
+ return 0;
+ if (strlen(c) >= l_value) { /* old larger; copy over */
+ while ((*c++ = *value++))
+ ;
+ return 0;
+ }
+ } else { /* create new slot */
+ int cnt;
+ char **p;
+
+ for (p = environ, cnt = 0; *p; ++p, ++cnt)
+ ;
+ if (alloced == environ) { /* just increase size */
+ p = (char **)realloc((char *)environ,
+ sizeof(char *) * (cnt + 2));
+ if (!p)
+ return -1;
+ alloced = environ = p;
+ } else { /* get new space */
+ /* copy old entries into it */
+ p = malloc(sizeof(char *) * (cnt + 2));
+ if (!p)
+ return -1;
+ memcpy(p, environ, cnt * sizeof(char *));
+ alloced = environ = p;
+ }
+ environ[cnt + 1] = NULL;
+ offset = cnt;
+ }
+ for (c = (char *)name; *c && *c != '='; ++c) /* no '=' in name */
+ ;
+ if (!(environ[offset] = /* name + '=' + value */
+ malloc((int)(c - name) + l_value + 2)))
+ return -1;
+ for (c = environ[offset]; (*c = *name++) && *c != '='; ++c)
+ ;
+ for (*c++ = '='; (*c++ = *value++); )
+ ;
+ return 0;
+}
+
+/*
+ * unsetenv(name) --
+ * Delete environmental variable "name".
+ */
+
+void
+unsetenv(const char *name)
+{
+ char **p;
+ int offset;
+
+ while (findenv(name, &offset)) /* if set multiple times */
+ for (p = &environ[offset];; ++p)
+ if (!(*p = *(p + 1)))
+ break;
+}
+
diff --git a/lisp/format.c b/lisp/format.c
new file mode 100644
index 0000000..aa593d6
--- /dev/null
+++ b/lisp/format.c
@@ -0,0 +1,2121 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/format.c,v 1.28 2002/11/30 23:13:11 paulo Exp $ */
+
+#include "io.h"
+#include "write.h"
+#include "format.h"
+#include <ctype.h>
+
+#define MAXFMT 8
+#define NOERROR 0
+
+/* parse error codes */
+#define PARSE_2MANYPARM 1 /* too many directive parameters */
+#define PARSE_2MANYATS 2 /* more than one @ in directive */
+#define PARSE_2MANYCOLS 3 /* more than one : in directive */
+#define PARSE_NOARGSLEFT 4 /* no arguments left to format */
+#define PARSE_BADFMTARG 5 /* argument is not an integer or char */
+#define PARSE_BADDIRECTIVE 6 /* unknown format directive */
+#define PARSE_BADINTEGER 7 /* bad integer representation */
+
+/* merge error codes */
+#define MERGE_2MANY 1 /* too many parameters to directive */
+#define MERGE_NOCHAR 2 /* parameter must be a character */
+#define MERGE_NOINT 3 /* parameter must be an integer */
+
+/* generic error codes */
+#define GENERIC_RADIX 1 /* radix not in range 2-36 */
+#define GENERIC_NEGATIVE 2 /* parameter is negative */
+#define GENERIC_BADSTRING 3 /* argument is not a string */
+#define GENERIC_BADLIST 4 /* argument is not a list */
+
+#define IF_SPECIFIED(arg) (arg).specified ? &((arg).value) : NULL
+
+#define UPANDOUT_NORMAL 1
+#define UPANDOUT_COLLON 2
+#define UPANDOUT_HASH 4 /* only useful inside a ~{ iteration
+ * forces loop finalization. */
+
+#define ITERATION_NORMAL 1
+#define ITERATION_LAST 2
+
+/*
+ * Types
+ */
+/* parameter to format */
+typedef struct {
+ unsigned int achar : 1; /* value was specified as a character */
+ unsigned int specified : 1; /* set if value was specified */
+ unsigned int offset : 30; /* offset in format string, for error printing */
+ int value;
+} FmtArg;
+
+/* information about format parameters */
+typedef struct {
+ unsigned int atsign : 1; /* @ specified */
+ unsigned int collon : 1; /* : specified */
+ unsigned int command : 8; /* the format command */
+ unsigned int count : 4; /* number of arguments processed */
+ unsigned int offset : 10; /* offset in format string, for error printing */
+ char *base, *format;
+ FmtArg arguments[MAXFMT];
+} FmtArgs;
+
+/* used for combining default format parameter values */
+typedef struct {
+ int achar;
+ int value;
+} FmtDef;
+
+/* number of default format parameter values and defaults */
+typedef struct {
+ int count;
+ FmtDef defaults[MAXFMT];
+} FmtDefs;
+
+/* used on recursive calls to LispFormat */
+typedef struct {
+ FmtArgs args;
+ LispObj *base_arguments; /* pointer to first format argument */
+ int total_arguments; /* number of objects in base_arguments */
+ char **format; /* if need to update format string pointer */
+ LispObj **object; /* CAR(arguments), for plural check */
+ LispObj **arguments; /* current element of base_arguments */
+ int *num_arguments; /* number of arguments after arguments */
+ int upandout; /* information for recursive calls */
+ int iteration; /* only set if in ~:{... or ~:@{ and in the
+ * last argument list, hint for upandout */
+} FmtInfo;
+
+/*
+ * Prototypes
+ */
+static void merge_arguments(FmtArgs*, FmtDefs*, int*);
+static char *parse_arguments(char*, FmtArgs*, int*, LispObj**, int*);
+static void merge_error(FmtArgs*, int);
+static void parse_error(FmtArgs*, int);
+static void generic_error(FmtArgs*, int);
+static void format_error(FmtArgs*, char*);
+
+static int format_object(LispObj*, LispObj*);
+
+static void format_ascii(LispObj*, LispObj*, FmtArgs*);
+static void format_in_radix(LispObj*, LispObj*, int, FmtArgs*);
+static void format_radix_special(LispObj*, LispObj*, FmtArgs*);
+static void format_roman(LispObj*, LispObj*, FmtArgs*);
+static void format_english(LispObj*, LispObj*, FmtArgs*);
+static void format_character(LispObj*, LispObj*, FmtArgs*);
+static void format_fixed_float(LispObj*, LispObj*, FmtArgs*);
+static void format_exponential_float(LispObj*, LispObj*, FmtArgs*);
+static void format_general_float(LispObj*, LispObj*, FmtArgs*);
+static void format_dollar_float(LispObj*, LispObj*, FmtArgs*);
+static void format_tabulate(LispObj*, FmtArgs*);
+
+static void format_goto(FmtInfo*);
+static void format_indirection(LispObj*, LispObj*, FmtInfo*);
+
+static void list_formats(FmtInfo*, int, char**, char***, int*, int*, int*, int*);
+static void free_formats(char**, int);
+
+static void format_case_conversion(LispObj*, FmtInfo*);
+static void format_conditional(LispObj*, FmtInfo*);
+static void format_iterate(LispObj*, FmtInfo*);
+static void format_justify(LispObj*, FmtInfo*);
+
+static void LispFormat(LispObj*, FmtInfo*);
+
+/*
+ * Initialization
+ */
+static FmtDefs AsciiDefs = {
+ 4,
+ {
+ {0, 0}, /* mincol */
+ {0, 1}, /* colinc */
+ {0, 0}, /* minpad */
+ {1, ' '}, /* padchar */
+ },
+};
+
+static FmtDefs IntegerDefs = {
+ 4,
+ {
+ {0, 0}, /* mincol */
+ {1, ' '}, /* padchar */
+ {1, ','}, /* commachar */
+ {0, 3}, /* commainterval */
+ },
+};
+
+static FmtDefs RadixDefs = {
+ 5,
+ {
+ {0, 10}, /* radix */
+ {0, 0}, /* mincol */
+ {1, ' '}, /* padchar */
+ {1, ','}, /* commachar */
+ {0, 3}, /* commainterval */
+ },
+};
+
+static FmtDefs NoneDefs = {
+ 0,
+};
+
+static FmtDefs FixedFloatDefs = {
+ 5,
+ {
+ {0, 0}, /* w */
+ {0, 16}, /* d */
+ {0, 0}, /* k */
+ {1, '\0'}, /* overflowchar */
+ {1, ' '}, /* padchar */
+ },
+};
+
+static FmtDefs ExponentialFloatDefs = {
+ 7,
+ {
+ {0, 0}, /* w */
+ {0, 16}, /* d */
+ {0, 0}, /* e */
+ {0, 1}, /* k */
+ {1, '\0'}, /* overflowchar */
+ {1, ' '}, /* padchar */
+ {1, 'E'}, /* exponentchar */
+ /* XXX if/when more than one float format,
+ * should default to object type */
+ },
+};
+
+static FmtDefs DollarFloatDefs = {
+ 4,
+ {
+ {0, 2}, /* d */
+ {0, 1}, /* n */
+ {0, 0}, /* w */
+ {1, ' '}, /* padchar */
+ },
+};
+
+static FmtDefs OneDefs = {
+ 1,
+ {
+ {0, 1},
+ },
+};
+
+static FmtDefs TabulateDefs = {
+ 2,
+ {
+ {0, 0}, /* colnum */
+ {0, 1}, /* colinc */
+ },
+};
+
+extern LispObj *Oprint_escape;
+
+/*
+ * Implementation
+ */
+static void
+merge_arguments(FmtArgs *arguments, FmtDefs *defaults, int *code)
+{
+ int count;
+ FmtDef *defaul;
+ FmtArg *argument;
+
+ defaul = &(defaults->defaults[0]);
+ argument = &(arguments->arguments[0]);
+ for (count = 0; count < defaults->count; count++, argument++, defaul++) {
+ if (count >= arguments->count)
+ argument->specified = 0;
+ if (argument->specified) {
+ if (argument->achar != defaul->achar) {
+ *code = defaul->achar ? MERGE_NOCHAR : MERGE_NOINT;
+ arguments->offset = argument->offset;
+ return;
+ }
+ }
+ else {
+ argument->specified = 0;
+ argument->achar = defaul->achar;
+ argument->value = defaul->value;
+ }
+ }
+
+ /* check if extra arguments were provided */
+ if (arguments->count > defaults->count)
+ *code = MERGE_2MANY;
+}
+
+/* the pointer arguments may be null, useful when just testing/parsing
+ * the directive parameters */
+static char *
+parse_arguments(char *format, FmtArgs *arguments,
+ int *num_objects, LispObj **objects, int *code)
+{
+ int test;
+ char *ptr;
+ FmtArg *argument;
+ unsigned int tmpcmd = 0;
+
+ /* initialize */
+ test = objects == NULL || code == NULL || num_objects == NULL;
+ ptr = format;
+ argument = &(arguments->arguments[0]);
+ arguments->atsign = arguments->collon = arguments->command = 0;
+
+ /* parse format parameters */
+ for (arguments->count = 0;; arguments->count++) {
+ arguments->offset = ptr - format + 1;
+ if (arguments->count >= MAXFMT) {
+ if (!test)
+ *code = PARSE_2MANYPARM;
+ return (ptr);
+ }
+ if (*ptr == '\'') { /* character parameter value */
+ ++ptr; /* skip ' */
+ argument->achar = argument->specified = 1;
+ argument->value = *ptr++;
+ }
+ else if (*ptr == ',') { /* use default parameter value */
+ argument->achar = 0;
+ argument->specified = 0;
+ /* don't increment ptr, will be incremented below */
+ }
+ else if (*ptr == '#') { /* number of arguments is value */
+ ++ptr; /* skip # */
+ argument->achar = 0;
+ argument->specified = 1;
+ if (!test)
+ argument->value = *num_objects;
+ }
+ else if (*ptr == 'v' ||
+ *ptr == 'V') { /* format object argument is value */
+ LispObj *object;
+
+ ++ptr; /* skip V */
+ if (!test) {
+ if (!CONSP(*objects)) {
+ *code = PARSE_NOARGSLEFT;
+ return (ptr);
+ }
+ object = CAR((*objects));
+ if (FIXNUMP(object)) {
+ argument->achar = 0;
+ argument->specified = 1;
+ argument->value = FIXNUM_VALUE(object);
+ }
+ else if (SCHARP(object)) {
+ argument->achar = argument->specified = 1;
+ argument->value = SCHAR_VALUE(object);
+ }
+ else {
+ *code = PARSE_BADFMTARG;
+ return (ptr);
+ }
+ *objects = CDR(*objects);
+ --*num_objects;
+ }
+ }
+ else if (isdigit(*ptr) ||
+ *ptr == '-' || *ptr == '+') { /* integer parameter value */
+ int sign;
+
+ argument->achar = 0;
+ argument->specified = 1;
+ if (!isdigit(*ptr)) {
+ sign = *ptr++ == '-';
+ }
+ else
+ sign = 0;
+ if (!test && !isdigit(*ptr)) {
+ *code = PARSE_BADINTEGER;
+ return (ptr);
+ }
+ argument->value = *ptr++ - '0';
+ while (isdigit(*ptr)) {
+ argument->value = (argument->value * 10) + (*ptr++ - '0');
+ if (argument->value > 65536) {
+ if (!test) {
+ *code = PARSE_BADINTEGER;
+ return (ptr);
+ }
+ }
+ }
+ if (sign)
+ argument->value = -argument->value;
+ }
+ else /* no more arguments to format */
+ break;
+
+ if (*ptr == ',')
+ ++ptr;
+
+ /* remember offset of format parameter, for better error printing */
+ argument->offset = arguments->offset;
+ argument++;
+ }
+
+ /* check for extra flags */
+ for (;;) {
+ if (*ptr == '@') { /* check for special parameter atsign */
+ if (arguments->atsign) {
+ if (!test) {
+ *code = PARSE_2MANYATS;
+ return (ptr);
+ }
+ }
+ ++ptr;
+ ++arguments->offset;
+ arguments->atsign = 1;
+ }
+ else if (*ptr == ':') { /* check for special parameter collon */
+ if (arguments->collon) {
+ if (!test) {
+ *code = PARSE_2MANYCOLS;
+ return (ptr);
+ }
+ }
+ ++ptr;
+ ++arguments->offset;
+ arguments->collon = 1;
+ }
+ else /* next value is format command */
+ break;
+ }
+
+ if (!test)
+ *code = NOERROR;
+ arguments->command = *ptr++;
+ tmpcmd = arguments->command;
+ if (islower(tmpcmd))
+ arguments->command = toupper(tmpcmd);
+ ++arguments->offset;
+
+ return (ptr);
+}
+
+static void
+parse_error(FmtArgs *args, int code)
+{
+ static char *errors[] = {
+ NULL,
+ "too many parameters to directive",
+ "too many @ parameters",
+ "too many : parameters",
+ "no arguments left to format",
+ "argument is not a fixnum integer or a character",
+ "unknown format directive",
+ "parameter is not a fixnum integer",
+ };
+
+ format_error(args, errors[code]);
+}
+
+static void
+merge_error(FmtArgs *args, int code)
+{
+ static char *errors[] = {
+ NULL,
+ "too many parameters to directive",
+ "argument must be a character",
+ "argument must be a fixnum integer",
+ };
+
+ format_error(args, errors[code]);
+}
+
+static void
+generic_error(FmtArgs *args, int code)
+{
+ static char *errors[] = {
+ NULL,
+ "radix must be in the range 2 to 36, inclusive",
+ "parameter must be positive",
+ "argument must be a string",
+ "argument must be a list",
+ };
+
+ format_error(args, errors[code]);
+}
+
+static void
+format_error(FmtArgs *args, char *str)
+{
+ char *message;
+ int errorlen, formatlen;
+
+ /* number of bytes of format to be printed */
+ formatlen = (args->format - args->base) + args->offset;
+
+ /* length of specific error message */
+ errorlen = strlen(str) + 1; /* plus '\n' */
+
+ /* XXX allocate string with LispMalloc,
+ * so that it will be freed in LispTopLevel */
+ message = LispMalloc(formatlen + errorlen + 1);
+
+ sprintf(message, "%s\n", str);
+ memcpy(message + errorlen, args->base, formatlen);
+ message[errorlen + formatlen] = '\0';
+
+ LispDestroy("FORMAT: %s", message);
+}
+
+static int
+format_object(LispObj *stream, LispObj *object)
+{
+ int length;
+
+ length = LispWriteObject(stream, object);
+
+ return (length);
+}
+
+static void
+format_ascii(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ GC_ENTER();
+ LispObj *string = NIL;
+ int length = 0,
+ atsign = args->atsign,
+ collon = args->collon,
+ mincol = args->arguments[0].value,
+ colinc = args->arguments[1].value,
+ minpad = args->arguments[2].value,
+ padchar = args->arguments[3].value;
+
+ /* check/correct arguments */
+ if (mincol < 0)
+ mincol = 0;
+ if (colinc < 0)
+ colinc = 1;
+ if (minpad < 0)
+ minpad = 0;
+ /* XXX pachar can be the null character? */
+
+ if (object == NIL)
+ length = collon ? 2 : 3; /* () or NIL */
+
+ /* left padding */
+ if (atsign) {
+ /* if length not yet known */
+ if (object == NIL) {
+ string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
+ GC_PROTECT(string);
+ length = LispWriteObject(string, object);
+ }
+
+ /* output minpad characters at left */
+ if (minpad) {
+ length += minpad;
+ LispWriteChars(stream, padchar, minpad);
+ }
+
+ if (colinc) {
+ /* puts colinc spaces at a time,
+ * until at least mincol chars out */
+ while (length < mincol) {
+ LispWriteChars(stream, padchar, colinc);
+ length += colinc;
+ }
+ }
+ }
+
+ if (object == NIL) {
+ if (collon)
+ LispWriteStr(stream, "()", 2);
+ else
+ LispWriteStr(stream, Snil, 3);
+ }
+ else {
+ /* if string is not NIL, atsign was specified
+ * and object printed to string */
+ if (string == NIL)
+ length = format_object(stream, object);
+ else {
+ int size;
+ char *str = LispGetSstring(SSTREAMP(string), &size);
+
+ LispWriteStr(stream, str, size);
+ }
+ }
+
+ /* right padding */
+ if (!atsign) {
+ /* output minpad characters at left */
+ if (minpad) {
+ length += minpad;
+ LispWriteChars(stream, padchar, minpad);
+ }
+ if (colinc) {
+ /* puts colinc spaces at a time,
+ * until at least mincol chars out */
+ while (length < mincol) {
+ LispWriteChars(stream, padchar, colinc);
+ length += colinc;
+ }
+ }
+ }
+
+ GC_LEAVE();
+}
+
+/* assumes radix is 0 or in range 2 - 36 */
+static void
+format_in_radix(LispObj *stream, LispObj *object, int radix, FmtArgs *args)
+{
+ if (INTEGERP(object)) {
+ int i, check, atsign, collon, mincol, padchar, commachar, commainterval;
+
+ i = check = (radix == 0);
+ atsign = args->atsign;
+ collon = args->collon;
+ if (radix == 0) {
+ radix = args->arguments[0].value;
+ if (radix < 2 || radix > 36) {
+ args->offset = args->arguments[0].offset;
+ generic_error(args, GENERIC_RADIX);
+ }
+ }
+ mincol = args->arguments[i++].value;
+ padchar = args->arguments[i++].value;
+ commachar = args->arguments[i++].value;
+ commainterval = args->arguments[i++].value;
+
+ LispFormatInteger(stream, object, radix, atsign, collon,
+ mincol, padchar, commachar, commainterval);
+ }
+ else
+ format_object(stream, object);
+}
+
+static void
+format_radix_special(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ if (FIXNUMP(object)) {
+ if (args->atsign)
+ format_roman(stream, object, args);
+ else
+ format_english(stream, object, args);
+ }
+ else
+ format_object(stream, object);
+}
+
+static void
+format_roman(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ long value = 0;
+ int cando, new_roman = args->collon == 0;
+
+ if (FIXNUMP(object)) {
+ value = FIXNUM_VALUE(object);
+ if (new_roman)
+ cando = value >= 1 && value <= 3999;
+ else
+ cando = value >= 1 && value <= 4999;
+ }
+ else
+ cando = 0;
+
+ if (cando)
+ LispFormatRomanInteger(stream, value, new_roman);
+ else
+ format_object(stream, object);
+}
+
+static void
+format_english(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ int cando;
+ long number = 0;
+
+ if (FIXNUMP(object)) {
+ number = FIXNUM_VALUE(object);
+ cando = number >= -999999999 && number <= 999999999;
+ }
+ else
+ cando = 0;
+
+ if (cando)
+ LispFormatEnglishInteger(stream, number, args->collon);
+ else
+ format_object(stream, object);
+}
+
+static void
+format_character(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ if (SCHARP(object))
+ LispFormatCharacter(stream, object, args->atsign, args->collon);
+ else
+ format_object(stream, object);
+}
+
+static void
+format_fixed_float(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ if (FLOATP(object))
+ LispFormatFixedFloat(stream, object, args->atsign,
+ args->arguments[0].value,
+ IF_SPECIFIED(args->arguments[1]),
+ args->arguments[2].value,
+ args->arguments[3].value,
+ args->arguments[4].value);
+ else
+ format_object(stream, object);
+}
+
+static void
+format_exponential_float(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ if (FLOATP(object))
+ LispFormatExponentialFloat(stream, object, args->atsign,
+ args->arguments[0].value,
+ IF_SPECIFIED(args->arguments[1]),
+ args->arguments[2].value,
+ args->arguments[3].value,
+ args->arguments[4].value,
+ args->arguments[5].value,
+ args->arguments[6].value);
+ else
+ format_object(stream, object);
+}
+
+static void
+format_general_float(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ if (FLOATP(object))
+ LispFormatGeneralFloat(stream, object, args->atsign,
+ args->arguments[0].value,
+ IF_SPECIFIED(args->arguments[1]),
+ args->arguments[2].value,
+ args->arguments[3].value,
+ args->arguments[4].value,
+ args->arguments[5].value,
+ args->arguments[6].value);
+ else
+ format_object(stream, object);
+}
+
+static void
+format_dollar_float(LispObj *stream, LispObj *object, FmtArgs *args)
+{
+ if (FLOATP(object))
+ LispFormatDollarFloat(stream, object,
+ args->atsign, args->collon,
+ args->arguments[0].value,
+ args->arguments[1].value,
+ args->arguments[2].value,
+ args->arguments[3].value);
+ else
+ format_object(stream, object);
+}
+
+static void
+format_tabulate(LispObj *stream, FmtArgs *args)
+{
+ int atsign = args->atsign,
+ colnum = args->arguments[0].value,
+ colinc = args->arguments[1].value,
+ column;
+
+ column = LispGetColumn(stream);
+
+ if (atsign) {
+ /* relative tabulation */
+ if (colnum > 0) {
+ LispWriteChars(stream, ' ', colnum);
+ column += colnum;
+ }
+ /* tabulate until at a multiple of colinc */
+ if (colinc > 0)
+ LispWriteChars(stream, ' ', colinc - (column % colinc));
+ }
+ else {
+ /* if colinc not specified, just move to given column */
+ if (colinc <= 0)
+ LispWriteChars(stream, ' ', column - colnum);
+ else {
+ /* always output at least colinc spaces */
+ do {
+ LispWriteChars(stream, ' ', colinc);
+ colnum -= colinc;
+ } while (colnum > column);
+ }
+ }
+}
+
+static void
+format_goto(FmtInfo *info)
+{
+ int count, num_arguments;
+ LispObj *object, *arguments;
+
+ /* number of arguments to ignore or goto offset */
+ count = info->args.arguments[0].value;
+ if (count < 0)
+ generic_error(&(info->args), GENERIC_NEGATIVE);
+
+ if (info->args.atsign) {
+ /* absolute goto */
+
+ /* if not specified, defaults to zero */
+ if (!(info->args.arguments[0].specified))
+ count = 0;
+
+ /* if offset too large */
+ if (count > info->total_arguments)
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+ else if (count != info->total_arguments - *(info->num_arguments)) {
+ /* calculate new parameters */
+ object = NIL;
+ arguments = info->base_arguments;
+ num_arguments = info->total_arguments - count;
+
+ for (; count > 0; count--, arguments = CDR(arguments))
+ object = CAR(arguments);
+
+ /* update format information */
+ *(info->object) = object;
+ *(info->arguments) = arguments;
+ *(info->num_arguments) = num_arguments;
+ }
+ }
+ else if (count) {
+ /* relative goto, ignore or go back count arguments */
+
+ /* prepare to update parameters */
+ arguments = *(info->arguments);
+ num_arguments = *(info->num_arguments);
+
+ /* go back count arguments? */
+ if (info->args.collon)
+ count = -count;
+
+ num_arguments -= count;
+
+ if (count > 0) {
+ if (count > *(info->num_arguments))
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+
+ object = *(info->object);
+ for (; count > 0; count--, arguments = CDR(arguments))
+ object = CAR(arguments);
+ }
+ else { /* count < 0 */
+ if (info->total_arguments + count - *(info->num_arguments) < 0)
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+
+ object = NIL;
+ arguments = info->base_arguments;
+ for (count = 0; count < info->total_arguments - num_arguments;
+ count++, arguments = CDR(arguments))
+ object = CAR(arguments);
+ }
+
+ /* update format parameters */
+ *(info->object) = object;
+ *(info->arguments) = arguments;
+ *(info->num_arguments) = num_arguments;
+ }
+}
+
+static void
+format_indirection(LispObj *stream, LispObj *format, FmtInfo *info)
+{
+ char *string;
+ LispObj *object;
+ FmtInfo indirect_info;
+
+ if (!STRINGP(format))
+ generic_error(&(info->args), GENERIC_BADSTRING);
+ string = THESTR(format);
+
+ /* most information is the same */
+ memcpy(&indirect_info, info, sizeof(FmtInfo));
+
+ /* set new format string */
+ indirect_info.args.base = indirect_info.args.format = string;
+ indirect_info.format = &string;
+
+ if (info->args.atsign) {
+ /* use current arguments */
+
+ /* do the indirect format */
+ LispFormat(stream, &indirect_info);
+ }
+ else {
+ /* next argument is the recursive call arguments */
+
+ int num_arguments;
+
+ /* it is valid to not have a list following string, as string may
+ * not have format directives */
+ if (CONSP(*(indirect_info.arguments)))
+ object = CAR(*(indirect_info.arguments));
+ else
+ object = NIL;
+
+ if (!LISTP(object) || !CONSP(*(info->arguments)))
+ generic_error(&(info->args), GENERIC_BADLIST);
+
+ /* update information now */
+ *(info->object) = object;
+ *(info->arguments) = CDR(*(info->arguments));
+ *(info->num_arguments) -= 1;
+
+ /* set arguments for recursive call */
+ indirect_info.base_arguments = object;
+ indirect_info.arguments = &object;
+ for (num_arguments = 0; CONSP(object); object = CDR(object))
+ ++num_arguments;
+
+ /* note that indirect_info.arguments is a pointer to "object",
+ * keep it pointing to the correct object */
+ object = indirect_info.base_arguments;
+ indirect_info.total_arguments = num_arguments;
+ indirect_info.num_arguments = &num_arguments;
+
+ /* do the indirect format */
+ LispFormat(stream, &indirect_info);
+ }
+}
+
+/* update pointers to a list of format strings:
+ * for '(' and '{' only one list is required
+ * for '[' and '<' more than one may be returned
+ * has_default is only meaningful for '[' and '<'
+ * comma_width and line_width are only meaningful to '<', and
+ * only valid if has_default set
+ * if the string is finished prematurely, LispDestroy is called
+ * format_ptr is updated to the correct pointer in the "main" format string
+ */
+static void
+list_formats(FmtInfo *info, int command, char **format_ptr,
+ char ***format_list, int *format_count, int *has_default,
+ int *comma_width, int *line_width)
+{
+ /* instead of processing the directives recursively, just separate the
+ * input formats in separate strings, then see if one of then need to
+ * be used */
+ FmtArgs args;
+ int counters[] = { 0, 0, 0, 0};
+ /* '[', '(', '{', '<' */
+ char *format, *next_format, *start, **formats;
+ int num_formats, format_index, separator, add_format;
+
+ /* initialize */
+ formats = NULL;
+ num_formats = format_index = 0;
+ if (has_default != NULL)
+ *has_default = 0;
+ if (comma_width != NULL)
+ *comma_width = 0;
+ if (line_width != NULL)
+ *line_width = 0;
+ format = start = next_format = *format_ptr;
+ switch (command) {
+ case '[': counters[0] = 1; format_index = 0; break;
+ case '(': counters[1] = 1; format_index = 1; break;
+ case '{': counters[2] = 1; format_index = 2; break;
+ case '<': counters[3] = 1; format_index = 3; break;
+ }
+
+#define LIST_FORMATS_ADD 1
+#define LIST_FORMATS_DONE 2
+
+ /* fill list of format options to conditional */
+ while (*format) {
+ if (*format == '~') {
+ separator = add_format = 0;
+ args.format = format + 1;
+ next_format = parse_arguments(format + 1, &args, NULL, NULL, NULL);
+ switch (args.command) {
+ case '[': ++counters[0]; break;
+ case ']': --counters[0]; break;
+ case '(': ++counters[1]; break;
+ case ')': --counters[1]; break;
+ case '{': ++counters[2]; break;
+ case '}': --counters[2]; break;
+ case '<': ++counters[3]; break;
+ case '>': --counters[3]; break;
+ case ';': separator = 1; break;
+ }
+
+ /* check if a new format string must be added */
+ if (separator && counters[format_index] == 1 &&
+ (command == '[' || command == '<'))
+ add_format = LIST_FORMATS_ADD;
+ else if (counters[format_index] == 0)
+ add_format = LIST_FORMATS_DONE;
+
+ if (add_format) {
+ int length = format - start;
+
+ formats = LispRealloc(formats,
+ (num_formats + 1) * sizeof(char*));
+
+ formats[num_formats] = LispMalloc(length + 1);
+ strncpy(formats[num_formats], start, length);
+ formats[num_formats][length] = '\0';
+ ++num_formats;
+ /* loop finished? */
+ if (add_format == LIST_FORMATS_DONE)
+ break;
+ else if (command == '[' && has_default != NULL)
+ /* will be set only for the last parameter, what is
+ * expected, just don't warn about it in the incorrect
+ * place */
+ *has_default = args.collon != 0;
+ else if (command == '<' && num_formats == 1) {
+ /* if the first parameter to '<', there may be overrides
+ * to comma-width and line-width */
+ if (args.collon && has_default != NULL) {
+ *has_default = 1;
+ if (comma_width != NULL &&
+ args.arguments[0].specified &&
+ !args.arguments[0].achar)
+ *comma_width = args.arguments[0].value;
+ if (line_width != NULL &&
+ args.arguments[1].specified &&
+ !args.arguments[1].achar)
+ *line_width = args.arguments[1].value;
+ }
+ }
+ start = next_format;
+ }
+ format = next_format;
+ }
+ else
+ ++format;
+ }
+
+ /* check if format string did not finish prematurely */
+ if (counters[format_index] != 0) {
+ char error_message[64];
+
+ sprintf(error_message, "expecting ~%c", command);
+ format_error(&(info->args), error_message);
+ }
+
+ /* update pointers */
+ *format_list = formats;
+ *format_count = num_formats;
+ *format_ptr = next_format;
+}
+
+static void
+free_formats(char **formats, int num_formats)
+{
+ if (num_formats) {
+ while (--num_formats >= 0)
+ LispFree(formats[num_formats]);
+ LispFree(formats);
+ }
+}
+
+static void
+format_case_conversion(LispObj *stream, FmtInfo *info)
+{
+ GC_ENTER();
+ LispObj *string;
+ FmtInfo case_info;
+ char *str, *ptr;
+ char *format, *next_format, **formats;
+ int atsign, collon, num_formats, length;
+
+ atsign = info->args.atsign;
+ collon = info->args.collon;
+
+ /* output to a string, before case conversion */
+ string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
+ GC_PROTECT(string);
+
+ /* most information is the same */
+ memcpy(&case_info, info, sizeof(FmtInfo));
+
+ /* list formats */
+ next_format = *(info->format);
+ list_formats(info, '(', &next_format, &formats, &num_formats,
+ NULL, NULL, NULL);
+
+ /* set new format string */
+ format = formats[0];
+ case_info.args.base = case_info.args.format = format;
+ case_info.format = &format;
+
+ /* format text to string */
+ LispFormat(string, &case_info);
+
+ str = ptr = LispGetSstring(SSTREAMP(string), &length);
+
+ /* do case conversion */
+ if (!atsign && !collon) {
+ /* convert all upercase to lowercase */
+ for (; *ptr; ptr++) {
+ if (isupper(*ptr))
+ *ptr = tolower(*ptr);
+ }
+ }
+ else if (atsign && collon) {
+ /* convert all lowercase to upercase */
+ for (; *ptr; ptr++) {
+ if (islower(*ptr))
+ *ptr = toupper(*ptr);
+ }
+ }
+ else {
+ int upper = 1;
+
+ /* skip non-alphanumeric characters */
+ for (; *ptr; ptr++)
+ if (isalnum(*ptr))
+ break;
+
+ /* capitalize words */
+ for (; *ptr; ptr++) {
+ if (isalnum(*ptr)) {
+ if (upper) {
+ if (islower(*ptr))
+ *ptr = toupper(*ptr);
+ upper = 0;
+ }
+ else if (isupper(*ptr))
+ *ptr = tolower(*ptr);
+ }
+ else
+ upper = collon;
+ /* if collon, capitalize all words, else just first word */
+ }
+ }
+
+ /* output case converted string */
+ LispWriteStr(stream, str, length);
+
+ /* temporary string stream is not necessary anymore */
+ GC_LEAVE();
+
+ /* free temporary memory */
+ free_formats(formats, num_formats);
+
+ /* this information always updated */
+ *(info->format) = next_format;
+}
+
+static void
+format_conditional(LispObj *stream, FmtInfo *info)
+{
+ LispObj *object, *arguments;
+ char *format, *next_format, **formats;
+ int choice, num_formats, has_default, num_arguments;
+
+ /* save information that may change */
+ object = *(info->object);
+ arguments = *(info->arguments);
+ num_arguments = *(info->num_arguments);
+
+ /* initialize */
+ choice = -1;
+ next_format = *(info->format);
+
+ /* list formats */
+ list_formats(info, '[',
+ &next_format, &formats, &num_formats, &has_default, NULL, NULL);
+
+ /* ~:[false;true] */
+ if (info->args.collon) {
+ /* one argument always consumed */
+ if (!CONSP(arguments))
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+ object = CAR(arguments);
+ arguments = CDR(arguments);
+ --num_arguments;
+ choice = object == NIL ? 0 : 1;
+ }
+ /* ~@[true] */
+ else if (info->args.atsign) {
+ /* argument consumed only if nil, but one must be available */
+ if (!CONSP(arguments))
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+ if (CAR(arguments) != NIL)
+ choice = 0;
+ else {
+ object = CAR(arguments);
+ arguments = CDR(arguments);
+ --num_arguments;
+ }
+ }
+ /* ~n[...~] */
+ else if (info->args.arguments[0].specified)
+ /* no arguments consumed */
+ choice = info->args.arguments[0].value;
+ /* ~[...~] */
+ else {
+ /* one argument consumed, it is the index in the available formats */
+ if (!CONSP(arguments))
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+ object = CAR(arguments);
+ arguments = CDR(arguments);
+ --num_arguments;
+ /* no error if it isn't a number? */
+ if (FIXNUMP(object))
+ choice = FIXNUM_VALUE(object);
+ }
+
+ /* update anything that may have changed */
+ *(info->object) = object;
+ *(info->arguments) = arguments;
+ *(info->num_arguments) = num_arguments;
+
+ /* if choice is out of range check if there is a default choice */
+ if (has_default && (choice < 0 || choice >= num_formats))
+ choice = num_formats - 1;
+
+ /* if one of the formats must be parsed */
+ if (choice >= 0 && choice < num_formats) {
+ FmtInfo conditional_info;
+
+ /* most information is the same */
+ memcpy(&conditional_info, info, sizeof(FmtInfo));
+
+ /* set new format string */
+ format = formats[choice];
+ conditional_info.args.base = conditional_info.args.format = format;
+ conditional_info.format = &format;
+
+ /* do the conditional format */
+ LispFormat(stream, &conditional_info);
+ }
+
+ /* free temporary memory */
+ free_formats(formats, num_formats);
+
+ /* this information always updated */
+ *(info->format) = next_format;
+}
+
+static void
+format_iterate(LispObj *stream, FmtInfo *info)
+{
+ FmtInfo iterate_info;
+ LispObj *object, *arguments, *iarguments, *iobject;
+ char *format, *next_format, *loop_format, **formats;
+ int num_arguments, iterate, iterate_max, has_max, has_min, inum_arguments,
+ num_formats;
+
+ /* save information that may change */
+ object = *(info->object);
+ arguments = *(info->arguments);
+ num_arguments = *(info->num_arguments);
+
+ /* initialize */
+ iterate = has_min = 0;
+ next_format = *(info->format);
+
+ /* if has_max set, iterate at most iterate_max times */
+ has_max = info->args.arguments[0].specified;
+ iterate_max = info->args.arguments[0].value;
+
+ /* list formats */
+ list_formats(info, '{', &next_format, &formats, &num_formats,
+ NULL, NULL, NULL);
+ loop_format = formats[0];
+
+ /* most information is the same */
+ memcpy(&iterate_info, info, sizeof(FmtInfo));
+
+ /* ~{...~} */
+ if (!info->args.atsign && !info->args.collon) {
+ /* next argument is the argument list for the iteration */
+
+ /* fetch argument list, must exist */
+ if (!CONSP(arguments))
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+ iarguments = object = CAR(arguments);
+ object = CAR(arguments);
+ arguments = CDR(arguments);
+ --num_arguments;
+
+ inum_arguments = 0;
+ if (CONSP(object)) {
+ /* count arguments to format */
+ for (iobject = object; CONSP(iobject); iobject = CDR(iobject))
+ ++inum_arguments;
+ }
+ else if (object != NIL)
+ generic_error(&(info->args), GENERIC_BADLIST);
+
+ iobject = NIL;
+
+ /* set new arguments to recursive calls */
+ iarguments = object;
+ iterate_info.base_arguments = iarguments;
+ iterate_info.total_arguments = inum_arguments;
+ iterate_info.object = &iobject;
+ iterate_info.arguments = &iarguments;
+ iterate_info.num_arguments = &inum_arguments;
+
+ /* iterate */
+ for (;; iterate++) {
+ /* if maximum iterations done or all arguments consumed */
+ if (has_max && iterate > iterate_max)
+ break;
+ else if (inum_arguments == 0 && (!has_min || iterate > 0))
+ break;
+
+ format = loop_format;
+
+ /* set new format string */
+ iterate_info.args.base = iterate_info.args.format = format;
+ iterate_info.format = &format;
+
+ /* information for possible ~^, in this case ~:^ is a noop */
+ iterate_info.iteration = ITERATION_NORMAL;
+
+ /* do the format */
+ LispFormat(stream, &iterate_info);
+
+ /* check for forced loop break */
+ if (iterate_info.upandout & UPANDOUT_HASH)
+ break;
+ }
+ }
+ /* ~:@{...~} */
+ else if (info->args.atsign && info->args.collon) {
+ /* every following argument is the argument list for the iteration */
+
+ /* iterate */
+ for (;; iterate++) {
+ /* if maximum iterations done or all arguments consumed */
+ if (has_max && iterate > iterate_max)
+ break;
+ else if (num_arguments == 0 && (!has_min || iterate > 0))
+ break;
+
+ /* fetch argument list, must exist */
+ if (!CONSP(arguments))
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+ iarguments = object = CAR(arguments);
+ object = CAR(arguments);
+ arguments = CDR(arguments);
+ --num_arguments;
+
+ inum_arguments = 0;
+ if (CONSP(object)) {
+ /* count arguments to format */
+ for (iobject = object; CONSP(iobject); iobject = CDR(iobject))
+ ++inum_arguments;
+ }
+ else if (object != NIL)
+ generic_error(&(info->args), GENERIC_BADLIST);
+
+ iobject = NIL;
+
+ /* set new arguments to recursive calls */
+ iarguments = object;
+ iterate_info.base_arguments = iarguments;
+ iterate_info.total_arguments = inum_arguments;
+ iterate_info.object = &iobject;
+ iterate_info.arguments = &iarguments;
+ iterate_info.num_arguments = &inum_arguments;
+
+ format = loop_format;
+
+ /* set new format string */
+ iterate_info.args.base = iterate_info.args.format = format;
+ iterate_info.format = &format;
+
+ /* information for possible ~^ */
+ iterate_info.iteration =
+ num_arguments > 0 ? ITERATION_NORMAL : ITERATION_LAST;
+
+ /* do the format */
+ LispFormat(stream, &iterate_info);
+
+ /* check for forced loop break */
+ if (iterate_info.upandout & UPANDOUT_HASH)
+ break;
+ }
+ }
+ /* ~:{...~} */
+ else if (info->args.collon) {
+ /* next argument is a list of lists */
+
+ LispObj *sarguments, *sobject;
+ int snum_arguments;
+
+ /* fetch argument list, must exist */
+ if (!CONSP(arguments))
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+ sarguments = object = CAR(arguments);
+ object = CAR(arguments);
+ arguments = CDR(arguments);
+ --num_arguments;
+
+ snum_arguments = 0;
+ if (CONSP(object)) {
+ /* count arguments to format */
+ for (sobject = object; CONSP(sobject); sobject = CDR(sobject))
+ ++snum_arguments;
+ }
+ else
+ generic_error(&(info->args), GENERIC_BADLIST);
+
+ /* iterate */
+ for (;; iterate++) {
+ /* if maximum iterations done or all arguments consumed */
+ if (has_max && iterate > iterate_max)
+ break;
+ else if (snum_arguments == 0 && (!has_min || iterate > 0))
+ break;
+
+ /* fetch argument list, must exist */
+ if (!CONSP(sarguments))
+ parse_error(&(info->args), PARSE_NOARGSLEFT);
+ iarguments = sobject = CAR(sarguments);
+ sobject = CAR(sarguments);
+ sarguments = CDR(sarguments);
+ --snum_arguments;
+
+ inum_arguments = 0;
+ if (CONSP(object)) {
+ /* count arguments to format */
+ for (iobject = sobject; CONSP(iobject); iobject = CDR(iobject))
+ ++inum_arguments;
+ }
+ else if (sobject != NIL)
+ generic_error(&(info->args), GENERIC_BADLIST);
+
+ iobject = NIL;
+
+ /* set new arguments to recursive calls */
+ iarguments = sobject;
+ iterate_info.base_arguments = iarguments;
+ iterate_info.total_arguments = inum_arguments;
+ iterate_info.object = &iobject;
+ iterate_info.arguments = &iarguments;
+ iterate_info.num_arguments = &inum_arguments;
+
+ format = loop_format;
+
+ /* set new format string */
+ iterate_info.args.base = iterate_info.args.format = format;
+ iterate_info.format = &format;
+
+ /* information for possible ~^ */
+ iterate_info.iteration =
+ snum_arguments > 0 ? ITERATION_NORMAL : ITERATION_LAST;
+
+ /* do the format */
+ LispFormat(stream, &iterate_info);
+
+ /* check for forced loop break */
+ if (iterate_info.upandout & UPANDOUT_HASH)
+ break;
+ }
+ }
+ /* ~@{...~} */
+ else if (info->args.atsign) {
+ /* current argument list is used */
+
+ /* set new arguments to recursive calls */
+ iterate_info.base_arguments = info->base_arguments;
+ iterate_info.total_arguments = info->total_arguments;
+ iterate_info.object = &object;
+ iterate_info.arguments = &arguments;
+ iterate_info.num_arguments = &num_arguments;
+
+ for (;; iterate++) {
+ /* if maximum iterations done or all arguments consumed */
+ if (has_max && iterate > iterate_max)
+ break;
+ else if (num_arguments == 0 && (!has_min || iterate > 0))
+ break;
+
+ format = loop_format;
+
+ /* set new format string */
+ iterate_info.args.base = iterate_info.args.format = format;
+ iterate_info.format = &format;
+
+ /* information for possible ~^, in this case ~:^ is a noop */
+ iterate_info.iteration = ITERATION_NORMAL;
+
+ /* do the format */
+ LispFormat(stream, &iterate_info);
+
+ /* check for forced loop break */
+ if (iterate_info.upandout & UPANDOUT_HASH)
+ break;
+ }
+ }
+
+ /* free temporary memory */
+ free_formats(formats, num_formats);
+
+ /* update anything that may have changed */
+ *(info->object) = object;
+ *(info->arguments) = arguments;
+ *(info->num_arguments) = num_arguments;
+
+ /* this information always updated */
+ *(info->format) = next_format;
+}
+
+static void
+format_justify(LispObj *stream, FmtInfo *info)
+{
+ GC_ENTER();
+ FmtInfo justify_info;
+ char **formats, *format, *next_format, *str;
+ LispObj *string, *strings = NIL, *cons;
+ int atsign = info->args.atsign,
+ collon = info->args.collon,
+ mincol = info->args.arguments[0].value,
+ colinc = info->args.arguments[1].value,
+ minpad = info->args.arguments[2].value,
+ padchar = info->args.arguments[3].value;
+ int i, k, total_length, length, padding, num_formats, has_default,
+ comma_width, line_width, size, extra;
+
+ next_format = *(info->format);
+
+ /* list formats */
+ list_formats(info, '<', &next_format, &formats, &num_formats,
+ &has_default, &comma_width, &line_width);
+
+ /* initialize list of strings streams */
+ if (num_formats) {
+ string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
+ strings = cons = CONS(string, NIL);
+ GC_PROTECT(strings);
+ for (i = 1; i < num_formats; i++) {
+ string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
+ RPLACD(cons, CONS(string, NIL));
+ cons = CDR(cons);
+ }
+ }
+
+ /* most information is the same */
+ memcpy(&justify_info, info, sizeof(FmtInfo));
+
+ /* loop formating strings */
+ for (i = 0, cons = strings; i < num_formats; i++, cons = CDR(cons)) {
+ /* set new format string */
+ format = formats[i];
+ justify_info.args.base = justify_info.args.format = format;
+ justify_info.format = &format;
+
+ /* format string, maybe consuming arguments */
+ LispFormat(CAR(cons), &justify_info);
+
+ /* if format was aborted, it is discarded */
+ if (justify_info.upandout)
+ RPLACA(cons, NIL);
+ /* check if the entire "main" iteration must be aborted */
+ if (justify_info.upandout & UPANDOUT_COLLON) {
+ for (cons = CDR(cons); i < num_formats; i++, cons = CDR(cons))
+ RPLACA(cons, NIL);
+ break;
+ }
+ }
+
+ /* free temporary format strings */
+ free_formats(formats, num_formats);
+
+ /* remove aborted formats */
+ /* first remove leading discarded formats */
+ if (CAR(strings) == NIL) {
+ while (CAR(strings) == NIL) {
+ strings = CDR(strings);
+ --num_formats;
+ }
+ /* keep strings gc protected, discarding first entries */
+ lisp__data.protect.objects[gc__protect] = strings;
+ }
+ /* now remove intermediary discarded formats */
+ cons = strings;
+ while (CONSP(cons)) {
+ if (CONSP(CDR(cons)) && CAR(CDR(cons)) == NIL) {
+ RPLACD(cons, CDR(CDR(cons)));
+ --num_formats;
+ }
+ else
+ cons = CDR(cons);
+ }
+
+ /* calculate total length required for output */
+ if (has_default)
+ cons = CDR(strings); /* if has_defaults, strings is surely a list */
+ else
+ cons = strings;
+ for (total_length = 0; CONSP(cons); cons = CDR(cons))
+ total_length += SSTREAMP(CAR(cons))->length;
+
+ /* initialize pointer to string streams */
+ if (has_default)
+ cons = CDR(strings);
+ else
+ cons = strings;
+
+ /* check if padding will need to be printed */
+ extra = 0;
+ padding = mincol - total_length;
+ if (padding < 0)
+ k = padding = 0;
+ else {
+ int num_fields = num_formats - (has_default != 0);
+
+ if (num_fields > 1) {
+ /* check if padding is distributed in num_fields or
+ * num_fields - 1 steps */
+ if (!collon)
+ --num_fields;
+ }
+
+ if (num_fields)
+ k = padding / num_fields;
+ else
+ k = padding;
+
+ if (k <= 0)
+ k = colinc;
+ else if (colinc)
+ k = k + (k % colinc);
+ extra = mincol - (num_fields * k + total_length);
+ if (extra < 0)
+ extra = 0;
+ }
+ if (padding && k < minpad) {
+ k = minpad;
+ if (colinc)
+ k = k + (k % colinc);
+ }
+
+ /* first check for the special case of only one string being justified */
+ if (num_formats - has_default == 1) {
+ if (has_default && line_width > 0 && comma_width >= 0 &&
+ total_length + comma_width > line_width) {
+ str = LispGetSstring(SSTREAMP(CAR(strings)), &size);
+ LispWriteStr(stream, str, size);
+ }
+ string = has_default ? CAR(CDR(strings)) : CAR(strings);
+ /* check if need left padding */
+ if (k && !atsign) {
+ LispWriteChars(stream, padchar, k);
+ k = 0;
+ }
+ /* check for centralizing text */
+ else if (k && atsign && collon) {
+ LispWriteChars(stream, padchar, k / 2 + ((k / 2) & 1));
+ k -= k / 2;
+ }
+ str = LispGetSstring(SSTREAMP(string), &size);
+ LispWriteStr(stream, str, size);
+ /* if any padding remaining */
+ if (k)
+ LispWriteChars(stream, padchar, k);
+ }
+ else {
+ LispObj *result;
+ int last, spaces_before, padout;
+
+ /* if has default, need to check output length */
+ if (has_default && line_width > 0 && comma_width >= 0) {
+ result = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
+ GC_PROTECT(result);
+ }
+ /* else write directly to stream */
+ else
+ result = stream;
+
+ /* loop printing justified text */
+ /* padout controls padding for cases where padding is
+ * is separated in n-1 chunks, where n is the number of
+ * formatted strings.
+ */
+ for (i = padout = 0; CONSP(cons); i++, cons = CDR(cons), --extra) {
+ string = CAR(cons);
+ last = !CONSP(CDR(cons));
+
+ spaces_before = (i != 0 || collon) && (!last || !atsign);
+
+ if (!spaces_before) {
+ /* check for special case */
+ if (last && atsign && collon && padding > 0) {
+ int spaces;
+
+ spaces = minpad > colinc ? minpad : colinc;
+ LispWriteChars(result, padchar, spaces + (extra > 0));
+ k -= spaces;
+ }
+ str = LispGetSstring(SSTREAMP(string), &size);
+ LispWriteStr(result, str, size);
+ padout = 0;
+ }
+ if (!padout)
+ LispWriteChars(result, padchar, k + (extra > 0));
+ padout = k;
+ /* if not first string, or if left padding specified */
+ if (spaces_before) {
+ str = LispGetSstring(SSTREAMP(string), &size);
+ LispWriteStr(result, str, size);
+ padout = 0;
+ }
+ padding -= k;
+ }
+
+ if (has_default && line_width > 0 && comma_width >= 0) {
+ length = SSTREAMP(result)->length + LispGetColumn(stream);
+
+ /* if current line is too large */
+ if (has_default && length + comma_width > line_width) {
+ str = LispGetSstring(SSTREAMP(CAR(strings)), &size);
+ LispWriteStr(stream, str, size);
+ }
+
+ /* write result to stream */
+ str = LispGetSstring(SSTREAMP(result), &size);
+ LispWriteStr(stream, str, size);
+ }
+ }
+
+ /* unprotect string streams from GC */
+ GC_LEAVE();
+
+ /* this information always updated */
+ *(info->format) = next_format;
+}
+
+static void
+LispFormat(LispObj *stream, FmtInfo *info)
+{
+ FmtArgs *args;
+ FmtDefs *defs = NULL;
+ LispObj *object, *arguments;
+ char stk[256], *format, *next_format;
+ int length, num_arguments, code, need_update, need_argument, hash, head;
+
+ /* arguments that will be updated on function exit */
+ format = *(info->format);
+ object = *(info->object);
+ arguments = *(info->arguments);
+ num_arguments = *(info->num_arguments);
+
+ /* initialize */
+ length = 0;
+ args = &(info->args);
+ info->upandout = 0;
+
+ while (*format) {
+ if (*format == '~') {
+ /* flush non formatted characters */
+ if (length) {
+ LispWriteStr(stream, stk, length);
+ length = 0;
+ }
+
+ need_argument = need_update = hash = 0;
+
+ /* parse parameters */
+ args->format = format + 1;
+ next_format = parse_arguments(format + 1, args, &num_arguments,
+ &arguments, &code);
+ if (code != NOERROR)
+ parse_error(args, code);
+
+ /* check parameters */
+ switch (args->command) {
+ case 'A': case 'S':
+ defs = &AsciiDefs;
+ break;
+ case 'B': case 'O': case 'D': case 'X':
+ defs = &IntegerDefs;
+ break;
+ case 'R':
+ defs = &RadixDefs;
+ break;
+ case 'P': case 'C':
+ defs = &NoneDefs;
+ break;
+ case 'F':
+ defs = &FixedFloatDefs;
+ break;
+ case 'E': case 'G':
+ defs = &ExponentialFloatDefs;
+ break;
+ case '$':
+ defs = &DollarFloatDefs;
+ break;
+ case '%': case '&': case '|': case '~': case '\n':
+ defs = &OneDefs;
+ break;
+ case 'T':
+ defs = &TabulateDefs;
+ break;
+ case '*':
+ defs = &OneDefs;
+ break;
+ case '?': case '(':
+ defs = &NoneDefs;
+ break;
+ case ')':
+ /* this is never seen, processed in format_case_conversion */
+ format_error(args, "no match for directive ~)");
+ case '[':
+ defs = &OneDefs;
+ break;
+ case ']':
+ /* this is never seen, processed in format_conditional */
+ format_error(args, "no match for directive ~]");
+ case '{':
+ defs = &OneDefs;
+ break;
+ case '}':
+ /* this is never seen, processed in format_iterate */
+ format_error(args, "no match for directive ~}");
+ case '<':
+ defs = &AsciiDefs;
+ break;
+ case '>':
+ /* this is never seen, processed in format_justify */
+ format_error(args, "no match for directive ~>");
+ case ';':
+ /* this is never seen here */
+ format_error(args, "misplaced directive ~;");
+ case '#':
+ /* special handling for ~#^ */
+ if (*next_format == '^') {
+ ++next_format;
+ hash = 1;
+ defs = &NoneDefs;
+ args->command = '^';
+ break;
+ }
+ parse_error(args, PARSE_BADDIRECTIVE);
+ case '^':
+ defs = &NoneDefs;
+ break;
+ default:
+ parse_error(args, PARSE_BADDIRECTIVE);
+ break;
+ }
+ merge_arguments(args, defs, &code);
+ if (code != NOERROR)
+ merge_error(args, code);
+
+ /* check if an argument is required by directive */
+ switch (args->command) {
+ case 'A': case 'S':
+ case 'B': case 'O': case 'D': case 'X': case 'R':
+ need_argument = 1;
+ break;
+ case 'P':
+ /* if collon specified, plural is the last print argument */
+ need_argument = !args->collon;
+ break;
+ case 'C':
+ need_argument = 1;
+ break;
+ case 'F': case 'E': case 'G': case '$':
+ need_argument = 1;
+ break;
+ case '%': case '&': case '|': case '~': case '\n':
+ break;
+ case 'T':
+ break;
+ case '*': /* check arguments below */
+ need_update = 1;
+ break;
+ case '?':
+ need_argument = need_update = 1;
+ break;
+ case '(': case '[': case '{': case '<':
+ need_update = 1;
+ break;
+ case '^':
+ break;
+ }
+ if (need_argument) {
+ if (!CONSP(arguments))
+ parse_error(args, PARSE_NOARGSLEFT);
+ object = CAR(arguments);
+ arguments = CDR(arguments);
+ --num_arguments;
+ }
+
+ /* will do recursive calls that change info */
+ if (need_update) {
+ *(info->format) = next_format;
+ *(info->object) = object;
+ *(info->arguments) = arguments;
+ *(info->num_arguments) = num_arguments;
+ }
+
+ /* everything seens fine, print the format directive */
+ switch (args->command) {
+ case 'A':
+ head = lisp__data.env.length;
+ LispAddVar(Oprint_escape, NIL);
+ ++lisp__data.env.head;
+ format_ascii(stream, object, args);
+ lisp__data.env.head = lisp__data.env.length = head;
+ break;
+ case 'S':
+ head = lisp__data.env.length;
+ LispAddVar(Oprint_escape, T);
+ ++lisp__data.env.head;
+ format_ascii(stream, object, args);
+ lisp__data.env.head = lisp__data.env.length = head;
+ break;
+ case 'B':
+ format_in_radix(stream, object, 2, args);
+ break;
+ case 'O':
+ format_in_radix(stream, object, 8, args);
+ break;
+ case 'D':
+ format_in_radix(stream, object, 10, args);
+ break;
+ case 'X':
+ format_in_radix(stream, object, 16, args);
+ break;
+ case 'R':
+ /* if a single argument specified */
+ if (args->count)
+ format_in_radix(stream, object, 0, args);
+ else
+ format_radix_special(stream, object, args);
+ break;
+ case 'P':
+ if (args->atsign) {
+ if (FIXNUMP(object) && FIXNUM_VALUE(object) == 1)
+ LispWriteChar(stream, 'y');
+ else
+ LispWriteStr(stream, "ies", 3);
+ }
+ else if (!FIXNUMP(object) || FIXNUM_VALUE(object) != 1)
+ LispWriteChar(stream, 's');
+ break;
+ case 'C':
+ format_character(stream, object, args);
+ break;
+ case 'F':
+ format_fixed_float(stream, object, args);
+ break;
+ case 'E':
+ format_exponential_float(stream, object, args);
+ break;
+ case 'G':
+ format_general_float(stream, object, args);
+ break;
+ case '$':
+ format_dollar_float(stream, object, args);
+ break;
+ case '&':
+ if (LispGetColumn(stream) == 0)
+ --args->arguments[0].value;
+ case '%':
+ LispWriteChars(stream, '\n', args->arguments[0].value);
+ break;
+ case '|':
+ LispWriteChars(stream, '\f', args->arguments[0].value);
+ break;
+ case '~':
+ LispWriteChars(stream, '~', args->arguments[0].value);
+ break;
+ case '\n':
+ if (!args->collon) {
+ if (args->atsign)
+ LispWriteChar(stream, '\n');
+ /* ignore newline and following spaces */
+ while (*next_format && isspace(*next_format))
+ ++next_format;
+ }
+ break;
+ case 'T':
+ format_tabulate(stream, args);
+ break;
+ case '*':
+ format_goto(info);
+ break;
+ case '?':
+ format_indirection(stream, object, info);
+ need_update = 1;
+ break;
+ case '(':
+ format_case_conversion(stream, info);
+ /* next_format if far from what is set now */
+ next_format = *(info->format);
+ break;
+ case '[':
+ format_conditional(stream, info);
+ /* next_format if far from what is set now */
+ next_format = *(info->format);
+ break;
+ case '{':
+ format_iterate(stream, info);
+ /* next_format if far from what is set now */
+ next_format = *(info->format);
+ break;
+ case '<':
+ format_justify(stream, info);
+ /* next_format if far from what is set now */
+ next_format = *(info->format);
+ break;
+ case '^':
+ if (args->collon) {
+ if (hash && num_arguments == 0) {
+ info->upandout = UPANDOUT_HASH;
+ goto format_up_and_out;
+ }
+ if (info->iteration &&
+ info->iteration == ITERATION_NORMAL)
+ /* not exactly an error, but in this case,
+ * command is ignored */
+ break;
+ info->upandout = UPANDOUT_COLLON;
+ goto format_up_and_out;
+ }
+ else if (num_arguments == 0) {
+ info->upandout = UPANDOUT_NORMAL;
+ goto format_up_and_out;
+ }
+ break;
+ }
+
+ if (need_update) {
+ object = *(info->object);
+ arguments = *(info->arguments);
+ num_arguments = *(info->num_arguments);
+ }
+
+ format = next_format;
+ }
+ else {
+ if (length >= sizeof(stk)) {
+ LispWriteStr(stream, stk, length);
+ length = 0;
+ }
+ stk[length++] = *format++;
+ }
+ }
+
+ /* flush any peding output */
+ if (length)
+ LispWriteStr(stream, stk, length);
+
+format_up_and_out:
+ /* update for recursive call */
+ *(info->format) = format;
+ *(info->object) = object;
+ *(info->arguments) = arguments;
+ *(info->num_arguments) = num_arguments;
+}
+
+LispObj *
+Lisp_Format(LispBuiltin *builtin)
+/*
+ format destination control-string &rest arguments
+ */
+{
+ GC_ENTER();
+ FmtInfo info;
+ LispObj *object;
+ char *control_string;
+ int num_arguments;
+
+ LispObj *stream, *format, *arguments;
+
+ arguments = ARGUMENT(2);
+ format = ARGUMENT(1);
+ stream = ARGUMENT(0);
+
+ /* check format and stream */
+ CHECK_STRING(format);
+ if (stream == NIL) { /* return a string */
+ stream = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
+ GC_PROTECT(stream);
+ }
+ else if (stream == T || /* print directly to *standard-output* */
+ stream == STANDARD_OUTPUT)
+ stream = NIL;
+ else {
+ CHECK_STREAM(stream);
+ if (!stream->data.stream.writable)
+ LispDestroy("%s: stream %s is not writable",
+ STRFUN(builtin), STROBJ(stream));
+ }
+
+ /* count number of arguments */
+ for (object = arguments, num_arguments = 0; CONSP(object);
+ object = CDR(object), num_arguments++)
+ ;
+
+ /* initialize plural/argument info */
+ object = NIL;
+
+ /* the format string */
+ control_string = THESTR(format);
+
+ /* arguments to recursive calls */
+ info.args.base = control_string;
+ info.base_arguments = arguments;
+ info.total_arguments = num_arguments;
+ info.format = &control_string;
+ info.object = &object;
+ info.arguments = &arguments;
+ info.num_arguments = &num_arguments;
+ info.iteration = 0;
+
+ /* format arguments */
+ LispFormat(stream, &info);
+
+ /* if printing to stdout */
+ if (stream == NIL)
+ LispFflush(Stdout);
+ /* else if printing to string-stream, return a string */
+ else if (stream->data.stream.type == LispStreamString) {
+ int length;
+ char *string;
+
+ string = LispGetSstring(SSTREAMP(stream), &length);
+ stream = LSTRING(string, length);
+ }
+
+ GC_LEAVE();
+
+ return (stream);
+}
diff --git a/lisp/format.h b/lisp/format.h
new file mode 100644
index 0000000..7ca14b9
--- /dev/null
+++ b/lisp/format.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/format.h,v 1.3 2002/11/08 08:00:56 paulo Exp $ */
+
+#ifndef Lisp_format_h
+#define Lisp_format_h
+
+#include "private.h"
+
+/*
+ * Prototypes
+ */
+LispObj *Lisp_Format(LispBuiltin*);
+
+#endif /* Lisp_format_h */
diff --git a/lisp/hash.c b/lisp/hash.c
new file mode 100644
index 0000000..3d32f07
--- /dev/null
+++ b/lisp/hash.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/hash.c,v 1.4 2002/11/23 08:26:48 paulo Exp $ */
+
+#include "hash.h"
+
+/* A simple hash-table implementation
+ * TODO: implement SXHASH and WITH-HASH-TABLE-ITERATOR
+ * May need a rewrite for better performance, and will
+ * need a rewrite if images/bytecode saved on disk.
+ */
+
+#define GET_HASH 1
+#define PUT_HASH 2
+#define REM_HASH 3
+
+/*
+ * Prototypes
+ */
+static unsigned long LispHashKey(LispObj*, int);
+static LispObj *LispHash(LispBuiltin*, int);
+static void LispRehash(LispHashTable*);
+static void LispFreeHashEntries(LispHashEntry*, long);
+
+/*
+ * Initialization
+ */
+extern LispObj *Oeq, *Oeql, *Oequal, *Oequalp;
+
+/* Hash tables will have one of these sizes, unless the user
+ * specified a very large size */
+static long some_primes[] = {
+ 5, 11, 17, 23,
+ 31, 47, 71, 97,
+ 139, 199, 307, 401,
+ 607, 809, 1213, 1619,
+ 2437, 3251, 4889, 6521
+};
+
+/*
+ * Implementation
+ */
+static unsigned long
+LispHashKey(LispObj *object, int function)
+{
+ mpi *bigi;
+ char *string;
+ long i, length;
+ unsigned long key = ((unsigned long)object) >> 4;
+
+ /* Must be the same object for EQ */
+ if (function == FEQ)
+ goto hash_key_done;
+
+ if (function == FEQUALP) {
+ switch (OBJECT_TYPE(object)) {
+ case LispSChar_t:
+ key = (unsigned long)toupper(SCHAR_VALUE(object));
+ goto hash_key_done;
+ 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) ^ toupper(string[i]);
+ goto hash_key_done;
+ default:
+ break;
+ }
+ }
+
+ /* Function is EQL, EQUAL or EQUALP */
+ switch (OBJECT_TYPE(object)) {
+ case LispFixnum_t:
+ case LispSChar_t:
+ key = (unsigned long)FIXNUM_VALUE(object);
+ goto hash_key_done;
+ case LispInteger_t:
+ key = (unsigned long)INT_VALUE(object);
+ goto hash_key_done;
+ case LispRatio_t:
+ key = (object->data.ratio.numerator << 16) ^
+ object->data.ratio.denominator;
+ goto hash_key_done;
+ case LispDFloat_t:
+ key = (unsigned long)DFLOAT_VALUE(object);
+ break;
+ case LispComplex_t:
+ key = (LispHashKey(object->data.complex.imag, function) << 16) ^
+ LispHashKey(object->data.complex.real, function);
+ goto hash_key_done;
+ case LispBignum_t:
+ bigi = object->data.mp.integer;
+ length = bigi->size;
+ if (length > 8)
+ length = 8;
+ key = bigi->sign;
+ for (i = 0; i < length; i++)
+ key = (key << 8) ^ bigi->digs[i];
+ goto hash_key_done;
+ case LispBigratio_t:
+ bigi = mpr_num(object->data.mp.ratio);
+ length = bigi->size;
+ if (length > 4)
+ length = 4;
+ key = bigi->sign;
+ for (i = 0; i < length; i++)
+ key = (key << 4) ^ bigi->digs[i];
+ bigi = mpr_den(object->data.mp.ratio);
+ length = bigi->size;
+ if (length > 4)
+ length = 4;
+ for (i = 0; i < length; i++)
+ key = (key << 4) ^ bigi->digs[i];
+ goto hash_key_done;
+ default:
+ break;
+ }
+
+ /* Anything else must be the same object for EQL */
+ if (function == FEQL)
+ goto hash_key_done;
+
+ switch (OBJECT_TYPE(object)) {
+ 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;
+ case LispCons_t:
+ key = (LispHashKey(CAR(object), function) << 16) ^
+ LispHashKey(CDR(object), function);
+ break;
+ case LispQuote_t:
+ case LispBackquote_t:
+ case LispPathname_t:
+ key = LispHashKey(object->data.pathname, function);
+ break;
+ case LispRegex_t:
+ key = LispHashKey(object->data.regex.pattern, function);
+ break;
+ default:
+ break;
+ }
+
+hash_key_done:
+ return (key);
+}
+
+static LispObj *
+LispHash(LispBuiltin *builtin, int code)
+{
+ LispHashEntry *entry;
+ LispHashTable *hash;
+ unsigned long key;
+ LispObj *result;
+ int found;
+ long i;
+
+ LispObj *okey, *hash_table, *value;
+
+ if (code == REM_HASH)
+ value = NIL;
+ else {
+ value = ARGUMENT(2);
+ if (value == UNSPEC)
+ value = NIL;
+ }
+ hash_table = ARGUMENT(1);
+ okey = ARGUMENT(0);
+
+ CHECK_HASHTABLE(hash_table);
+
+ /* get hash entry */
+ hash = hash_table->data.hash.table;
+ key = LispHashKey(okey, hash->function) % hash->num_entries;
+ entry = hash->entries + key;
+
+ /* search entry in the hash table */
+ if (entry->count == 0)
+ i = 0;
+ else {
+ if (hash->function == FEQ) {
+ for (i = entry->cache; i >= 0; i--) {
+ if (entry->keys[i] == okey)
+ goto found_key;
+ }
+ for (i = entry->cache + 1; i < entry->count; i++) {
+ if (entry->keys[i] == okey)
+ break;
+ }
+ }
+ else {
+ for (i = entry->cache; i >= 0; i--) {
+ if (LispObjectCompare(entry->keys[i], okey,
+ hash->function) == T)
+ goto found_key;
+ }
+ for (i = entry->cache + 1; i < entry->count; i++) {
+ if (LispObjectCompare(entry->keys[i], okey,
+ hash->function) == T)
+ break;
+ }
+ }
+ }
+
+found_key:
+ result = value;
+ if ((found = i < entry->count) == 0)
+ i = entry->count;
+
+ switch (code) {
+ case GET_HASH:
+ RETURN_COUNT = 1;
+ if (found) {
+ RETURN(0) = T;
+ entry->cache = i;
+ result = entry->values[i];
+ }
+ else
+ RETURN(0) = NIL;
+ break;
+ case PUT_HASH:
+ entry->cache = i;
+ if (found)
+ /* Just replace current entry */
+ entry->values[i] = value;
+ else {
+ if ((i % 4) == 0) {
+ LispObj **keys, **values;
+
+ keys = realloc(entry->keys, sizeof(LispObj*) * (i + 4));
+ if (keys == NULL)
+ LispDestroy("out of memory");
+ values = realloc(entry->values, sizeof(LispObj*) * (i + 4));
+ if (values == NULL) {
+ free(keys);
+ LispDestroy("out of memory");
+ }
+ entry->keys = keys;
+ entry->values = values;
+ }
+ entry->keys[i] = okey;
+ entry->values[i] = value;
+ ++entry->count;
+ ++hash->count;
+ if (hash->count > hash->rehash_threshold * hash->num_entries)
+ LispRehash(hash);
+ }
+ break;
+ case REM_HASH:
+ if (found) {
+ result = T;
+ --entry->count;
+ --hash->count;
+ if (i < entry->count) {
+ memmove(entry->keys + i, entry->keys + i + 1,
+ (entry->count - i) * sizeof(LispObj*));
+ memmove(entry->values + i, entry->values + i + 1,
+ (entry->count - i) * sizeof(LispObj*));
+ }
+ if (entry->cache && entry->cache == entry->count)
+ --entry->cache;
+ }
+ break;
+ }
+
+ return (result);
+}
+
+static void
+LispRehash(LispHashTable *hash)
+{
+ unsigned long key;
+ LispHashEntry *entries, *nentry, *entry, *last;
+ long i, size = hash->num_entries * hash->rehash_size;
+
+ for (i = 0; i < sizeof(some_primes) / sizeof(some_primes[0]); i++)
+ if (some_primes[i] >= size) {
+ size = some_primes[i];
+ break;
+ }
+
+ entries = calloc(1, sizeof(LispHashEntry) * size);
+ if (entries == NULL)
+ goto out_of_memory;
+
+ for (entry = hash->entries, last = entry + hash->num_entries;
+ entry < last; entry++) {
+ for (i = 0; i < entry->count; i++) {
+ key = LispHashKey(entry->keys[i], hash->function) % size;
+ nentry = entries + key;
+ if ((nentry->count % 4) == 0) {
+ LispObj **keys, **values;
+
+ keys = realloc(nentry->keys, sizeof(LispObj*) * (i + 4));
+ if (keys == NULL)
+ goto out_of_memory;
+ values = realloc(nentry->values, sizeof(LispObj*) * (i + 4));
+ if (values == NULL) {
+ free(keys);
+ goto out_of_memory;
+ }
+ nentry->keys = keys;
+ nentry->values = values;
+ }
+ nentry->keys[nentry->count] = entry->keys[i];
+ nentry->values[nentry->count] = entry->values[i];
+ ++nentry->count;
+
+ }
+ }
+ LispFreeHashEntries(hash->entries, hash->num_entries);
+ hash->entries = entries;
+ hash->num_entries = size;
+ return;
+
+out_of_memory:
+ if (entries)
+ LispFreeHashEntries(entries, size);
+ LispDestroy("out of memory");
+}
+
+static void
+LispFreeHashEntries(LispHashEntry *entries, long num_entries)
+{
+ LispHashEntry *entry, *last;
+
+ for (entry = entries, last = entry + num_entries; entry < last; entry++) {
+ free(entry->keys);
+ free(entry->values);
+ }
+ free(entries);
+}
+
+void
+LispFreeHashTable(LispHashTable *hash)
+{
+ LispFreeHashEntries(hash->entries, hash->num_entries);
+ free(hash);
+}
+
+LispObj *
+Lisp_Clrhash(LispBuiltin *builtin)
+/*
+ clrhash hash-table
+ */
+{
+ LispHashTable *hash;
+ LispHashEntry *entry, *last;
+
+ LispObj *hash_table = ARGUMENT(0);
+
+ CHECK_HASHTABLE(hash_table);
+
+ hash = hash_table->data.hash.table;
+ for (entry = hash->entries, last = entry + hash->num_entries;
+ entry < last; entry++) {
+ free(entry->keys);
+ free(entry->values);
+ entry->keys = entry->values = NULL;
+ entry->count = entry->cache = 0;
+ }
+ hash->count = 0;
+
+ return (hash_table);
+}
+
+LispObj *
+Lisp_Gethash(LispBuiltin *builtin)
+/*
+ gethash key hash-table &optional default
+ */
+{
+ return (LispHash(builtin, GET_HASH));
+}
+
+LispObj *
+Lisp_HashTableP(LispBuiltin *builtin)
+/*
+ hash-table-p object
+ */
+{
+ LispObj *object = ARGUMENT(0);
+
+ return (HASHTABLEP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_HashTableCount(LispBuiltin *builtin)
+/*
+ hash-table-count hash-table
+ */
+{
+ LispObj *hash_table = ARGUMENT(0);
+
+ CHECK_HASHTABLE(hash_table);
+
+ return (FIXNUM(hash_table->data.hash.table->count));
+}
+
+LispObj *
+Lisp_HashTableRehashSize(LispBuiltin *builtin)
+/*
+ hash-table-rehash-size hash-table
+ */
+{
+ LispObj *hash_table = ARGUMENT(0);
+
+ CHECK_HASHTABLE(hash_table);
+
+ return (DFLOAT(hash_table->data.hash.table->rehash_size));
+}
+
+LispObj *
+Lisp_HashTableRehashThreshold(LispBuiltin *builtin)
+/*
+ hash-table-rehash-threshold hash-table
+ */
+{
+ LispObj *hash_table = ARGUMENT(0);
+
+ CHECK_HASHTABLE(hash_table);
+
+ return (DFLOAT(hash_table->data.hash.table->rehash_threshold));
+}
+
+LispObj *
+Lisp_HashTableSize(LispBuiltin *builtin)
+/*
+ hash-table-size hash-table
+ */
+{
+ LispObj *hash_table = ARGUMENT(0);
+
+ CHECK_HASHTABLE(hash_table);
+
+ return (FIXNUM(hash_table->data.hash.table->num_entries));
+}
+
+LispObj *
+Lisp_HashTableTest(LispBuiltin *builtin)
+/*
+ hash-table-test hash-table
+ */
+{
+ LispObj *hash_table = ARGUMENT(0);
+
+ CHECK_HASHTABLE(hash_table);
+
+ return (hash_table->data.hash.test);
+}
+
+LispObj *
+Lisp_Maphash(LispBuiltin *builtin)
+/*
+ maphash function hash-table
+ */
+{
+ long i;
+ LispHashEntry *entry, *last;
+
+ LispObj *function, *hash_table;
+
+ hash_table = ARGUMENT(1);
+ function = ARGUMENT(0);
+
+ CHECK_HASHTABLE(hash_table);
+
+ for (entry = hash_table->data.hash.table->entries,
+ last = entry + hash_table->data.hash.table->num_entries;
+ entry < last; entry++) {
+ for (i = 0; i < entry->count; i++)
+ APPLY2(function, entry->keys[i], entry->values[i]);
+ }
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_MakeHashTable(LispBuiltin *builtin)
+/*
+ make-hash-table &key test size rehash-size rehash-threshold initial-contents
+ */
+{
+ int function = FEQL;
+ unsigned long i, isize, xsize;
+ double drsize, drthreshold;
+ LispHashTable *hash_table;
+ LispObj *cons, *result;
+
+ LispObj *test, *size, *rehash_size, *rehash_threshold, *initial_contents;
+
+ initial_contents = ARGUMENT(4);
+ rehash_threshold = ARGUMENT(3);
+ rehash_size = ARGUMENT(2);
+ size = ARGUMENT(1);
+ test = ARGUMENT(0);
+
+ if (test != UNSPEC) {
+ if (test == Oeq)
+ function = FEQ;
+ else if (test == Oeql)
+ function = FEQL;
+ else if (test == Oequal)
+ function = FEQUAL;
+ else if (test == Oequalp)
+ function = FEQUALP;
+ else
+ LispDestroy("%s: :TEST must be EQ, EQL, EQUAL, "
+ "or EQUALP, not %s", STRFUN(builtin), STROBJ(test));
+ }
+ else
+ test = Oeql;
+
+ if (size != UNSPEC) {
+ CHECK_INDEX(size);
+ isize = FIXNUM_VALUE(size);
+ }
+ else
+ isize = 1;
+
+ if (rehash_size != UNSPEC) {
+ CHECK_DFLOAT(rehash_size);
+ if (DFLOAT_VALUE(rehash_size) <= 1.0)
+ LispDestroy("%s: :REHASH-SIZE must a float > 1, not %s",
+ STRFUN(builtin), STROBJ(rehash_size));
+ drsize = DFLOAT_VALUE(rehash_size);
+ }
+ else
+ drsize = 1.5;
+
+ if (rehash_threshold != UNSPEC) {
+ CHECK_DFLOAT(rehash_threshold);
+ if (DFLOAT_VALUE(rehash_threshold) < 0.0 ||
+ DFLOAT_VALUE(rehash_threshold) > 1.0)
+ LispDestroy("%s: :REHASH-THRESHOLD must a float "
+ "in the range 0.0 - 1.0, not %s",
+ STRFUN(builtin), STROBJ(rehash_threshold));
+ drthreshold = DFLOAT_VALUE(rehash_threshold);
+ }
+ else
+ drthreshold = 0.75;
+
+ if (initial_contents == UNSPEC)
+ initial_contents = NIL;
+ CHECK_LIST(initial_contents);
+ for (xsize = 0, cons = initial_contents;
+ CONSP(cons);
+ xsize++, cons = CDR(cons))
+ CHECK_CONS(CAR(cons));
+
+ if (xsize > isize)
+ isize = xsize;
+
+ for (i = 0; i < sizeof(some_primes) / sizeof(some_primes[0]); i++)
+ if (some_primes[i] >= isize) {
+ isize = some_primes[i];
+ break;
+ }
+
+ hash_table = LispMalloc(sizeof(LispHashTable));
+ hash_table->entries = LispCalloc(1, sizeof(LispHashEntry) * isize);
+ hash_table->num_entries = isize;
+ hash_table->count = 0;
+ hash_table->function = function;
+ hash_table->rehash_size = drsize;
+ hash_table->rehash_threshold = drthreshold;
+
+ result = LispNew(NIL, NIL);
+ result->type = LispHashTable_t;
+ result->data.hash.table = hash_table;
+ result->data.hash.test = test;
+
+ LispMused(hash_table);
+ LispMused(hash_table->entries);
+
+ if (initial_contents != UNSPEC) {
+ unsigned long key;
+ LispHashEntry *entry;
+
+ for (cons = initial_contents; CONSP(cons); cons = CDR(cons)) {
+ key = LispHashKey(CAAR(cons), function) % isize;
+ entry = hash_table->entries + key;
+
+ if ((entry->count % 4) == 0) {
+ LispObj **keys, **values;
+
+ keys = realloc(entry->keys, sizeof(LispObj*) * (i + 4));
+ if (keys == NULL)
+ LispDestroy("out of memory");
+ values = realloc(entry->values, sizeof(LispObj*) * (i + 4));
+ if (values == NULL) {
+ free(keys);
+ LispDestroy("out of memory");
+ }
+ entry->keys = keys;
+ entry->values = values;
+ }
+ entry->keys[entry->count] = CAAR(cons);
+ entry->values[entry->count] = CDAR(cons);
+ ++entry->count;
+ }
+ hash_table->count = xsize;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Remhash(LispBuiltin *builtin)
+/*
+ remhash key hash-table
+ */
+{
+ return (LispHash(builtin, REM_HASH));
+}
+
+LispObj *
+Lisp_XeditPuthash(LispBuiltin *builtin)
+/*
+ lisp::puthash key hash-table value
+ */
+{
+ return (LispHash(builtin, PUT_HASH));
+}
diff --git a/lisp/hash.h b/lisp/hash.h
new file mode 100644
index 0000000..df74c96
--- /dev/null
+++ b/lisp/hash.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/hash.h,v 1.2 2002/11/08 08:00:56 paulo Exp $ */
+
+#ifndef Lisp_hash_h
+#define Lisp_hash_h
+
+#include "private.h"
+
+typedef struct _LispHashEntry {
+ LispObj **keys;
+ LispObj **values;
+ long cache;
+ long count;
+} LispHashEntry;
+
+struct _LispHashTable {
+ LispHashEntry *entries;
+ long num_entries;
+ long count;
+ int function; /* Function is EQ, EQL, EQUAL, or EQUALP */
+ double rehash_size;
+ double rehash_threshold;
+};
+
+/*
+ * Prototypes
+ */
+void LispFreeHashTable(LispHashTable*);
+
+LispObj *Lisp_Clrhash(LispBuiltin*);
+LispObj *Lisp_Gethash(LispBuiltin*);
+LispObj *Lisp_HashTableCount(LispBuiltin*);
+LispObj *Lisp_HashTableP(LispBuiltin*);
+LispObj *Lisp_HashTableRehashSize(LispBuiltin*);
+LispObj *Lisp_HashTableRehashThreshold(LispBuiltin*);
+LispObj *Lisp_HashTableSize(LispBuiltin*);
+LispObj *Lisp_HashTableTest(LispBuiltin*);
+LispObj *Lisp_Maphash(LispBuiltin*);
+LispObj *Lisp_MakeHashTable(LispBuiltin*);
+LispObj *Lisp_Remhash(LispBuiltin*);
+LispObj *Lisp_XeditPuthash(LispBuiltin*);
+
+#endif /* Lisp_hash_h */
diff --git a/lisp/helper.c b/lisp/helper.c
new file mode 100644
index 0000000..65749c5
--- /dev/null
+++ b/lisp/helper.c
@@ -0,0 +1,1124 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/helper.c,v 1.47 2002/11/26 04:06:28 paulo Exp $ */
+
+#include "helper.h"
+#include "pathname.h"
+#include "package.h"
+#include "read.h"
+#include "stream.h"
+#include "write.h"
+#include "hash.h"
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <math.h>
+#include <sys/stat.h>
+
+/*
+ * Prototypes
+ */
+static LispObj *LispReallyDo(LispBuiltin*, int);
+static LispObj *LispReallyDoListTimes(LispBuiltin*, int);
+
+/* in math.c */
+extern LispObj *LispFloatCoerce(LispBuiltin*, LispObj*);
+
+/*
+ * Implementation
+ */
+LispObj *
+LispObjectCompare(LispObj *left, LispObj *right, int function)
+{
+ LispType ltype, rtype;
+ LispObj *result = left == right ? T : NIL;
+
+ /* If left and right are the same object, or if function is EQ */
+ if (result == T || function == FEQ)
+ return (result);
+
+ ltype = OBJECT_TYPE(left);
+ rtype = OBJECT_TYPE(right);
+
+ /* Equalp requires that numeric objects be compared by value, and
+ * strings or characters comparison be case insenstive */
+ if (function == FEQUALP) {
+ switch (ltype) {
+ case LispFixnum_t:
+ case LispInteger_t:
+ case LispBignum_t:
+ case LispDFloat_t:
+ case LispRatio_t:
+ case LispBigratio_t:
+ case LispComplex_t:
+ switch (rtype) {
+ case LispFixnum_t:
+ case LispInteger_t:
+ case LispBignum_t:
+ case LispDFloat_t:
+ case LispRatio_t:
+ case LispBigratio_t:
+ case LispComplex_t:
+ result = APPLY2(Oequal_, left, right);
+ break;
+ default:
+ break;
+ }
+ goto compare_done;
+ case LispSChar_t:
+ if (rtype == LispSChar_t &&
+ toupper(SCHAR_VALUE(left)) == toupper(SCHAR_VALUE(right)))
+ result = T;
+ goto compare_done;
+ case LispString_t:
+ if (rtype == LispString_t && STRLEN(left) == STRLEN(right)) {
+ long i = STRLEN(left);
+ char *sl = THESTR(left), *sr = THESTR(right);
+
+ for (--i; i >= 0; i--)
+ if (toupper(sl[i]) != toupper(sr[i]))
+ break;
+ if (i < 0)
+ result = T;
+ }
+ goto compare_done;
+ case LispArray_t:
+ if (rtype == LispArray_t &&
+ left->data.array.type == right->data.array.type &&
+ left->data.array.rank == right->data.array.rank &&
+ LispObjectCompare(left->data.array.dim,
+ right->data.array.dim,
+ FEQUAL) != NIL) {
+ LispObj *llist = left->data.array.list,
+ *rlist = right->data.array.list;
+
+ for (; CONSP(llist); llist = CDR(llist), rlist = CDR(rlist))
+ if (LispObjectCompare(CAR(llist), CAR(rlist),
+ FEQUALP) == NIL)
+ break;
+ if (!CONSP(llist))
+ result = T;
+ }
+ goto compare_done;
+ case LispStruct_t:
+ if (rtype == LispStruct_t &&
+ left->data.struc.def == right->data.struc.def) {
+ LispObj *lfield = left->data.struc.fields,
+ *rfield = right->data.struc.fields;
+
+ for (; CONSP(lfield);
+ lfield = CDR(lfield), rfield = CDR(rfield)) {
+ if (LispObjectCompare(CAR(lfield), CAR(rfield),
+ FEQUALP) != T)
+ break;
+ }
+ if (!CONSP(lfield))
+ result = T;
+ }
+ goto compare_done;
+ case LispHashTable_t:
+ if (rtype == LispHashTable_t &&
+ left->data.hash.table->count ==
+ right->data.hash.table->count &&
+ left->data.hash.test == right->data.hash.test) {
+ unsigned long i;
+ LispObj *test = left->data.hash.test;
+ LispHashEntry *lentry = left->data.hash.table->entries,
+ *llast = lentry +
+ left->data.hash.table->num_entries,
+ *rentry = right->data.hash.table->entries;
+
+ for (; lentry < llast; lentry++, rentry++) {
+ if (lentry->count != rentry->count)
+ break;
+ for (i = 0; i < lentry->count; i++) {
+ if (APPLY2(test,
+ lentry->keys[i],
+ rentry->keys[i]) == NIL ||
+ LispObjectCompare(lentry->values[i],
+ rentry->values[i],
+ FEQUALP) == NIL)
+ break;
+ }
+ if (i < lentry->count)
+ break;
+ }
+ if (lentry == llast)
+ result = T;
+ }
+ goto compare_done;
+ default:
+ break;
+ }
+ }
+
+ /* Function is EQL or EQUAL, or EQUALP on arguments with the same rules */
+ if (ltype == rtype) {
+ switch (ltype) {
+ case LispFixnum_t:
+ case LispSChar_t:
+ if (FIXNUM_VALUE(left) == FIXNUM_VALUE(right))
+ result = T;
+ break;
+ case LispInteger_t:
+ if (INT_VALUE(left) == INT_VALUE(right))
+ result = T;
+ break;
+ case LispDFloat_t:
+ if (DFLOAT_VALUE(left) == DFLOAT_VALUE(right))
+ result = T;
+ break;
+ case LispRatio_t:
+ if (left->data.ratio.numerator ==
+ right->data.ratio.numerator &&
+ left->data.ratio.denominator ==
+ right->data.ratio.denominator)
+ result = T;
+ break;
+ case LispComplex_t:
+ if (LispObjectCompare(left->data.complex.real,
+ right->data.complex.real,
+ function) == T &&
+ LispObjectCompare(left->data.complex.imag,
+ right->data.complex.imag,
+ function) == T)
+ result = T;
+ break;
+ case LispBignum_t:
+ if (mpi_cmp(left->data.mp.integer, right->data.mp.integer) == 0)
+ result = T;
+ break;
+ case LispBigratio_t:
+ if (mpr_cmp(left->data.mp.ratio, right->data.mp.ratio) == 0)
+ result = T;
+ break;
+ default:
+ break;
+ }
+
+ /* Next types must be the same object for EQL */
+ if (function == FEQL)
+ goto compare_done;
+
+ switch (ltype) {
+ case LispString_t:
+ if (STRLEN(left) == STRLEN(right) &&
+ memcmp(THESTR(left), THESTR(right), STRLEN(left)) == 0)
+ result = T;
+ break;
+ case LispCons_t:
+ if (LispObjectCompare(CAR(left), CAR(right), function) == T &&
+ LispObjectCompare(CDR(left), CDR(right), function) == T)
+ result = T;
+ break;
+ case LispQuote_t:
+ case LispBackquote_t:
+ case LispPathname_t:
+ result = LispObjectCompare(left->data.pathname,
+ right->data.pathname, function);
+ break;
+ case LispLambda_t:
+ result = LispObjectCompare(left->data.lambda.name,
+ right->data.lambda.name,
+ function);
+ break;
+ case LispOpaque_t:
+ if (left->data.opaque.data == right->data.opaque.data)
+ result = T;
+ break;
+ case LispRegex_t:
+ /* If the regexs are guaranteed to generate the same matches */
+ if (left->data.regex.options == right->data.regex.options)
+ result = LispObjectCompare(left->data.regex.pattern,
+ right->data.regex.pattern,
+ function);
+ break;
+ default:
+ break;
+ }
+ }
+
+compare_done:
+ return (result);
+}
+
+void
+LispCheckSequenceStartEnd(LispBuiltin *builtin,
+ LispObj *sequence, LispObj *start, LispObj *end,
+ long *pstart, long *pend, long *plength)
+{
+ /* Calculate length of sequence and check it's type */
+ *plength = LispLength(sequence);
+
+ /* Check start argument */
+ if (start == UNSPEC || start == NIL)
+ *pstart = 0;
+ else {
+ CHECK_INDEX(start);
+ *pstart = FIXNUM_VALUE(start);
+ }
+
+ /* Check end argument */
+ if (end == UNSPEC || end == NIL)
+ *pend = *plength;
+ else {
+ CHECK_INDEX(end);
+ *pend = FIXNUM_VALUE(end);
+ }
+
+ /* Check start argument */
+ if (*pstart > *pend)
+ LispDestroy("%s: :START %ld is larger than :END %ld",
+ STRFUN(builtin), *pstart, *pend);
+
+ /* Check end argument */
+ if (*pend > *plength)
+ LispDestroy("%s: :END %ld is larger then sequence length %ld",
+ STRFUN(builtin), *pend, *plength);
+}
+
+long
+LispLength(LispObj *sequence)
+{
+ long length;
+
+ if (sequence == NIL)
+ return (0);
+ switch (OBJECT_TYPE(sequence)) {
+ case LispString_t:
+ length = STRLEN(sequence);
+ break;
+ case LispArray_t:
+ if (sequence->data.array.rank != 1)
+ goto not_a_sequence;
+ sequence = sequence->data.array.list;
+ /*FALLTROUGH*/
+ case LispCons_t:
+ for (length = 0;
+ CONSP(sequence);
+ length++, sequence = CDR(sequence))
+ ;
+ break;
+ default:
+not_a_sequence:
+ LispDestroy("LENGTH: %s is not a sequence", STROBJ(sequence));
+ /*NOTREACHED*/
+ length = 0;
+ }
+
+ return (length);
+}
+
+LispObj *
+LispCharacterCoerce(LispBuiltin *builtin, LispObj *object)
+{
+ if (SCHARP(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 (INDEXP(object)) {
+ int c = FIXNUM_VALUE(object);
+
+ if (c <= 0xff)
+ return (SCHAR(c));
+ }
+ else if (object == T)
+ return (SCHAR('T'));
+
+ LispDestroy("%s: cannot convert %s to character",
+ STRFUN(builtin), STROBJ(object));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+LispStringCoerce(LispBuiltin *builtin, LispObj *object)
+{
+ if (STRINGP(object))
+ return (object);
+ else if (SYMBOLP(object))
+ return (LispSymbolName(object));
+ else if (SCHARP(object)) {
+ char string[1];
+
+ string[0] = SCHAR_VALUE(object);
+ return (LSTRING(string, 1));
+ }
+ else if (object == NIL)
+ return (LSTRING(Snil, 3));
+ else if (object == T)
+ return (LSTRING(St, 1));
+ else
+ LispDestroy("%s: cannot convert %s to string",
+ STRFUN(builtin), STROBJ(object));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+LispCoerce(LispBuiltin *builtin,
+ LispObj *object, LispObj *result_type)
+{
+ LispObj *result = NIL;
+ LispType type = LispNil_t;
+
+ if (result_type == NIL)
+ /* not even NIL can be converted to NIL? */
+ LispDestroy("%s: cannot convert %s to NIL",
+ STRFUN(builtin), STROBJ(object));
+
+ else if (result_type == T)
+ /* no conversion */
+ return (object);
+
+ else if (!SYMBOLP(result_type))
+ /* only know about simple types */
+ LispDestroy("%s: bad argument %s",
+ STRFUN(builtin), STROBJ(result_type));
+
+ else {
+ /* check all known types */
+
+ Atom_id atom = ATOMID(result_type);
+
+ if (atom == Satom) {
+ if (CONSP(object))
+ goto coerce_fail;
+ return (object);
+ }
+ /* only convert ATOM to SYMBOL */
+
+ if (atom == Sfloat)
+ type = LispDFloat_t;
+ else if (atom == Sinteger)
+ type = LispInteger_t;
+ else if (atom == Scons || atom == Slist) {
+ if (object == NIL)
+ return (object);
+ type = LispCons_t;
+ }
+ else if (atom == Sstring)
+ type = LispString_t;
+ else if (atom == Scharacter)
+ type = LispSChar_t;
+ else if (atom == Scomplex)
+ type = LispComplex_t;
+ else if (atom == Svector || atom == Sarray)
+ type = LispArray_t;
+ else if (atom == Sopaque)
+ type = LispOpaque_t;
+ else if (atom == Srational)
+ type = LispRatio_t;
+ else if (atom == Spathname)
+ type = LispPathname_t;
+ else
+ LispDestroy("%s: invalid type specification %s",
+ STRFUN(builtin), ATOMID(result_type));
+ }
+
+ if (OBJECT_TYPE(object) == LispOpaque_t) {
+ switch (type) {
+ case LispAtom_t:
+ result = ATOM(object->data.opaque.data);
+ break;
+ case LispString_t:
+ result = STRING(object->data.opaque.data);
+ break;
+ case LispSChar_t:
+ result = SCHAR((unsigned long)object->data.opaque.data);
+ break;
+ case LispDFloat_t:
+ result = DFLOAT((double)((long)object->data.opaque.data));
+ break;
+ case LispInteger_t:
+ result = INTEGER(((long)object->data.opaque.data));
+ break;
+ case LispOpaque_t:
+ result = OPAQUE(object->data.opaque.data, 0);
+ break;
+ default:
+ goto coerce_fail;
+ break;
+ }
+ }
+
+ else if (OBJECT_TYPE(object) != type) {
+ switch (type) {
+ case LispInteger_t:
+ if (INTEGERP(object))
+ result = object;
+ else if (DFLOATP(object)) {
+ if ((long)DFLOAT_VALUE(object) == DFLOAT_VALUE(object))
+ result = INTEGER((long)DFLOAT_VALUE(object));
+ else {
+ mpi *integer = LispMalloc(sizeof(mpi));
+
+ mpi_init(integer);
+ mpi_setd(integer, DFLOAT_VALUE(object));
+ if (mpi_getd(integer) != DFLOAT_VALUE(object)) {
+ mpi_clear(integer);
+ LispFree(integer);
+ goto coerce_fail;
+ }
+ result = BIGNUM(integer);
+ }
+ }
+ else
+ goto coerce_fail;
+ break;
+ case LispRatio_t:
+ if (DFLOATP(object)) {
+ mpr *ratio = LispMalloc(sizeof(mpr));
+
+ mpr_init(ratio);
+ mpr_setd(ratio, DFLOAT_VALUE(object));
+ if (mpr_fiti(ratio)) {
+ result = RATIO(mpi_geti(mpr_num(ratio)),
+ mpi_geti(mpr_den(ratio)));
+ mpr_clear(ratio);
+ LispFree(ratio);
+ }
+ else
+ result = BIGRATIO(ratio);
+ }
+ else if (RATIONALP(object))
+ result = object;
+ else
+ goto coerce_fail;
+ break;
+ case LispDFloat_t:
+ result = LispFloatCoerce(builtin, object);
+ break;
+ case LispComplex_t:
+ if (NUMBERP(object))
+ result = object;
+ else
+ goto coerce_fail;
+ break;
+ case LispString_t:
+ if (object == NIL)
+ result = STRING("");
+ else
+ result = LispStringCoerce(builtin, object);
+ break;
+ case LispSChar_t:
+ result = LispCharacterCoerce(builtin, object);
+ break;
+ case LispArray_t:
+ if (LISTP(object))
+ result = VECTOR(object);
+ else
+ goto coerce_fail;
+ break;
+ case LispCons_t:
+ if (ARRAYP(object) && object->data.array.rank == 1)
+ result = object->data.array.list;
+ else
+ goto coerce_fail;
+ break;
+ case LispPathname_t:
+ result = APPLY1(Oparse_namestring, object);
+ break;
+ default:
+ goto coerce_fail;
+ }
+ }
+ else
+ result = object;
+
+ return (result);
+
+coerce_fail:
+ LispDestroy("%s: cannot convert %s to %s",
+ STRFUN(builtin), STROBJ(object), ATOMID(result_type));
+ /* NOTREACHED */
+ return (NIL);
+}
+
+static LispObj *
+LispReallyDo(LispBuiltin *builtin, int refs)
+/*
+ do init test &rest body
+ do* init test &rest body
+ */
+{
+ GC_ENTER();
+ int stack, lex, head;
+ LispObj *list, *symbol, *value, *values, *cons;
+
+ LispObj *init, *test, *body;
+
+ body = ARGUMENT(2);
+ test = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ if (!CONSP(test))
+ LispDestroy("%s: end test condition must be a list, not %s",
+ STRFUN(builtin), STROBJ(init));
+
+ CHECK_LIST(init);
+
+ /* Save state */
+ stack = lisp__data.stack.length;
+ lex = lisp__data.env.lex;
+ head = lisp__data.env.length;
+
+ values = cons = NIL;
+ for (list = init; CONSP(list); list = CDR(list)) {
+ symbol = CAR(list);
+ if (!SYMBOLP(symbol)) {
+ CHECK_CONS(symbol);
+ value = CDR(symbol);
+ symbol = CAR(symbol);
+ CHECK_SYMBOL(symbol);
+ CHECK_CONS(value);
+ value = EVAL(CAR(value));
+ }
+ else
+ value = NIL;
+
+ CHECK_CONSTANT(symbol);
+
+ LispAddVar(symbol, value);
+
+ /* Bind variable now */
+ if (refs) {
+ ++lisp__data.env.head;
+ }
+ else {
+ if (values == NIL) {
+ values = cons = CONS(NIL, NIL);
+ GC_PROTECT(values);
+ }
+ else {
+ RPLACD(cons, CONS(NIL, NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ if (!refs)
+ lisp__data.env.head = lisp__data.env.length;
+
+ for (;;) {
+ if (EVAL(CAR(test)) != NIL)
+ break;
+
+ /* TODO Run this code in an implicit tagbody */
+ for (list = body; CONSP(list); list = CDR(list))
+ (void)EVAL(CAR(list));
+
+ /* Error checking already done in the initialization */
+ for (list = init, cons = values; CONSP(list); list = CDR(list)) {
+ symbol = CAR(list);
+ if (CONSP(symbol)) {
+ value = CDDR(symbol);
+ symbol = CAR(symbol);
+ if (CONSP(value))
+ value = EVAL(CAR(value));
+ else
+ value = NIL;
+ }
+ else
+ value = NIL;
+
+ if (refs)
+ LispSetVar(symbol, value);
+ else {
+ RPLACA(cons, value);
+ cons = CDR(cons);
+ }
+ }
+ if (!refs) {
+ for (list = init, cons = values;
+ CONSP(list);
+ list = CDR(list), cons = CDR(cons)) {
+ symbol = CAR(list);
+ if (CONSP(symbol)) {
+ if (CONSP(CDR(symbol)))
+ LispSetVar(CAR(symbol), CAR(cons));
+ }
+ }
+ }
+ }
+
+ if (CONSP(CDR(test)))
+ value = EVAL(CADR(test));
+ else
+ value = NIL;
+
+ /* Restore state */
+ lisp__data.stack.length = stack;
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = head;
+ GC_LEAVE();
+
+ return (value);
+}
+
+LispObj *
+LispDo(LispBuiltin *builtin, int refs)
+/*
+ do init test &rest body
+ do* init test &rest body
+ */
+{
+ int jumped, *pjumped;
+ LispObj *result, **presult;
+ LispBlock *block;
+
+ jumped = 1;
+ result = NIL;
+ presult = &result;
+ pjumped = &jumped;
+ block = LispBeginBlock(NIL, LispBlockTag);
+ if (setjmp(block->jmp) == 0) {
+ result = LispReallyDo(builtin, refs);
+ jumped = 0;
+ }
+ LispEndBlock(block);
+ if (jumped)
+ result = lisp__data.block.block_ret;
+
+ return (result);
+}
+
+static LispObj *
+LispReallyDoListTimes(LispBuiltin *builtin, int times)
+/*
+ dolist init &rest body
+ dotimes init &rest body
+ */
+{
+ GC_ENTER();
+ int head = lisp__data.env.length;
+ long count = 0, end = 0;
+ LispObj *symbol, *value = NIL, *result = NIL, *init, *body, *object;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ /* Parse arguments */
+ CHECK_CONS(init);
+ symbol = CAR(init);
+ CHECK_SYMBOL(symbol);
+ init = CDR(init);
+
+ if (init == NIL) {
+ if (times)
+ LispDestroy("%s: NIL is not a number", STRFUN(builtin));
+ }
+ else {
+ CHECK_CONS(init);
+ value = CAR(init);
+ init = CDR(init);
+ if (init != NIL) {
+ CHECK_CONS(init);
+ result = CAR(init);
+ }
+
+ value = EVAL(value);
+
+ if (times) {
+ CHECK_INDEX(value);
+ end = FIXNUM_VALUE(value);
+ }
+ else {
+ CHECK_LIST(value);
+ /* Protect iteration control from gc */
+ GC_PROTECT(value);
+ }
+ }
+
+ /* The variable is only bound inside the loop, so it is safe to optimize
+ * it out if there is no code to execute. But the result form may reference
+ * the bound variable. */
+ if (!CONSP(body)) {
+ if (times)
+ count = end;
+ else
+ value = NIL;
+ }
+
+ /* Initialize counter */
+ CHECK_CONSTANT(symbol);
+ if (times)
+ LispAddVar(symbol, FIXNUM(count));
+ else
+ LispAddVar(symbol, CONSP(value) ? CAR(value) : value);
+ ++lisp__data.env.head;
+
+ if (!CONSP(body) || (times && count >= end) || (!times && !CONSP(value)))
+ goto loop_done;
+
+ /* Execute iterations */
+ for (;;) {
+ for (object = body; CONSP(object); object = CDR(object))
+ (void)EVAL(CAR(object));
+
+ /* Update symbols and check exit condition */
+ if (times) {
+ ++count;
+ LispSetVar(symbol, FIXNUM(count));
+ if (count >= end)
+ break;
+ }
+ else {
+ value = CDR(value);
+ if (!CONSP(value)) {
+ LispSetVar(symbol, NIL);
+ break;
+ }
+ LispSetVar(symbol, CAR(value));
+ }
+ }
+
+loop_done:
+ result = EVAL(result);
+ lisp__data.env.head = lisp__data.env.length = head;
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+LispDoListTimes(LispBuiltin *builtin, int times)
+/*
+ dolist init &rest body
+ dotimes init &rest body
+ */
+{
+ int did_jump, *pdid_jump = &did_jump;
+ LispObj *result, **presult = &result;
+ LispBlock *block;
+
+ *presult = NIL;
+ *pdid_jump = 1;
+ block = LispBeginBlock(NIL, LispBlockTag);
+ if (setjmp(block->jmp) == 0) {
+ result = LispReallyDoListTimes(builtin, times);
+ did_jump = 0;
+ }
+ LispEndBlock(block);
+ if (did_jump)
+ result = lisp__data.block.block_ret;
+
+ return (result);
+}
+
+LispObj *
+LispLoadFile(LispObj *filename, int verbose, int print, int ifdoesnotexist)
+{
+ LispObj *stream, *cod, *obj, *result;
+ int ch;
+
+ LispObj *savepackage;
+ LispPackage *savepack;
+
+ if (verbose)
+ LispMessage("; Loading %s", THESTR(filename));
+
+ if (ifdoesnotexist) {
+ GC_ENTER();
+ result = CONS(filename, CONS(Kif_does_not_exist, CONS(Kerror, NIL)));
+ GC_PROTECT(result);
+ stream = APPLY(Oopen, result);
+ GC_LEAVE();
+ }
+ else
+ stream = APPLY1(Oopen, filename);
+
+ if (stream == NIL)
+ return (NIL);
+
+ result = NIL;
+ LispPushInput(stream);
+ ch = LispGet();
+ if (ch != '#')
+ LispUnget(ch);
+ else if ((ch = LispGet()) == '!') {
+ for (;;) {
+ ch = LispGet();
+ if (ch == '\n' || ch == EOF)
+ break;
+ }
+ }
+ else {
+ LispUnget(ch);
+ LispUnget('#');
+ }
+
+ /* Save package environment */
+ savepackage = PACKAGE;
+ savepack = lisp__data.pack;
+
+ cod = COD;
+
+ /*CONSTCOND*/
+ while (1) {
+ if ((obj = LispRead()) != NULL) {
+ result = EVAL(obj);
+ COD = cod;
+ if (print) {
+ int i;
+
+ if (RETURN_COUNT >= 0)
+ LispPrint(result, NIL, 1);
+ for (i = 0; i < RETURN_COUNT; i++)
+ LispPrint(RETURN(i), NIL, 1);
+ }
+ }
+ if (lisp__data.eof)
+ break;
+ }
+ LispPopInput(stream);
+
+ /* Restore package environment */
+ PACKAGE = savepackage;
+ lisp__data.pack = savepack;
+
+ APPLY1(Oclose, stream);
+
+ return (T);
+}
+
+void
+LispGetStringArgs(LispBuiltin *builtin,
+ char **string1, char **string2,
+ long *start1, long *end1, long *start2, long *end2)
+{
+ long length1, length2;
+ LispObj *ostring1, *ostring2, *ostart1, *oend1, *ostart2, *oend2;
+
+ oend2 = ARGUMENT(5);
+ ostart2 = ARGUMENT(4);
+ oend1 = ARGUMENT(3);
+ ostart1 = ARGUMENT(2);
+ ostring2 = ARGUMENT(1);
+ ostring1 = ARGUMENT(0);
+
+ CHECK_STRING(ostring1);
+ *string1 = THESTR(ostring1);
+ length1 = STRLEN(ostring1);
+
+ CHECK_STRING(ostring2);
+ *string2 = THESTR(ostring2);
+ length2 = STRLEN(ostring2);
+
+ if (ostart1 == UNSPEC)
+ *start1 = 0;
+ else {
+ CHECK_INDEX(ostart1);
+ *start1 = FIXNUM_VALUE(ostart1);
+ }
+ if (oend1 == UNSPEC)
+ *end1 = length1;
+ else {
+ CHECK_INDEX(oend1);
+ *end1 = FIXNUM_VALUE(oend1);
+ }
+
+ if (ostart2 == UNSPEC)
+ *start2 = 0;
+ else {
+ CHECK_INDEX(ostart2);
+ *start2 = FIXNUM_VALUE(ostart2);
+ }
+
+ if (oend2 == UNSPEC)
+ *end2 = length2;
+ else {
+ CHECK_INDEX(oend2);
+ *end2 = FIXNUM_VALUE(oend2);
+ }
+
+ if (*start1 > *end1)
+ LispDestroy("%s: :START1 %ld larger than :END1 %ld",
+ STRFUN(builtin), *start1, *end1);
+ if (*start2 > *end2)
+ LispDestroy("%s: :START2 %ld larger than :END2 %ld",
+ STRFUN(builtin), *start2, *end2);
+ if (*end1 > length1)
+ LispDestroy("%s: :END1 %ld larger than string length %ld",
+ STRFUN(builtin), *end1, length1);
+ if (*end2 > length2)
+ LispDestroy("%s: :END2 %ld larger than string length %ld",
+ STRFUN(builtin), *end2, length2);
+}
+
+LispObj *
+LispPathnameField(int field, int string)
+{
+ int offset = field;
+ LispObj *pathname, *result, *object;
+
+ pathname = ARGUMENT(0);
+
+ if (PATHNAMEP(pathname))
+ pathname = APPLY1(Oparse_namestring, pathname);
+
+ result = pathname->data.pathname;
+ while (offset) {
+ result = CDR(result);
+ --offset;
+ }
+ object = result;
+ result = CAR(result);
+
+ if (string) {
+ if (!STRINGP(result)) {
+ if (result == NIL)
+ result = STRING("");
+ else if (field == PATH_DIRECTORY) {
+ char *name = THESTR(CAR(pathname->data.pathname)), *ptr;
+
+ ptr = strrchr(name, PATH_SEP);
+ if (ptr) {
+ int length = ptr - name + 1;
+ char data[PATH_MAX];
+
+ if (length > PATH_MAX - 1)
+ length = PATH_MAX - 1;
+ strncpy(data, name, length);
+ data[length] = '\0';
+ result = STRING(data);
+ }
+ else
+ result = STRING("");
+ }
+ else
+ result = Kunspecific;
+ }
+ else if (field == PATH_NAME) {
+ object = CAR(CDR(object));
+ if (STRINGP(object)) {
+ int length;
+ char name[PATH_MAX + 1];
+
+ strcpy(name, THESTR(result));
+ length = STRLEN(result);
+ if (length + 1 < sizeof(name)) {
+ name[length++] = PATH_TYPESEP;
+ name[length] = '\0';
+ }
+ if (STRLEN(object) + length < sizeof(name))
+ strcpy(name + length, THESTR(object));
+ /* else LispDestroy ... */
+ result = STRING(name);
+ }
+ }
+ }
+
+ return (result);
+}
+
+LispObj *
+LispProbeFile(LispBuiltin *builtin, int probe)
+{
+ GC_ENTER();
+ LispObj *result;
+ char *name = NULL, resolved[PATH_MAX + 1];
+ struct stat st;
+
+ LispObj *pathname;
+
+ pathname = ARGUMENT(0);
+
+ if (!POINTERP(pathname))
+ goto bad_pathname;
+
+ if (XSTRINGP(pathname))
+ name = THESTR(pathname);
+ else if (XPATHNAMEP(pathname))
+ name = THESTR(CAR(pathname->data.pathname));
+ else if (STREAMP(pathname) && pathname->data.stream.type == LispStreamFile)
+ name = THESTR(CAR(pathname->data.stream.pathname->data.pathname));
+
+ if (realpath(name, &resolved[0]) == NULL ||
+ stat(resolved, &st)) {
+ if (probe)
+ return (NIL);
+ LispDestroy("%s: realpath(\"%s\"): %s",
+ STRFUN(builtin), name, strerror(errno));
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ int length = strlen(resolved);
+
+ if (!length || resolved[length - 1] != PATH_SEP) {
+ resolved[length++] = PATH_SEP;
+ resolved[length] = '\0';
+ }
+ }
+
+ result = STRING(resolved);
+ GC_PROTECT(result);
+ result = APPLY1(Oparse_namestring, result);
+ GC_LEAVE();
+
+ return (result);
+
+bad_pathname:
+ LispDestroy("%s: bad pathname %s", STRFUN(builtin), STROBJ(pathname));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+LispWriteString_(LispBuiltin *builtin, int newline)
+/*
+ write-line string &optional output-stream &key start end
+ write-string string &optional output-stream &key start end
+ */
+{
+ char *text;
+ long start, end, length;
+
+ LispObj *string, *output_stream, *ostart, *oend;
+
+ oend = ARGUMENT(3);
+ ostart = ARGUMENT(2);
+ output_stream = ARGUMENT(1);
+ string = ARGUMENT(0);
+
+ CHECK_STRING(string);
+ LispCheckSequenceStartEnd(builtin, string, ostart, oend,
+ &start, &end, &length);
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ text = THESTR(string);
+ if (end > start)
+ LispWriteStr(output_stream, text + start, end - start);
+ if (newline)
+ LispWriteChar(output_stream, '\n');
+
+ return (string);
+}
diff --git a/lisp/helper.h b/lisp/helper.h
new file mode 100644
index 0000000..865f397
--- /dev/null
+++ b/lisp/helper.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/helper.h,v 1.15 2002/11/25 02:35:30 paulo Exp $ */
+
+#ifndef Lisp_helper_h
+#define Lisp_helper_h
+
+#include "private.h"
+
+/*
+ * Prototypes
+ */
+void LispCheckSequenceStartEnd(LispBuiltin*, LispObj*,
+ LispObj*, LispObj*, long*, long*, long*);
+long LispLength(LispObj*);
+LispObj *LispCharacterCoerce(LispBuiltin*, LispObj*);
+LispObj *LispStringCoerce(LispBuiltin*, LispObj*);
+LispObj *LispCoerce(LispBuiltin*, LispObj*, LispObj*);
+
+/*
+ do init test &rest body
+ do* init test &rest body
+ */
+LispObj *LispDo(LispBuiltin*, int);
+
+/*
+ dolist init &rest body
+ dotimes init &rest body
+ */
+LispObj *LispDoListTimes(LispBuiltin*, int);
+
+#define FEQ 1
+#define FEQL 2
+#define FEQUAL 3
+#define FEQUALP 4
+LispObj *LispObjectCompare(LispObj*, LispObj*, int);
+#define XEQ(x, y) LispObjectCompare(x, y, FEQ)
+#define XEQL(x, y) LispObjectCompare(x, y, FEQL)
+#define XEQUAL(x, y) LispObjectCompare(x, y, FEQUAL)
+#define XEQUALP(x, y) LispObjectCompare(x, y, FEQUALP)
+
+LispObj *LispLoadFile(LispObj*, int, int, int);
+
+/*
+ string= string1 string2 &key start1 end1 start2 end2
+ string< string1 string2 &key start1 end1 start2 end2
+ string> string1 string2 &key start1 end1 start2 end2
+ string<= string1 string2 &key start1 end1 start2 end2
+ string>= string1 string2 &key start1 end1 start2 end2
+ string/= string1 string2 &key start1 end1 start2 end2
+ string-equal string1 string2 &key start1 end1 start2 end2
+ string-lessp string1 string2 &key start1 end1 start2 end2
+ string-greaterp string1 string2 &key start1 end1 start2 end2
+ string-not-lessp string1 string2 &key start1 end1 start2 end2
+ string-not-greaterp string1 string2 &key start1 end1 start2 end2
+ string-not-equal string1 string2 &key start1 end1 start2 end2
+*/
+void LispGetStringArgs(LispBuiltin*,
+ char**, /* string1 */
+ char**, /* string2 */
+ long*, /* start1 */
+ long*, /* end1 */
+ long*, /* start2 */
+ long*); /* end2 */
+
+/*
+ pathname-host pathname
+ pathname-device pathname
+ pathname-directory pathname
+ pathname-name pathname
+ pathname-type pathname
+ pathname-version pathname
+ */
+LispObj *LispPathnameField(int, int);
+
+/*
+ truename pathname
+ probe-file pathname
+ */
+LispObj *LispProbeFile(LispBuiltin*, int);
+
+/*
+ write-string string &optional output-stream &key start end
+ write-line string &optional output-stream &key start end
+ */
+LispObj *LispWriteString_(LispBuiltin*, int);
+
+#endif /* Lisp_helper_h */
diff --git a/lisp/internal.h b/lisp/internal.h
new file mode 100644
index 0000000..1061618
--- /dev/null
+++ b/lisp/internal.h
@@ -0,0 +1,784 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/internal.h,v 1.49 2002/12/11 04:44:27 paulo Exp $ */
+
+#ifndef Lisp_internal_h
+#define Lisp_internal_h
+
+#include <stdio.h>
+#include "lisp.h"
+
+#include "mp.h"
+#include "re.h"
+
+/*
+ * Defines
+ */
+typedef struct _LispMac LispMac;
+
+#define STREAM_READ 0x01
+#define STREAM_WRITE 0x02
+#define STREAM_BINARY 0x20
+
+#define RPLACA(cons, object) (CAR(cons) = object)
+#define RPLACD(cons, object) (CDR(cons) = object)
+
+#define CAR(list) ((list)->data.cons.car)
+#define CAAR(list) ((list)->data.cons.car->data.cons.car)
+#define CADR(list) ((list)->data.cons.cdr->data.cons.car)
+#define CDR(list) ((list)->data.cons.cdr)
+#define CDAR(list) ((list)->data.cons.car->data.cons.cdr)
+#define CDDR(list) ((list)->data.cons.cdr->data.cons.cdr)
+#define CONS(car, cdr) LispNewCons(car, cdr)
+#define EVAL(list) LispEval(list)
+#define APPLY(fun, args) LispFuncall(fun, args, 0)
+#define APPLY1(fun, arg) LispApply1(fun, arg)
+#define APPLY2(fun, arg1, arg2) LispApply2(fun, arg1, arg2)
+#define APPLY3(f, a1, a2, a3) LispApply3(f, a1, a2, a3)
+#define EXECUTE(string) LispExecute(string)
+#define SYMBOL(atom) LispNewSymbol(atom)
+#define ATOM(string) LispNewAtom(string, 1)
+#define UNINTERNED_ATOM(string) LispNewAtom(string, 0)
+#define FUNCTION(symbol) LispNewFunction(symbol)
+#define FUNCTION_QUOTE(symbol) LispNewFunctionQuote(symbol)
+
+ /* atom string is a static variable */
+#define ATOM2(string) LispNewSymbol(LispGetPermAtom(string))
+
+ /* make a gc never released variable with a static string argument */
+#define STATIC_ATOM(string) LispNewStaticAtom(string)
+
+#define STRING(str) LispNewString(str, strlen(str), 0)
+#define LSTRING(str, size) LispNewString(str, size, 0)
+
+ /* string must be from the LispXXX allocation functions,
+ * and LispMused not yet called on it */
+#define STRING2(str) LispNewString(str, strlen(str), 1)
+#define LSTRING2(str, size) LispNewString(str, size, 1)
+
+#define VECTOR(objects) LispNewVector(objects)
+
+ /* STRINGSTREAM2 and LSTRINGSTREAM2 require that the
+ * string be allocated from the LispXXX allocation functions,
+ * and LispMused not yet called on it */
+#define STRINGSTREAM(str, flag) \
+ LispNewStringStream(str, flag, strlen(str), 0)
+#define STRINGSTREAM2(str, flag) \
+ LispNewStringStream(str, flag, strlen(str), 1)
+#define LSTRINGSTREAM(str, flag, length) \
+ LispNewStringStream(str, flag, length, 0)
+#define LSTRINGSTREAM2(str, flag, length) \
+ LispNewStringStream(str, flag, length, 1)
+
+#define FILESTREAM(file, path, flag) \
+ LispNewFileStream(file, path, flag)
+#define PIPESTREAM(file, path, flag) \
+ LispNewPipeStream(file, path, flag)
+
+#define CHECKO(obj, typ) \
+ ((obj)->type == LispOpaque_t && \
+ ((obj)->data.opaque.type == typ || (obj)->data.opaque.type == 0))
+#define PROTECT(key, list) LispProtect(key, list)
+#define UPROTECT(key, list) LispUProtect(key, list)
+
+/* create a new unique static atom string */
+#define GETATOMID(string) LispGetAtomString(string, 1)
+
+#define GCDisable() ++gcpro
+#define GCEnable() --gcpro
+
+
+/* pointer to something unique to all atoms with the same print representation */
+#define ATOMID(object) (object)->data.atom->string
+
+
+
+#define NIL_BIT 0x01
+#define FIXNUM_BIT 0x02
+#define FIXNUM_MASK 0x03
+#define SCHAR_BIT 0x04
+#define SCHAR_MASK 0x05
+#define BIT_COUNT 4
+#define BIT_MASK 0x0f
+#define POINTERP(object) \
+ (((unsigned long)(object) & NIL_BIT) == 0)
+
+#define MOST_POSITIVE_FIXNUM ((1L << (sizeof(long) * 8 - 5)) - 1)
+#define MOST_NEGATIVE_FIXNUM (-1L << (sizeof(long) * 8 - 5))
+
+#define SCHAR(value) \
+ ((LispObj*)(((long)(value) << BIT_COUNT) | SCHAR_MASK))
+#define SCHAR_VALUE(object) FIXNUM_VALUE(object)
+#define SCHARP(object) \
+ (((unsigned long)(object) & BIT_MASK) == SCHAR_MASK)
+#define CHECK_SCHAR(object) \
+ if (!SCHARP(object)) \
+ LispDestroy("%s: %s is not a character", \
+ STRFUN(builtin), STROBJ(object))
+
+#define XOBJECT_TYPE(object) ((object)->type)
+#define OBJECT_TYPE(object) \
+ (POINTERP(object) ? XOBJECT_TYPE(object) : (long)(object) & BIT_MASK)
+
+
+#define NIL (LispObj*)0x00000001
+#define T (LispObj*)0x00000011
+#define DOT (LispObj*)0x00000021
+/* unmatched ')' */
+#define EOLIST (LispObj*)0x00000031
+#define READLABEL_MASK 0x00000041
+/* unspecified argument */
+#define UNSPEC (LispObj*)0x00000051
+#define INVALIDP(object) \
+ ((object) == NULL || (object) == EOLIST || (object) == DOT)
+
+
+/* cons */
+#define XCONSP(object) ((object)->type == LispCons_t)
+#define CONSP(object) (POINTERP(object) && XCONSP(object))
+#define CHECK_CONS(object) \
+ if (!CONSP(object)) \
+ LispDestroy("%s: %s is not of type cons", \
+ STRFUN(builtin), STROBJ(object))
+#define LISTP(object) (object == NIL || CONSP(object))
+#define CHECK_LIST(object) \
+ if (!LISTP(object)) \
+ LispDestroy("%s: %s is not a list", \
+ STRFUN(builtin), STROBJ(object))
+
+/* fixnum */
+#define FIXNUM(value) \
+ ((LispObj*)(((long)(value) << BIT_COUNT) | FIXNUM_MASK))
+#define FIXNUM_VALUE(object) ((long)(object) >> BIT_COUNT)
+#define FIXNUMP(object) \
+ (((unsigned long)(object) & BIT_MASK) == FIXNUM_MASK)
+#define CHECK_FIXNUM(object) \
+ if (!FIXNUMP(object)) \
+ LispDestroy("%s: %s is not a fixnum", \
+ STRFUN(builtin), STROBJ(object))
+#define INDEXP(object) \
+ (FIXNUMP(object) && FIXNUM_VALUE(object) >= 0)
+#define CHECK_INDEX(object) \
+ if (!INDEXP(object)) \
+ LispDestroy("%s: %s is not a positive fixnum", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* long int integer */
+#define XINTP(object) ((object)->type == LispInteger_t)
+#define INTP(objet) (POINTERP(object) && XINTP(object))
+#define INT_VALUE(object) (object)->data.integer
+
+
+/* values that fit in a machine long int but not in a fixnum */
+#define LONGINTP(object) \
+ (POINTERP(object) ? XINTP(object) : FIXNUMP(object))
+#define LONGINT_VALUE(object) \
+ (POINTERP(object) ? INT_VALUE(object) : FIXNUM_VALUE(object))
+#define CHECK_LONGINT(object) \
+ if (!LONGINTP(object)) \
+ LispDestroy("%s: %s is not an integer", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* bignum */
+#define XBIGNUMP(object) ((object)->type == LispBignum_t)
+#define BIGNUMP(object) (POINTERP(object) && XBIGNUMP(object))
+#define BIGNUM(object) LispNewBignum(object)
+
+
+/* generic integer */
+#define INTEGER(integer) LispNewInteger(integer)
+#define INTEGERP(object) \
+ (POINTERP(object) ? XINTP(object) || XBIGNUMP(object) : FIXNUMP(object))
+#define CHECK_INTEGER(object) \
+ if (!INTEGERP(object)) \
+ LispDestroy("%s: %s is not an integer", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* ratio */
+#define XRATIOP(object) ((object)->type == LispRatio_t)
+#define RATIOP(object) (POINTERP(object) && XRATIOP(object))
+#define RATIO(num, den) LispNewRatio(num, den)
+
+
+/* bigratio */
+#define XBIGRATIOP(object) ((object)->type == LispBigratio_t)
+#define BIGRATIOP(object) (POINTERP(object) && XBIGRATIOP(object))
+#define BIGRATIO(ratio) LispNewBigratio(ratio)
+
+
+/* generic rational */
+#define RATIONALP(object) \
+ (POINTERP(object) ? XINTP(object) || XRATIOP(object) || \
+ XBIGNUMP(object) || XBIGRATIOP(object) : \
+ FIXNUMP(object))
+
+
+/* double float */
+#define XDFLOATP(object) ((object)->type == LispDFloat_t)
+#define DFLOATP(object) (POINTERP(object) && XDFLOATP(object))
+#define DFLOAT_VALUE(object) (object)->data.dfloat
+#define CHECK_DFLOAT(object) \
+ if (!DFLOATP(object)) \
+ LispDestroy("%s: %s is not a float number", \
+ STRFUN(builtin), STROBJ(object))
+#define DFLOAT(value) LispNewDFloat(value)
+
+
+/* generic float - currently only double float supported */
+#define FLOATP(object) DFLOATP(object)
+
+
+/* real number */
+#define REALP(object) \
+ (POINTERP(object) ? XINTP(object) || XDFLOATP(object) || \
+ XRATIOP(object) || XBIGNUMP(object) || \
+ XBIGRATIOP(object) : \
+ FIXNUMP(object))
+#define CHECK_REAL(object) \
+ if (!REALP(object)) \
+ LispDestroy("%s: %s is not a real number", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* complex */
+#define XCOMPLEXP(object) ((object)->type == LispComplex_t)
+#define COMPLEXP(object) (POINTERP(object) && XCOMPLEXP(object))
+#define COMPLEX(real, imag) LispNewComplex(real, imag)
+
+
+/* generic number */
+#define NUMBERP(object) \
+ (POINTERP(object) ? XINTP(object) || XDFLOATP(object) || \
+ XRATIOP(object) || XBIGNUMP(object) || \
+ XBIGRATIOP(object) || XCOMPLEXP(object) : \
+ FIXNUMP(object))
+#define CHECK_NUMBER(object) \
+ if (!NUMBERP(object)) \
+ LispDestroy("%s: %s is not a number", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* symbol */
+#define XSYMBOLP(object) ((object)->type == LispAtom_t)
+#define SYMBOLP(object) (POINTERP(object) && XSYMBOLP(object))
+#define CHECK_SYMBOL(object) \
+ if (!SYMBOLP(object)) \
+ LispDestroy("%s: %s is not a symbol", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* keyword */
+#define XKEYWORDP(object) \
+ ((object)->data.atom->package == lisp__data.keyword)
+#define KEYWORDP(object) \
+ (POINTERP(object) && XSYMBOLP(object) && XKEYWORDP(object))
+#define KEYWORD(string) LispNewKeyword(string)
+#define CHECK_KEYWORD(object) \
+ if (!KEYWORDP(object)) \
+ LispDestroy("%s: %s is not a keyword", \
+ STRFUN(builtin), STROBJ(object))
+#define CHECK_CONSTANT(object) \
+ if ((object)->data.atom->constant) \
+ LispDestroy("%s: %s is a constant", \
+ STRFUN(builtin), STROBJ(object))
+
+#define SETVALUE(atom, object) ((atom)->property->value = object)
+
+
+/* function */
+#define XFUNCTIONP(object) ((object)->type == LispFunction_t)
+#define FUNCTIONP(object) (POINTERP(object) && XFUNCTIONP(object))
+
+
+/* lambda */
+#define XLAMBDAP(object) ((object)->type == LispLambda_t)
+#define LAMBDAP(object) (POINTERP(object) && XLAMBDAP(object))
+
+
+/* string - currently only simple 8 bit characters */
+#define XSTRINGP(object) ((object)->type == LispString_t)
+#define STRINGP(object) (POINTERP(object) && XSTRINGP(object))
+#define THESTR(object) (object)->data.string.string
+#define STRLEN(object) (object)->data.string.length
+#define CHECK_STRING(object) \
+ if (!STRINGP(object)) \
+ LispDestroy("%s: %s is not a string", \
+ STRFUN(builtin), STROBJ(object))
+#define CHECK_STRING_WRITABLE(object) \
+ if (!object->data.string.writable) \
+ LispDestroy("%s: %s is readonly", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* array/vector */
+#define XARRAYP(object) ((object)->type == LispArray_t)
+#define ARRAYP(object) (POINTERP(object) && XARRAYP(object))
+#define CHECK_ARRAY(object) \
+ if (!ARRAYP(object)) \
+ LispDestroy("%s: %s is not an array", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* quote */
+#define XQUOTEP(object) ((object)->type == LispQuote_t)
+#define QUOTEP(object) (POINTERP(object) && XQUOTEP(object))
+#define QUOTE(object) LispNewQuote(object)
+
+#define XBACKQUOTEP(object) ((object)->type == LispBackquote_t)
+#define BACKQUOTEP(object) (POINTERP(object) && XBACKQUOTEP(object))
+#define BACKQUOTE(object) LispNewBackquote(object)
+
+#define XCOMMAP(object) ((object)->type == LispComma_t)
+#define COMMAP(object) (POINTERP(object) && XCOMMAP(object))
+#define COMMA(object, at) LispNewComma(object, at)
+
+
+/* package */
+#define XPACKAGEP(object) ((object)->type == LispPackage_t)
+#define PACKAGEP(object) (POINTERP(object) && XPACKAGEP(object))
+
+
+/* pathname */
+#define XPATHNAMEP(object) ((object)->type == LispPathname_t)
+#define PATHNAMEP(object) (POINTERP(object) && XPATHNAMEP(object))
+#define PATHNAME(object) LispNewPathname(object)
+#define CHECK_PATHNAME(object) \
+ if (!PATHNAMEP(object)) \
+ LispDestroy("%s: %s is not a pathname", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* stream */
+#define XSTREAMP(object) ((object)->type == LispStream_t)
+#define STREAMP(object) (POINTERP(object) && XSTREAMP(object))
+#define CHECK_STREAM(object) \
+ if (!STREAMP(object)) \
+ LispDestroy("%s: %s is not a stream", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* hastable */
+#define XHASHTABLEP(object) ((object)->type == LispHashTable_t)
+#define HASHTABLEP(object) (POINTERP(object) && XHASHTABLEP(object))
+#define CHECK_HASHTABLE(object) \
+ if (!HASHTABLEP(object)) \
+ LispDestroy("%s: %s is not a hash-table", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* regex */
+#define XREGEXP(object) ((object)->type == LispRegex_t)
+#define REGEXP(object) (POINTERP(object) && XREGEXP(object))
+#define CHECK_REGEX(object) \
+ if (!REGEXP(object)) \
+ LispDestroy("%s: %s is not a regexp", \
+ STRFUN(builtin), STROBJ(object))
+
+
+/* bytecode */
+#define XBYTECODEP(object) ((object)->type == LispBytecode_t)
+#define BYTECODEP(object) (POINTERP(object) && XBYTECODEP(object))
+
+
+/* opaque */
+#define XOPAQUEP(object) ((object)->type == LispOpaque_t)
+#define OPAQUEP(object) (POINTERP(object) && XOPAQUEP(object))
+#define OPAQUE(data, type) LispNewOpaque((void*)((long)data), type)
+
+
+
+#define SSTREAMP(str) ((str)->data.stream.source.string)
+
+#define FSTREAMP(str) ((str)->data.stream.source.file)
+
+#define PSTREAMP(str) ((str)->data.stream.source.program)
+#define PIDPSTREAMP(str) ((str)->data.stream.source.program->pid)
+#define IPSTREAMP(str) ((str)->data.stream.source.program->input)
+#define OPSTREAMP(str) ((str)->data.stream.source.program->output)
+#define EPSTREAMP(str) \
+ FSTREAMP((str)->data.stream.source.program->errorp)
+
+#define LispFileno(file) ((file)->descriptor)
+
+#define STRFUN(builtin) ATOMID(builtin->symbol)
+#define STROBJ(obj) LispStrObj(obj)
+
+/* fetch builtin function/macro argument value
+ */
+#define ARGUMENT(index) \
+ lisp__data.stack.values[lisp__data.stack.base + (index)]
+
+#define RETURN(index) lisp__data.returns.values[(index)]
+#define RETURN_COUNT lisp__data.returns.count
+#define RETURN_CHECK(value) \
+ value < MULTIPLE_VALUES_LIMIT ? \
+ value : MULTIPLE_VALUES_LIMIT
+
+#define GC_ENTER() int gc__protect = lisp__data.protect.length
+
+#define GC_PROTECT(object) \
+ if (lisp__data.protect.length >= lisp__data.protect.space) \
+ LispMoreProtects(); \
+ lisp__data.protect.objects[lisp__data.protect.length++] = object
+
+#define GC_LEAVE() lisp__data.protect.length = gc__protect
+
+
+#define ERROR_CHECK_SPECIAL_FORM(atom) \
+ if (atom->property->fun.builtin->compile) \
+ LispDestroy("%s: the special form %s cannot be redefined", \
+ STRFUN(builtin), atom->string)
+
+
+
+#define CONSTANTP(object) \
+ (!POINTERP(object) || \
+ XOBJECT_TYPE(object) < LispAtom_t || \
+ (XSYMBOLP(object) && XKEYWORDP(object)))
+
+/* slightly faster test, since keywords are very uncommon as eval arguments */
+#define NCONSTANTP(object) \
+ (OBJECT_TYPE(object) >= LispAtom_t)
+
+
+/*
+ * Types
+ */
+typedef struct _LispObj LispObj;
+typedef struct _LispAtom LispAtom;
+typedef struct _LispBuiltin LispBuiltin;
+typedef struct _LispModuleData LispModuleData;
+typedef struct _LispFile LispFile;
+typedef struct _LispString LispString;
+typedef struct _LispPackage LispPackage;
+typedef struct _LispBytecode LispBytecode;
+typedef struct _LispHashTable LispHashTable;
+
+/* Bytecode compiler data */
+typedef struct _LispCom LispCom;
+
+typedef char *Atom_id;
+
+typedef enum _LispType {
+ /* objects encoded in the LispObj pointer */
+ LispNil_t = 1,
+ LispFixnum_t = 3,
+ LispSChar_t = 5,
+
+ /* objects that have a structure */
+ LispInteger_t = 16,
+ LispDFloat_t,
+ LispString_t,
+ LispRatio_t,
+ LispOpaque_t,
+
+ /* simple access for marking */
+ LispBignum_t,
+ LispBigratio_t,
+
+ LispAtom_t,
+ LispFunction_t,
+ LispFunctionQuote_t,
+
+ LispLambda_t,
+
+ LispComplex_t,
+ LispCons_t,
+ LispQuote_t,
+ LispArray_t,
+ LispStruct_t,
+ LispStream_t,
+ LispBackquote_t,
+ LispComma_t,
+ LispPathname_t,
+ LispPackage_t,
+ LispRegex_t,
+ LispBytecode_t,
+ LispHashTable_t
+} LispType;
+
+typedef enum _LispFunType {
+ LispLambda,
+ LispFunction,
+ LispMacro,
+ LispSetf
+} LispFunType;
+
+typedef enum _LispStreamType {
+ LispStreamString,
+ LispStreamFile,
+ LispStreamStandard,
+ LispStreamPipe
+} LispStreamType;
+
+typedef struct {
+ int pid; /* process id of program */
+ LispFile *input; /* if READABLE: stdout of program */
+ LispFile *output; /* if WRITABLE: stdin of program */
+ LispObj *errorp; /* ALWAYS (ONLY) READABLE: stderr of program */
+} LispPipe;
+
+struct _LispObj {
+ LispType type : 6;
+ unsigned int mark : 1; /* gc protected */
+ unsigned int prot: 1; /* protection for constant/unamed variables */
+ LispFunType funtype : 4; /* this is subject to change in the future */
+ union {
+ LispAtom *atom;
+ struct {
+ char *string;
+ long length;
+ int writable : 1;
+ } string;
+ long integer;
+ double dfloat;
+ LispObj *quote;
+ LispObj *pathname; /* don't use quote generic name,
+ * to avoid confusing code */
+ struct {
+ long numerator;
+ long denominator;
+ } ratio;
+ union {
+ mpi *integer;
+ mpr *ratio;
+ } mp;
+ struct {
+ LispObj *real;
+ LispObj *imag;
+ } complex;
+ struct {
+ LispObj *car;
+ LispObj *cdr;
+ } cons;
+ struct {
+ LispObj *name;
+ LispObj *code;
+ LispObj *data; /* extra data to protect */
+ } lambda;
+ struct {
+ LispObj *list; /* stored as a linear list */
+ LispObj *dim; /* dimensions of array */
+ unsigned int rank : 8; /* i.e. array-rank-limit => 256 */
+ unsigned int type : 7; /* converted to LispType, if not
+ * Lisp_Nil_t only accepts given
+ * type in array fields */
+ unsigned int zero : 1; /* at least one of the dimensions
+ * is zero */
+ } array;
+ struct {
+ LispObj *fields; /* structure fields */
+ LispObj *def; /* structure definition */
+ } struc;
+ struct {
+ union {
+ LispFile *file;
+ LispPipe *program;
+ LispString *string;
+ } source;
+ LispObj *pathname;
+ LispStreamType type : 6;
+ int readable : 1;
+ int writable : 1;
+ } stream;
+ struct {
+ void *data;
+ int type;
+ } opaque;
+ struct {
+ LispObj *eval;
+ int atlist;
+ } comma;
+ struct {
+ LispObj *name;
+ LispObj *nicknames;
+ LispPackage *package;
+ } package;
+ struct {
+ re_cod *regex;
+ LispObj *pattern; /* regex string */
+ int options; /* regex compile flags */
+ } regex;
+ struct {
+ LispBytecode *bytecode;
+ LispObj *code; /* object used to generate bytecode */
+ LispObj *name; /* name of function, or NIL */
+ } bytecode;
+ struct {
+ LispHashTable *table;
+ LispObj *test;
+ } hash;
+ } data;
+};
+
+typedef LispObj *(*LispFunPtr)(LispBuiltin*);
+typedef void (*LispComPtr)(LispCom*, LispBuiltin*);
+
+struct _LispBuiltin {
+ /* these fields must be set */
+ LispFunType type;
+ LispFunPtr function;
+ char *declaration;
+
+ /* this field is optional, set if the function returns multiple values */
+ int multiple_values;
+
+ /* this field is also optional, set if the function should not be exported */
+ int internal;
+
+ /* this optional field points to a function of the bytecode compiler */
+ LispComPtr compile;
+
+ /* this field is set at runtime */
+ LispObj *symbol;
+};
+
+typedef int (*LispLoadModule)(void);
+typedef int (*LispUnloadModule)(void);
+
+#define LISP_MODULE_VERSION 1
+struct _LispModuleData {
+ int version;
+ LispLoadModule load;
+ LispUnloadModule unload;
+};
+
+/*
+ * Prototypes
+ */
+LispObj *LispEval(LispObj*);
+LispObj *LispFuncall(LispObj*, LispObj*, int);
+LispObj *LispApply1(LispObj*, LispObj*);
+LispObj *LispApply2(LispObj*, LispObj*, LispObj*);
+LispObj *LispApply3(LispObj*, LispObj*, LispObj*, LispObj*);
+
+LispObj *LispNew(LispObj*, LispObj*);
+LispObj *LispNewSymbol(LispAtom*);
+LispObj *LispNewAtom(char*, int);
+LispObj *LispNewFunction(LispObj*);
+LispObj *LispNewFunctionQuote(LispObj*);
+LispObj *LispNewStaticAtom(char*);
+LispObj *LispNewDFloat(double);
+LispObj *LispNewString(char*, long, int);
+LispObj *LispNewInteger(long);
+LispObj *LispNewRatio(long, long);
+LispObj *LispNewVector(LispObj*);
+LispObj *LispNewQuote(LispObj*);
+LispObj *LispNewBackquote(LispObj*);
+LispObj *LispNewComma(LispObj*, int);
+LispObj *LispNewCons(LispObj*, LispObj*);
+LispObj *LispNewLambda(LispObj*, LispObj*, LispObj*, LispFunType);
+LispObj *LispNewStruct(LispObj*, LispObj*);
+LispObj *LispNewComplex(LispObj*, LispObj*);
+LispObj *LispNewOpaque(void*, int);
+LispObj *LispNewKeyword(char*);
+LispObj *LispNewPathname(LispObj*);
+LispObj *LispNewStringStream(char*, int, long, int);
+LispObj *LispNewFileStream(LispFile*, LispObj*, int);
+LispObj *LispNewPipeStream(LispPipe*, LispObj*, int);
+LispObj *LispNewBignum(mpi*);
+LispObj *LispNewBigratio(mpr*);
+
+LispAtom *LispGetAtom(char*);
+
+/* This function does not allocate a copy of it's argument, but the argument
+ * itself. The argument string should never change. */
+LispAtom *LispGetPermAtom(char*);
+
+void *LispMalloc(size_t);
+void *LispCalloc(size_t, size_t);
+void *LispRealloc(void*, size_t);
+char *LispStrdup(char*);
+void LispFree(void*);
+/* LispMused means memory is now safe from LispDestroy, and should not be
+ * freed in case of an error */
+void LispMused(void*);
+
+void LispGC(LispObj*, LispObj*);
+
+char *LispStrObj(LispObj*);
+
+#ifdef __GNUC__
+#define PRINTF_FORMAT __attribute__ ((format (printf, 1, 2)))
+#else
+#define PRINTF_FORMAT /**/
+#endif
+void LispDestroy(char *fmt, ...) PRINTF_FORMAT;
+ /* continuable error */
+void LispContinuable(char *fmt, ...) PRINTF_FORMAT;
+void LispMessage(char *fmt, ...) PRINTF_FORMAT;
+void LispWarning(char *fmt, ...) PRINTF_FORMAT;
+#undef PRINTF_FORMAT
+
+LispObj *LispSetVariable(LispObj*, LispObj*, char*, int);
+
+int LispRegisterOpaqueType(char*);
+
+int LispPrintString(LispObj*, char*);
+
+void LispProtect(LispObj*, LispObj*);
+void LispUProtect(LispObj*, LispObj*);
+
+/* this function should be called when a module is loaded, and is called
+ * when loading the interpreter */
+void LispAddBuiltinFunction(LispBuiltin*);
+
+/*
+ * Initialization
+ */
+extern LispObj *UNBOUND;
+extern int gcpro;
+
+extern LispObj *Okey, *Orest, *Ooptional, *Oaux, *Olambda;
+extern Atom_id Snil, St, Skey, Srest, Soptional, Saux;
+extern Atom_id Sand, Sor, Snot;
+extern Atom_id Satom, Ssymbol, Sinteger, Scharacter, Sstring, Slist,
+ Scons, Svector, Sarray, Sstruct, Skeyword, Sfunction, Spathname,
+ Srational, Sfloat, Scomplex, Sopaque, Sdefault;
+
+extern LispObj *Ocomplex, *Oformat, *Kunspecific;
+
+extern LispObj *Omake_array, *Kinitial_contents, *Osetf;
+extern Atom_id Svariable, Sstructure, Stype, Ssetf;
+
+extern Atom_id Smake_struct, Sstruct_access, Sstruct_store, Sstruct_type;
+extern LispObj *Omake_struct, *Ostruct_access, *Ostruct_store, *Ostruct_type;
+
+extern LispObj *Oparse_namestring, *Kerror, *Kabsolute, *Krelative, *Oopen,
+ *Oclose, *Kif_does_not_exist;
+
+extern LispObj *Oequal_;
+
+extern LispFile *Stdout, *Stdin, *Stderr;
+
+#endif /* Lisp_internal_h */
diff --git a/lisp/io.c b/lisp/io.c
new file mode 100644
index 0000000..ea59575
--- /dev/null
+++ b/lisp/io.c
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/io.c,v 1.16 2002/12/16 03:59:27 paulo Exp $ */
+
+#include "io.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Match the FILE_XXX flags */
+#define READ_BIT 0x01
+#define WRITE_BIT 0x02
+#define APPEND_BIT 0x04
+#define BUFFERED_BIT 0x08
+#define UNBUFFERED_BIT 0x10
+#define BINARY_BIT 0x20
+
+/*
+ * Prototypes
+ */
+static int calculate_line(void*, int);
+static int calculate_column(void*, int, int);
+
+/*
+ * Initialization
+ */
+extern int pagesize;
+
+/*
+ * Implementation
+ */
+int
+LispGet(void)
+{
+ int ch = EOF;
+ LispUngetInfo *unget = lisp__data.unget[lisp__data.iunget];
+
+ if (unget->offset)
+ ch = ((unsigned char*)unget->buffer)[--unget->offset];
+ else if (SINPUT->data.stream.readable) {
+ LispFile *file = NULL;
+
+ switch (SINPUT->data.stream.type) {
+ case LispStreamStandard:
+ case LispStreamFile:
+ file = FSTREAMP(SINPUT);
+ break;
+ case LispStreamPipe:
+ file = IPSTREAMP(SINPUT);
+ break;
+ case LispStreamString:
+ ch = LispSgetc(SSTREAMP(SINPUT));
+ break;
+ default:
+ ch = EOF;
+ break;
+ }
+ if (file != NULL) {
+ if (file->nonblock) {
+ if (fcntl(file->descriptor, F_SETFL, 0) < 0)
+ LispDestroy("fcntl: %s", strerror(errno));
+ file->nonblock = 0;
+ }
+ ch = LispFgetc(file);
+ }
+ }
+ else
+ LispDestroy("cannot read from *STANDARD-INPUT*");
+
+ if (ch == EOF)
+ lisp__data.eof = 1;
+
+ return (ch);
+}
+
+int
+LispUnget(int ch)
+{
+ LispUngetInfo *unget = lisp__data.unget[lisp__data.iunget];
+
+ if (unget->offset == sizeof(unget->buffer)) {
+ LispWarning("character %c lost at LispUnget()", unget->buffer[0]);
+ memmove(unget->buffer, unget->buffer + 1, unget->offset - 1);
+ unget->buffer[unget->offset - 1] = ch;
+ }
+ else
+ unget->buffer[unget->offset++] = ch;
+
+ return (ch);
+}
+
+void
+LispPushInput(LispObj *stream)
+{
+ if (!STREAMP(stream) || !stream->data.stream.readable)
+ LispDestroy("bad stream at PUSH-INPUT");
+ lisp__data.input_list = CONS(stream, lisp__data.input_list);
+ SINPUT = stream;
+ if (lisp__data.iunget + 1 == lisp__data.nunget) {
+ LispUngetInfo **info =
+ realloc(lisp__data.unget,
+ sizeof(LispUngetInfo) * (lisp__data.nunget + 1));
+
+ if (!info ||
+ (info[lisp__data.nunget] =
+ calloc(1, sizeof(LispUngetInfo))) == NULL)
+ LispDestroy("out of memory");
+ lisp__data.unget = info;
+ ++lisp__data.nunget;
+ }
+ ++lisp__data.iunget;
+ memset(lisp__data.unget[lisp__data.iunget], '\0', sizeof(LispUngetInfo));
+ lisp__data.eof = 0;
+}
+
+void
+LispPopInput(LispObj *stream)
+{
+ if (!CONSP(lisp__data.input_list) || stream != CAR(lisp__data.input_list))
+ LispDestroy("bad stream at POP-INPUT");
+ lisp__data.input_list = CDR(lisp__data.input_list);
+ SINPUT = CONSP(lisp__data.input_list) ?
+ CAR(lisp__data.input_list) : lisp__data.input_list;
+ --lisp__data.iunget;
+ lisp__data.eof = 0;
+}
+
+/*
+ * Low level functions
+ */
+static int
+calculate_line(void *data, int size)
+{
+ int line = 0;
+ char *str, *ptr;
+
+ for (str = (char*)data, ptr = (char*)data + size; str < ptr; str++)
+ if (*ptr == '\n')
+ ++line;
+
+ return (line);
+}
+
+static int
+calculate_column(void *data, int size, int column)
+{
+ char *str, *ptr;
+
+ /* search for newline in data */
+ for (str = (char*)data, ptr = (char*)data + size - 1; ptr >= str; ptr--)
+ if (*ptr == '\n')
+ break;
+
+ /* newline found */
+ if (ptr >= str)
+ return (size - (ptr - str) - 1);
+
+ /* newline not found */
+ return (column + size);
+}
+
+LispFile *
+LispFdopen(int descriptor, int mode)
+{
+ LispFile *file = calloc(1, sizeof(LispFile));
+
+ if (file) {
+ struct stat st;
+
+ file->descriptor = descriptor;
+ file->readable = (mode & READ_BIT) != 0;
+ file->writable = (mode & WRITE_BIT) != 0;
+
+ if (fstat(descriptor, &st) == 0)
+ file->regular = S_ISREG(st.st_mode);
+ else
+ file->regular = 0;
+
+ file->buffered = (mode & BUFFERED_BIT) != 0;
+ if ((mode & UNBUFFERED_BIT) == 0)
+ file->buffered = file->regular;
+
+ if (file->buffered) {
+ file->buffer = malloc(pagesize);
+ if (file->buffer == NULL)
+ file->buffered = 0;
+ }
+ file->line = 1;
+ file->binary = (mode & BINARY_BIT) != 0;
+ file->io_write = write;
+ }
+
+ return (file);
+}
+
+LispFile *
+LispFopen(char *path, int mode)
+{
+ LispFile *file;
+ int descriptor;
+ int flags = O_NOCTTY;
+
+ /* check read/write attributes */
+ if ((mode & (READ_BIT | WRITE_BIT)) == (READ_BIT | WRITE_BIT))
+ flags |= O_RDWR;
+ else if (mode & READ_BIT)
+ flags |= O_RDONLY;
+ else if (mode & WRITE_BIT)
+ flags |= O_WRONLY;
+
+ /* create if does not exist */
+ if (mode & WRITE_BIT) {
+ flags |= O_CREAT;
+
+ /* append if exists? */
+ if (mode & APPEND_BIT)
+ flags |= O_APPEND;
+ else
+ flags |= O_TRUNC;
+ }
+
+ /* open file */
+ descriptor = open(path, flags, 0666);
+ if (descriptor < 0)
+ return (NULL);
+
+ /* initialize LispFile structure */
+ file = LispFdopen(descriptor, mode);
+ if (file == NULL)
+ close(descriptor);
+
+ return (file);
+}
+
+void
+LispFclose(LispFile *file)
+{
+ /* flush any pending output */
+ LispFflush(file);
+ /* cleanup */
+ close(file->descriptor);
+ if (file->buffer)
+ free(file->buffer);
+ free(file);
+}
+
+io_write_fn
+LispSetFileWrite(LispFile *file, io_write_fn new_write)
+{
+ io_write_fn old_write = file->io_write;
+
+ file->io_write = new_write;
+
+ return (old_write);
+}
+
+int
+LispFflush(LispFile *file)
+{
+ if (file->writable && file->length) {
+ int length = (*file->io_write)(file->descriptor,
+ file->buffer, file->length);
+
+ if (length > 0) {
+ if (file->length > length)
+ memmove(file->buffer, file->buffer + length,
+ file->length - length);
+ file->length -= length;
+ }
+ return (length);
+ }
+
+ return (0);
+}
+
+int
+LispFungetc(LispFile *file, int ch)
+{
+ if (file->readable) {
+ file->available = 1;
+ file->unget = ch;
+ /* this should never happen */
+ if (ch == '\n' && !file->binary)
+ --file->line;
+ }
+
+ return (ch);
+}
+
+int
+LispFgetc(LispFile *file)
+{
+ int ch;
+
+ if (file->readable) {
+ unsigned char c;
+
+ if (file->available) {
+ ch = file->unget;
+ file->available = 0;
+ }
+ else if (file->buffered) {
+ if (file->writable) {
+ LispFflush(file);
+ if (read(file->descriptor, &c, 1) == 1)
+ ch = c;
+ else
+ ch = EOF;
+ }
+ else {
+ if (file->offset < file->length)
+ ch = ((unsigned char*)file->buffer)[file->offset++];
+ else {
+ int length = read(file->descriptor,
+ file->buffer, pagesize);
+
+ if (length >= 0)
+ file->length = length;
+ else
+ file->length = 0;
+ file->offset = 0;
+ if (file->length)
+ ch = ((unsigned char*)file->buffer)[file->offset++];
+ else
+ ch = EOF;
+ }
+ }
+ }
+ else if (read(file->descriptor, &c, 1) == 1)
+ ch = c;
+ else
+ ch = EOF;
+ }
+ else
+ ch = EOF;
+
+ if (ch == '\n' && !file->binary)
+ ++file->line;
+
+ return (ch);
+}
+
+int
+LispFputc(LispFile *file, int ch)
+{
+ if (file->writable) {
+ unsigned char c = ch;
+
+ if (file->buffered) {
+ if (file->length + 1 >= pagesize)
+ LispFflush(file);
+ file->buffer[file->length++] = c;
+ }
+ else if ((*file->io_write)(file->descriptor, &c, 1) != 1)
+ ch = EOF;
+
+ if (!file->binary) {
+ /* update column number */
+ if (ch == '\n')
+ file->column = 0;
+ else
+ ++file->column;
+ }
+ }
+
+ return (ch);
+}
+
+int
+LispSgetc(LispString *string)
+{
+ int ch;
+
+ if (string->input >= string->length)
+ return (EOF); /* EOF reading from string */
+
+ ch = ((unsigned char*)string->string)[string->input++];
+ if (ch == '\n' && !string->binary)
+ ++string->line;
+
+ return (ch);
+}
+
+int
+LispSputc(LispString *string, int ch)
+{
+ if (string->output + 1 >= string->space) {
+ if (string->fixed)
+ return (EOF);
+ else {
+ char *tmp = realloc(string->string, string->space + pagesize);
+
+ if (tmp == NULL)
+ return (EOF);
+ string->string = tmp;
+ string->space += pagesize;
+ }
+ }
+
+ string->string[string->output++] = ch;
+ if (string->length < string->output)
+ string->length = string->output;
+
+ /* update column number */
+ if (!string->binary) {
+ if (ch == '\n')
+ string->column = 0;
+ else
+ ++string->column;
+ }
+
+ return (ch);
+}
+
+char *
+LispFgets(LispFile *file, char *string, int size)
+{
+ int ch, offset = 0;
+
+ if (size < 1)
+ return (string);
+
+ for (;;) {
+ if (offset + 1 >= size)
+ break;
+ if ((ch = LispFgetc(file)) == EOF)
+ break;
+ string[offset++] = ch;
+ /* line number is calculated in LispFgetc */
+ if (ch == '\n')
+ break;
+ }
+ string[offset] = '\0';
+
+ return (offset ? string : NULL);
+}
+
+int
+LispFputs(LispFile *file, char *buffer)
+{
+ return (LispFwrite(file, buffer, strlen(buffer)));
+}
+
+int
+LispSputs(LispString *string, char *buffer)
+{
+ return (LispSwrite(string, buffer, strlen(buffer)));
+}
+
+int
+LispFread(LispFile *file, void *data, int size)
+{
+ int bytes, length;
+ char *buffer;
+
+ if (!file->readable)
+ return (EOF);
+
+ if (size <= 0)
+ return (size);
+
+ length = 0;
+ buffer = (char*)data;
+
+ /* check if there is an unget character */
+ if (file->available) {
+ *buffer++ = file->unget;
+ file->available = 0;
+ if (--size == 0) {
+ if (file->unget == '\n' && !file->binary)
+ ++file->line;
+
+ return (1);
+ }
+
+ length = 1;
+ }
+
+ if (file->buffered) {
+ void *base_data = (char*)data - length;
+
+ if (file->writable) {
+ LispFflush(file);
+ bytes = read(file->descriptor, buffer, size);
+ if (bytes < 0)
+ bytes = 0;
+ if (!file->binary)
+ file->line += calculate_line(base_data, length + bytes);
+
+ return (length + bytes);
+ }
+
+ /* read anything that is in the buffer */
+ if (file->offset < file->length) {
+ bytes = file->length - file->offset;
+ if (bytes > size)
+ bytes = size;
+ memcpy(buffer, file->buffer + file->offset, bytes);
+ buffer += bytes;
+ file->offset += bytes;
+ size -= bytes;
+ }
+
+ /* if there is still something to read */
+ if (size) {
+ bytes = read(file->descriptor, buffer, size);
+ if (bytes < 0)
+ bytes = 0;
+
+ length += bytes;
+ }
+
+ if (!file->binary)
+ file->line += calculate_line(base_data, length);
+
+ return (length);
+ }
+
+ bytes = read(file->descriptor, buffer, size);
+ if (bytes < 0)
+ bytes = 0;
+ if (!file->binary)
+ file->line += calculate_line(buffer - length, length + bytes);
+
+ return (length + bytes);
+}
+
+int
+LispFwrite(LispFile *file, void *data, int size)
+{
+ if (!file->writable || size < 0)
+ return (EOF);
+
+ if (!file->binary)
+ file->column = calculate_column(data, size, file->column);
+
+ if (file->buffered) {
+ int length, bytes;
+ char *buffer = (char*)data;
+
+ length = 0;
+ if (size + file->length > pagesize) {
+ /* fill remaining space in buffer and flush */
+ bytes = pagesize - file->length;
+ memcpy(file->buffer + file->length, buffer, bytes);
+ file->length += bytes;
+ LispFflush(file);
+
+ /* check if all data was written */
+ if (file->length)
+ return (pagesize - file->length);
+
+ length = bytes;
+ buffer += bytes;
+ size -= bytes;
+ }
+
+ while (size > pagesize) {
+ /* write multiple of pagesize */
+ bytes = (*file->io_write)(file->descriptor, buffer,
+ size - (size % pagesize));
+ if (bytes <= 0)
+ return (length);
+
+ length += bytes;
+ buffer += bytes;
+ size -= bytes;
+ }
+
+ if (size) {
+ /* keep remaining data in buffer */
+ switch (size) {
+ case 8:
+ file->buffer[file->length++] = *buffer++;
+ case 7:
+ file->buffer[file->length++] = *buffer++;
+ case 6:
+ file->buffer[file->length++] = *buffer++;
+ case 5:
+ file->buffer[file->length++] = *buffer++;
+ case 4:
+ file->buffer[file->length++] = *buffer++;
+ case 3:
+ file->buffer[file->length++] = *buffer++;
+ case 2:
+ file->buffer[file->length++] = *buffer++;
+ case 1:
+ file->buffer[file->length++] = *buffer++;
+ break;
+ default:
+ memcpy(file->buffer + file->length, buffer, size);
+ file->length += size;
+ break;
+ }
+ length += size;
+ }
+
+ return (length);
+ }
+
+ return ((*file->io_write)(file->descriptor, data, size));
+}
+
+int
+LispSwrite(LispString *string, void *data, int size)
+{
+ if (size < 0)
+ return (EOF);
+
+ if (string->output + size >= string->space) {
+ if (string->fixed) {
+ /* leave space for a ending nul character */
+ size = string->space - string->output - 1;
+
+ if (size <= 0)
+ return (-1);
+ }
+ else {
+ char *tmp = realloc(string->string, string->space +
+ (size / pagesize) * pagesize + pagesize);
+
+ if (tmp == NULL)
+ return (-1);
+
+ string->string = tmp;
+ string->space += pagesize;
+ }
+ }
+ memcpy(string->string + string->output, data, size);
+ string->output += size;
+ if (string->length < string->output)
+ string->length = string->output;
+
+ if (!string->binary)
+ string->column = calculate_column(data, size, string->column);
+
+ return (size);
+}
+
+char *
+LispGetSstring(LispString *string, int *length)
+{
+ if (string->string == NULL || string->length <= 0) {
+ *length = 0;
+
+ return ("");
+ }
+ *length = string->length;
+ if (string->string[string->length -1] != '\0') {
+ if (string->length < string->space)
+ string->string[string->length] = '\0';
+ else if (string->fixed && string->space)
+ string->string[string->space - 1] = '\0';
+ else {
+ char *tmp = realloc(string->string, string->space + pagesize);
+
+ if (tmp == NULL)
+ string->string[string->space - 1] = '\0';
+ else {
+ string->string = tmp;
+ string->space += pagesize;
+ string->string[string->length] = '\0';
+ }
+ }
+ }
+
+ return (string->string);
+}
+
+int
+LispRename(char *from, char *to)
+{
+ return (rename(from, to));
+}
+
+int
+LispUnlink(char *name)
+{
+ return (unlink(name));
+}
diff --git a/lisp/io.h b/lisp/io.h
new file mode 100644
index 0000000..eb5edc6
--- /dev/null
+++ b/lisp/io.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/io.h,v 1.8 2002/12/06 03:25:27 paulo Exp $ */
+
+#ifndef Lisp_io_h
+#define Lisp_io_h
+
+#include "private.h"
+
+#define FILE_READ 0x01
+#define FILE_WRITE 0x02
+#define FILE_IO 0x03
+#define FILE_APPEND 0x06 /* append mode, write bit also set */
+#define FILE_BUFFERED 0x08 /* force buffered mode */
+#define FILE_UNBUFFERED 0x10 /* force unbuffered mode */
+#define FILE_BINARY 0x20
+
+/*
+ * Types
+ */
+typedef ssize_t (*io_write_fn)(int, const void*, size_t);
+
+struct _LispFile {
+ char *buffer;
+ int line; /* input line number */
+ int column; /* output column number */
+ int descriptor;
+ int length; /* number of bytes used */
+ int offset; /* read/write offset */
+ int unget : 8; /* unread char */
+ int readable : 1;
+ int writable : 1;
+ int regular : 1; /* regular file */
+ int buffered : 1;
+ int available : 1; /* unget field holds a char */
+ int nonblock : 1; /* in nonblock mode */
+ int binary : 1; /* if set, don't calculate column/line-number */
+ io_write_fn io_write;
+};
+
+struct _LispString {
+ char *string;
+ int line; /* input line number */
+ int column; /* output column number */
+ int space; /* number of bytes alocated */
+ int length; /* number of bytes used */
+ int input; /* input offset, for read operations */
+ int output; /* output offset, for write operations */
+ int fixed : 1; /* if set, don't try to reallocate string */
+ int binary : 1; /* if set, don't calculate column/line-number */
+};
+
+/*
+ * Prototypes
+ */
+ /* higher level functions */
+int LispGet(void);
+int LispUnget(int);
+void LispPushInput(LispObj*);
+void LispPopInput(LispObj*);
+
+ /* functions that read/write using the LispFile structure */
+LispFile *LispFdopen(int, int);
+LispFile *LispFopen(char*, int);
+void LispFclose(LispFile*);
+int LispFflush(LispFile*);
+int LispFungetc(LispFile*, int);
+int LispFgetc(LispFile*);
+int LispFputc(LispFile*, int);
+char *LispFgets(LispFile*, char*, int);
+int LispFputs(LispFile*, char*);
+int LispFread(LispFile*, void*, int);
+int LispFwrite(LispFile*, void*, int);
+int LispRename(char*, char*);
+int LispUnlink(char*);
+
+ /* io wrappers */
+io_write_fn LispSetFileWrite(LispFile*, io_write_fn);
+
+ /* functions that read/write using the LispString structure */
+int LispSgetc(LispString*);
+int LispSputc(LispString*, int);
+int LispSputs(LispString*, char*);
+int LispSwrite(LispString*, void*, int);
+
+char *LispGetSstring(LispString*, int*);
+
+#endif /* Lisp_io_h */
diff --git a/lisp/lisp.c b/lisp/lisp.c
new file mode 100644
index 0000000..cda8c14
--- /dev/null
+++ b/lisp/lisp.c
@@ -0,0 +1,5507 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/lisp.c,v 1.85 2003/01/29 03:05:53 paulo Exp $ */
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef sun
+#include <strings.h>
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#ifndef X_NOT_POSIX
+#include <unistd.h> /* for sysconf(), and getpagesize() */
+#endif
+
+#if defined(linux)
+#include <asm/page.h> /* for PAGE_SIZE */
+#define HAS_GETPAGESIZE
+#define HAS_SC_PAGESIZE /* _SC_PAGESIZE may be an enum for Linux */
+#endif
+
+#if defined(CSRG_BASED)
+#define HAS_GETPAGESIZE
+#endif
+
+#if defined(sun)
+#define HAS_GETPAGESIZE
+#endif
+
+#if defined(QNX4)
+#define HAS_GETPAGESIZE
+#endif
+
+#if defined(__QNXNTO__)
+#define HAS_SC_PAGESIZE
+#endif
+
+#include "bytecode.h"
+
+#include "read.h"
+#include "format.h"
+#include "math.h"
+#include "hash.h"
+#include "package.h"
+#include "pathname.h"
+#include "regex.h"
+#include "require.h"
+#include "stream.h"
+#include "struct.h"
+#include "time.h"
+#include "write.h"
+#include <math.h>
+
+typedef struct {
+ LispObj **objects;
+ LispObj *freeobj;
+ int nsegs;
+ int nobjs;
+ int nfree;
+} LispObjSeg;
+
+/*
+ * Prototypes
+ */
+static void Lisp__GC(LispObj*, LispObj*);
+static LispObj *Lisp__New(LispObj*, LispObj*);
+
+/* run a user function, to be called only by LispEval */
+static LispObj *LispRunFunMac(LispObj*, LispObj*, int, int);
+
+/* expands and executes a setf method, to be called only by Lisp_Setf */
+LispObj *LispRunSetf(LispArgList*, LispObj*, LispObj*, LispObj*);
+LispObj *LispRunSetfMacro(LispAtom*, LispObj*, LispObj*);
+
+/* increases storage size for environment */
+void LispMoreEnvironment(void);
+
+/* increases storage size for stack of builtin arguments */
+void LispMoreStack(void);
+
+/* increases storage size for global variables */
+void LispMoreGlobals(LispPackage*);
+
+#ifdef __GNUC__
+static INLINE LispObj *LispDoGetVar(LispObj*);
+#endif
+static INLINE void LispDoAddVar(LispObj*, LispObj*);
+
+/* Helper for importing symbol(s) functions,
+ * Search for the specified object in the current package */
+static INLINE LispObj *LispGetVarPack(LispObj*);
+
+/* create environment for function call */
+static int LispMakeEnvironment(LispArgList*, LispObj*, LispObj*, int, int);
+
+ /* if not already in keyword package, move atom to keyword package */
+static LispObj *LispCheckKeyword(LispObj*);
+
+ /* builtin backquote parsing */
+static LispObj *LispEvalBackquoteObject(LispObj*, int, int);
+ /* used also by the bytecode compiler */
+LispObj *LispEvalBackquote(LispObj*, int);
+
+ /* create or change object property */
+void LispSetAtomObjectProperty(LispAtom*, LispObj*);
+ /* remove object property */
+static void LispRemAtomObjectProperty(LispAtom*);
+
+ /* allocates a new LispProperty for the given atom */
+static void LispAllocAtomProperty(LispAtom*);
+ /* Increment reference count of atom property */
+static void LispIncrementAtomReference(LispAtom*);
+ /* Decrement reference count of atom property */
+static void LispDecrementAtomReference(LispAtom*);
+ /* Removes all atom properties */
+static void LispRemAtomAllProperties(LispAtom*);
+
+static LispObj *LispAtomPropertyFunction(LispAtom*, LispObj*, int);
+
+static INLINE void LispCheckMemLevel(void);
+
+void LispAllocSeg(LispObjSeg*, int);
+static INLINE void LispMark(LispObj*);
+
+/* functions, macros, setf methods, and structure definitions */
+static INLINE void LispProt(LispObj*);
+
+static LispObj *LispCheckNeedProtect(LispObj*);
+
+static
+#ifdef SIGNALRETURNSINT
+int
+#else
+void
+#endif
+LispSignalHandler(int);
+
+/*
+ * Initialization
+ */
+LispMac lisp__data;
+
+static LispObj lispunbound = {LispNil_t};
+LispObj *UNBOUND = &lispunbound;
+
+static volatile int lisp__disable_int;
+static volatile int lisp__interrupted;
+
+LispObj *Okey, *Orest, *Ooptional, *Oaux, *Olambda;
+
+Atom_id Snil, St;
+Atom_id Saux, Skey, Soptional, Srest;
+Atom_id Satom, Ssymbol, Sinteger, Scharacter, Sstring, Slist,
+ Scons, Svector, Sarray, Sstruct, Skeyword, Sfunction, Spathname,
+ Srational, Sfloat, Scomplex, Sopaque, Sdefault;
+
+LispObj *Oformat, *Kunspecific;
+LispObj *Oexpand_setf_method;
+
+static LispProperty noproperty;
+LispProperty *NOPROPERTY = &noproperty;
+static int segsize, minfree;
+int pagesize, gcpro;
+
+static LispObjSeg objseg = {NULL, NIL};
+static LispObjSeg atomseg = {NULL, NIL};
+
+int LispArgList_t;
+
+LispFile *Stdout, *Stdin, *Stderr;
+
+static LispBuiltin lispbuiltins[] = {
+ {LispFunction, Lisp_Mul, "* &rest numbers"},
+ {LispFunction, Lisp_Plus, "+ &rest numbers"},
+ {LispFunction, Lisp_Minus, "- number &rest more-numbers"},
+ {LispFunction, Lisp_Div, "/ number &rest more-numbers"},
+ {LispFunction, Lisp_OnePlus, "1+ number"},
+ {LispFunction, Lisp_OneMinus, "1- number"},
+ {LispFunction, Lisp_Less, "< number &rest more-numbers"},
+ {LispFunction, Lisp_LessEqual, "<= number &rest more-numbers"},
+ {LispFunction, Lisp_Equal_, "= number &rest more-numbers"},
+ {LispFunction, Lisp_Greater, "> number &rest more-numbers"},
+ {LispFunction, Lisp_GreaterEqual, ">= number &rest more-numbers"},
+ {LispFunction, Lisp_NotEqual, "/= number &rest more-numbers"},
+ {LispFunction, Lisp_Max, "max number &rest more-numbers"},
+ {LispFunction, Lisp_Min, "min number &rest more-numbers"},
+ {LispFunction, Lisp_Abs, "abs number"},
+ {LispFunction, Lisp_Acons, "acons key datum alist"},
+ {LispFunction, Lisp_Adjoin, "adjoin item list &key key test test-not"},
+ {LispFunction, Lisp_AlphaCharP, "alpha-char-p char"},
+ {LispMacro, Lisp_And, "and &rest args", 1, 0, Com_And},
+ {LispFunction, Lisp_Append, "append &rest lists"},
+ {LispFunction, Lisp_Apply, "apply function arg &rest more-args", 1},
+ {LispFunction, Lisp_Aref, "aref array &rest subscripts"},
+ {LispFunction, Lisp_Assoc, "assoc item list &key test test-not key"},
+ {LispFunction, Lisp_AssocIf, "assoc-if predicate list &key key"},
+ {LispFunction, Lisp_AssocIfNot, "assoc-if-not predicate list &key key"},
+ {LispFunction, Lisp_Atom, "atom object"},
+ {LispMacro, Lisp_Block, "block name &rest body", 1, 0, Com_Block},
+ {LispFunction, Lisp_BothCaseP, "both-case-p character"},
+ {LispFunction, Lisp_Boundp, "boundp symbol"},
+ {LispFunction, Lisp_Butlast, "butlast list &optional count"},
+ {LispFunction, Lisp_Nbutlast, "nbutlast list &optional count"},
+ {LispFunction, Lisp_Car, "car list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_Car, "first list", 0, 0, Com_C_r},
+ {LispMacro, Lisp_Case, "case keyform &rest body"},
+ {LispMacro, Lisp_Catch, "catch tag &rest body", 1},
+ {LispFunction, Lisp_Cdr, "cdr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_Cdr, "rest list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_Ceiling, "ceiling number &optional divisor", 1},
+ {LispFunction, Lisp_Fceiling, "fceiling number &optional divisor", 1},
+ {LispFunction, Lisp_Char, "char string index"},
+ {LispFunction, Lisp_Char, "schar simple-string index"},
+ {LispFunction, Lisp_CharLess, "char< character &rest more-characters"},
+ {LispFunction, Lisp_CharLessEqual, "char<= character &rest more-characters"},
+ {LispFunction, Lisp_CharEqual_, "char= character &rest more-characters"},
+ {LispFunction, Lisp_CharGreater, "char> character &rest more-characters"},
+ {LispFunction, Lisp_CharGreaterEqual, "char>= character &rest more-characters"},
+ {LispFunction, Lisp_CharNotEqual_, "char/= character &rest more-characters"},
+ {LispFunction, Lisp_CharLessp, "char-lessp character &rest more-characters"},
+ {LispFunction, Lisp_CharNotGreaterp, "char-not-greaterp character &rest more-characters"},
+ {LispFunction, Lisp_CharEqual, "char-equal character &rest more-characters"},
+ {LispFunction, Lisp_CharGreaterp, "char-greaterp character &rest more-characters"},
+ {LispFunction, Lisp_CharNotLessp, "char-not-lessp character &rest more-characters"},
+ {LispFunction, Lisp_CharNotEqual, "char-not-equal character &rest more-characters"},
+ {LispFunction, Lisp_CharDowncase, "char-downcase character"},
+ {LispFunction, Lisp_CharInt, "char-code character"},
+ {LispFunction, Lisp_CharInt, "char-int character"},
+ {LispFunction, Lisp_CharUpcase, "char-upcase character"},
+ {LispFunction, Lisp_Character, "character object"},
+ {LispFunction, Lisp_Characterp, "characterp object"},
+ {LispFunction, Lisp_Clrhash, "clrhash hash-table"},
+ {LispFunction, Lisp_IntChar, "code-char integer"},
+ {LispFunction, Lisp_Coerce, "coerce object result-type"},
+ {LispFunction, Lisp_Compile, "compile name &optional definition", 1},
+ {LispFunction, Lisp_Complex, "complex realpart &optional imagpart"},
+ {LispMacro, Lisp_Cond, "cond &rest body", 0, 0, Com_Cond},
+ {LispFunction, Lisp_Cons, "cons car cdr", 0, 0, Com_Cons},
+ {LispFunction, Lisp_Consp, "consp object", 0, 0, Com_Consp},
+ {LispFunction, Lisp_Constantp, "constantp form &optional environment"},
+ {LispFunction, Lisp_Conjugate, "conjugate number"},
+ {LispFunction, Lisp_Complexp, "complexp object"},
+ {LispFunction, Lisp_CopyAlist, "copy-alist list"},
+ {LispFunction, Lisp_CopyList, "copy-list list"},
+ {LispFunction, Lisp_CopyTree, "copy-tree list"},
+ {LispFunction, Lisp_Close, "close stream &key abort"},
+ {LispFunction, Lisp_C_r, "caar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cadr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cddr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "caaar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "caadr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cadar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "caddr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdaar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdadr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cddar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdddr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "caaaar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "caaadr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "caadar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "caaddr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cadaar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cadadr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "caddar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cadddr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdaaar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdaadr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdadar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdaddr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cddaar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cddadr list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cdddar list", 0, 0, Com_C_r},
+ {LispFunction, Lisp_C_r, "cddddr list", 0, 0, Com_C_r},
+ {LispMacro, Lisp_Decf, "decf place &optional delta"},
+ {LispMacro, Lisp_Defconstant, "defconstant name initial-value &optional documentation"},
+ {LispMacro, Lisp_Defmacro, "defmacro name lambda-list &rest body"},
+ {LispMacro, Lisp_Defstruct, "defstruct name &rest description"},
+ {LispMacro, Lisp_Defun, "defun name lambda-list &rest body"},
+ {LispMacro, Lisp_Defsetf, "defsetf function lambda-list &rest body"},
+ {LispMacro, Lisp_Defparameter, "defparameter name initial-value &optional documentation"},
+ {LispMacro, Lisp_Defvar, "defvar name &optional initial-value documentation"},
+ {LispFunction, Lisp_Delete, "delete item sequence &key from-end test test-not start end count key"},
+ {LispFunction, Lisp_DeleteDuplicates, "delete-duplicates sequence &key from-end test test-not start end key"},
+ {LispFunction, Lisp_DeleteIf, "delete-if predicate sequence &key from-end start end count key"},
+ {LispFunction, Lisp_DeleteIfNot, "delete-if-not predicate sequence &key from-end start end count key"},
+ {LispFunction, Lisp_DeleteFile, "delete-file filename"},
+ {LispFunction, Lisp_Denominator, "denominator rational"},
+ {LispFunction, Lisp_DigitChar, "digit-char weight &optional radix"},
+ {LispFunction, Lisp_DigitCharP, "digit-char-p character &optional radix"},
+ {LispFunction, Lisp_Directory, "directory pathname &key all if-cannot-read"},
+ {LispFunction, Lisp_DirectoryNamestring, "directory-namestring pathname"},
+ {LispFunction, Lisp_Disassemble, "disassemble function"},
+ {LispMacro, Lisp_Do, "do init test &rest body"},
+ {LispMacro, Lisp_DoP, "do* init test &rest body"},
+ {LispFunction, Lisp_Documentation, "documentation symbol type"},
+ {LispMacro, Lisp_DoList, "dolist init &rest body", 0, 0, Com_Dolist},
+ {LispMacro, Lisp_DoTimes, "dotimes init &rest body"},
+ {LispMacro, Lisp_DoAllSymbols, "do-all-symbols init &rest body"},
+ {LispMacro, Lisp_DoExternalSymbols, "do-external-symbols init &rest body"},
+ {LispMacro, Lisp_DoSymbols, "do-symbols init &rest body"},
+ {LispFunction, Lisp_Elt, "elt sequence index"},
+ {LispFunction, Lisp_Endp, "endp object"},
+ {LispFunction, Lisp_EnoughNamestring, "enough-namestring pathname &optional defaults"},
+ {LispFunction, Lisp_Eq, "eq left right", 0, 0, Com_Eq},
+ {LispFunction, Lisp_Eql, "eql left right", 0, 0, Com_Eq},
+ {LispFunction, Lisp_Equal, "equal left right", 0, 0, Com_Eq},
+ {LispFunction, Lisp_Equalp, "equalp left right", 0, 0, Com_Eq},
+ {LispFunction, Lisp_Error, "error control-string &rest arguments"},
+ {LispFunction, Lisp_Evenp, "evenp integer"},
+ {LispFunction, Lisp_Export, "export symbols &optional package"},
+ {LispFunction, Lisp_Eval, "eval form"},
+ {LispFunction, Lisp_Every, "every predicate sequence &rest more-sequences"},
+ {LispFunction, Lisp_Some, "some predicate sequence &rest more-sequences"},
+ {LispFunction, Lisp_Notevery, "notevery predicate sequence &rest more-sequences"},
+ {LispFunction, Lisp_Notany, "notany predicate sequence &rest more-sequences"},
+ {LispFunction, Lisp_Fboundp, "fboundp symbol"},
+ {LispFunction, Lisp_Find, "find item sequence &key from-end test test-not start end key"},
+ {LispFunction, Lisp_FindIf, "find-if predicate sequence &key from-end start end key"},
+ {LispFunction, Lisp_FindIfNot, "find-if-not predicate sequence &key from-end start end key"},
+ {LispFunction, Lisp_FileNamestring, "file-namestring pathname"},
+ {LispFunction, Lisp_Fill, "fill sequence item &key start end"},
+ {LispFunction, Lisp_FindAllSymbols, "find-all-symbols string-or-symbol"},
+ {LispFunction, Lisp_FindSymbol, "find-symbol string &optional package", 1},
+ {LispFunction, Lisp_FindPackage, "find-package name"},
+ {LispFunction, Lisp_Float, "float number &optional other"},
+ {LispFunction, Lisp_Floatp, "floatp object"},
+ {LispFunction, Lisp_Floor, "floor number &optional divisor", 1},
+ {LispFunction, Lisp_Ffloor, "ffloor number &optional divisor", 1},
+ {LispFunction, Lisp_Fmakunbound, "fmakunbound symbol"},
+ {LispFunction, Lisp_Format, "format destination control-string &rest arguments"},
+ {LispFunction, Lisp_FreshLine, "fresh-line &optional output-stream"},
+ {LispFunction, Lisp_Funcall, "funcall function &rest arguments", 1},
+ {LispFunction, Lisp_Functionp, "functionp object"},
+ {LispFunction, Lisp_Gc, "gc &optional car cdr"},
+ {LispFunction, Lisp_Gcd, "gcd &rest integers"},
+ {LispFunction, Lisp_Gensym, "gensym &optional arg"},
+ {LispFunction, Lisp_Get, "get symbol indicator &optional default"},
+ {LispFunction, Lisp_Gethash, "gethash key hash-table &optional default", 1},
+ {LispMacro, Lisp_Go, "go tag", 0, 0, Com_Go},
+ {LispFunction, Lisp_GraphicCharP, "graphic-char-p char"},
+ {LispFunction, Lisp_HashTableP, "hash-table-p object"},
+ {LispFunction, Lisp_HashTableCount, "hash-table-count hash-table"},
+ {LispFunction, Lisp_HashTableRehashSize, "hash-table-rehash-size hash-table"},
+ {LispFunction, Lisp_HashTableRehashThreshold, "hash-table-rehash-threshold hash-table"},
+ {LispFunction, Lisp_HashTableSize, "hash-table-size hash-table"},
+ {LispFunction, Lisp_HashTableTest, "hash-table-test hash-table"},
+ {LispFunction, Lisp_HostNamestring, "host-namestring pathname"},
+ {LispMacro, Lisp_If, "if test then &optional else", 0, 0, Com_If},
+ {LispMacro, Lisp_IgnoreErrors, "ignore-errors &rest body", 1},
+ {LispFunction, Lisp_Imagpart, "imagpart number"},
+ {LispMacro, Lisp_InPackage, "in-package name"},
+ {LispMacro, Lisp_Incf, "incf place &optional delta"},
+ {LispFunction, Lisp_Import, "import symbols &optional package"},
+ {LispFunction, Lisp_InputStreamP, "input-stream-p stream"},
+ {LispFunction, Lisp_IntChar, "int-char integer"},
+ {LispFunction, Lisp_Integerp, "integerp object"},
+ {LispFunction, Lisp_Intern, "intern string &optional package", 1},
+ {LispFunction, Lisp_Intersection, "intersection list1 list2 &key test test-not key"},
+ {LispFunction, Lisp_Nintersection, "nintersection list1 list2 &key test test-not key"},
+ {LispFunction, Lisp_Isqrt, "isqrt natural"},
+ {LispFunction, Lisp_Keywordp, "keywordp object"},
+ {LispFunction, Lisp_Last, "last list &optional count", 0, 0, Com_Last},
+ {LispMacro, Lisp_Lambda, "lambda lambda-list &rest body"},
+ {LispFunction, Lisp_Lcm, "lcm &rest integers"},
+ {LispFunction, Lisp_Length, "length sequence", 0, 0, Com_Length},
+ {LispMacro, Lisp_Let, "let init &rest body", 1, 0, Com_Let},
+ {LispMacro, Lisp_LetP, "let* init &rest body", 1, 0, Com_Letx},
+ {LispFunction, Lisp_ListP, "list* object &rest more-objects"},
+ {LispFunction, Lisp_ListAllPackages, "list-all-packages"},
+ {LispFunction, Lisp_List, "list &rest args"},
+ {LispFunction, Lisp_ListLength, "list-length list"},
+ {LispFunction, Lisp_Listp, "listp object", 0, 0, Com_Listp},
+ {LispFunction, Lisp_Listen, "listen &optional input-stream"},
+ {LispFunction, Lisp_Load, "load filename &key verbose print if-does-not-exist"},
+ {LispFunction, Lisp_Logand, "logand &rest integers"},
+ {LispFunction, Lisp_Logeqv, "logeqv &rest integers"},
+ {LispFunction, Lisp_Logior, "logior &rest integers"},
+ {LispFunction, Lisp_Lognot, "lognot integer"},
+ {LispFunction, Lisp_Logxor, "logxor &rest integers"},
+ {LispMacro, Lisp_Loop, "loop &rest body", 0, 0, Com_Loop},
+ {LispFunction, Lisp_LowerCaseP, "lower-case-p character"},
+ {LispFunction, Lisp_MakeArray, "make-array dimensions &key element-type initial-element initial-contents adjustable fill-pointer displaced-to displaced-index-offset"},
+ {LispFunction, Lisp_MakeHashTable, "make-hash-table &key test size rehash-size rehash-threshold initial-contents"},
+ {LispFunction, Lisp_MakeList, "make-list size &key initial-element"},
+ {LispFunction, Lisp_MakePackage, "make-package package-name &key nicknames use"},
+ {LispFunction, Lisp_MakePathname, "make-pathname &key host device directory name type version defaults"},
+ {LispFunction, Lisp_MakeString, "make-string size &key initial-element element-type"},
+ {LispFunction, Lisp_MakeSymbol, "make-symbol name"},
+ {LispFunction, Lisp_MakeStringInputStream, "make-string-input-stream string &optional start end"},
+ {LispFunction, Lisp_MakeStringOutputStream, "make-string-output-stream &key element-type"},
+ {LispFunction, Lisp_GetOutputStreamString, "get-output-stream-string string-output-stream"},
+ {LispFunction, Lisp_Makunbound, "makunbound symbol"},
+ {LispFunction, Lisp_Mapc, "mapc function list &rest more-lists"},
+ {LispFunction, Lisp_Mapcar, "mapcar function list &rest more-lists"},
+ {LispFunction, Lisp_Mapcan, "mapcan function list &rest more-lists"},
+ {LispFunction, Lisp_Maphash, "maphash function hash-table"},
+ {LispFunction, Lisp_Mapl, "mapl function list &rest more-lists"},
+ {LispFunction, Lisp_Maplist, "maplist function list &rest more-lists"},
+ {LispFunction, Lisp_Mapcon, "mapcon function list &rest more-lists"},
+ {LispFunction, Lisp_Member, "member item list &key test test-not key"},
+ {LispFunction, Lisp_MemberIf, "member-if predicate list &key key"},
+ {LispFunction, Lisp_MemberIfNot, "member-if-not predicate list &key key"},
+ {LispFunction, Lisp_Minusp, "minusp number"},
+ {LispFunction, Lisp_Mod, "mod number divisor"},
+ {LispMacro, Lisp_MultipleValueBind, "multiple-value-bind symbols values &rest body"},
+ {LispMacro, Lisp_MultipleValueCall, "multiple-value-call function &rest form", 1},
+ {LispMacro, Lisp_MultipleValueProg1, "multiple-value-prog1 first-form &rest form", 1},
+ {LispMacro, Lisp_MultipleValueList, "multiple-value-list form"},
+ {LispMacro, Lisp_MultipleValueSetq, "multiple-value-setq symbols form"},
+ {LispFunction, Lisp_Nconc, "nconc &rest lists"},
+ {LispFunction, Lisp_Nreverse, "nreverse sequence"},
+ {LispFunction, Lisp_NsetDifference, "nset-difference list1 list2 &key test test-not key"},
+ {LispFunction, Lisp_Nsubstitute, "nsubstitute newitem olditem sequence &key from-end test test-not start end count key"},
+ {LispFunction, Lisp_NsubstituteIf, "nsubstitute-if newitem test sequence &key from-end start end count key"},
+ {LispFunction, Lisp_NsubstituteIfNot, "nsubstitute-if-not newitem test sequence &key from-end start end count key"},
+ {LispFunction, Lisp_Nth, "nth index list"},
+ {LispFunction, Lisp_Nthcdr, "nthcdr index list", 0, 0, Com_Nthcdr},
+ {LispMacro, Lisp_NthValue, "nth-value index form"},
+ {LispFunction, Lisp_Numerator, "numerator rational"},
+ {LispFunction, Lisp_Namestring, "namestring pathname"},
+ {LispFunction, Lisp_Null, "not arg", 0, 0, Com_Null},
+ {LispFunction, Lisp_Null, "null list", 0, 0, Com_Null},
+ {LispFunction, Lisp_Numberp, "numberp object", 0, 0, Com_Numberp},
+ {LispFunction, Lisp_Oddp, "oddp integer"},
+ {LispFunction, Lisp_Open, "open filename &key direction element-type if-exists if-does-not-exist external-format"},
+ {LispFunction, Lisp_OpenStreamP, "open-stream-p stream"},
+ {LispMacro, Lisp_Or, "or &rest args", 1, 0, Com_Or},
+ {LispFunction, Lisp_OutputStreamP, "output-stream-p stream"},
+ {LispFunction, Lisp_Packagep, "packagep object"},
+ {LispFunction, Lisp_PackageName, "package-name package"},
+ {LispFunction, Lisp_PackageNicknames, "package-nicknames package"},
+ {LispFunction, Lisp_PackageUseList, "package-use-list package"},
+ {LispFunction, Lisp_PackageUsedByList, "package-used-by-list package"},
+ {LispFunction, Lisp_Pairlis, "pairlis key data &optional alist"},
+ {LispFunction, Lisp_ParseInteger, "parse-integer string &key start end radix junk-allowed", 1},
+ {LispFunction, Lisp_ParseNamestring, "parse-namestring object &optional host defaults &key start end junk-allowed", 1},
+ {LispFunction, Lisp_PathnameHost, "pathname-host pathname"},
+ {LispFunction, Lisp_PathnameDevice, "pathname-device pathname"},
+ {LispFunction, Lisp_PathnameDirectory, "pathname-directory pathname"},
+ {LispFunction, Lisp_PathnameName, "pathname-name pathname"},
+ {LispFunction, Lisp_PathnameType, "pathname-type pathname"},
+ {LispFunction, Lisp_PathnameVersion, "pathname-version pathname"},
+ {LispFunction, Lisp_Pathnamep, "pathnamep object"},
+ {LispFunction, Lisp_Plusp, "plusp number"},
+ {LispMacro, Lisp_Pop, "pop place"},
+ {LispFunction, Lisp_Position, "position item sequence &key from-end test test-not start end key"},
+ {LispFunction, Lisp_PositionIf, "position-if predicate sequence &key from-end start end key"},
+ {LispFunction, Lisp_PositionIfNot, "position-if-not predicate sequence &key from-end start end key"},
+ {LispFunction, Lisp_Prin1, "prin1 object &optional output-stream"},
+ {LispFunction, Lisp_Princ, "princ object &optional output-stream"},
+ {LispFunction, Lisp_Print, "print object &optional output-stream"},
+ {LispFunction, Lisp_ProbeFile, "probe-file pathname"},
+ {LispFunction, Lisp_Proclaim, "proclaim declaration"},
+ {LispMacro, Lisp_Prog1, "prog1 first &rest body"},
+ {LispMacro, Lisp_Prog2, "prog2 first second &rest body"},
+ {LispMacro, Lisp_Progn, "progn &rest body", 1, 0, Com_Progn},
+ {LispMacro, Lisp_Progv, "progv symbols values &rest body", 1},
+ {LispFunction, Lisp_Provide, "provide module"},
+ {LispMacro, Lisp_Push, "push item place"},
+ {LispMacro, Lisp_Pushnew, "pushnew item place &key key test test-not"},
+ {LispFunction, Lisp_Quit, "quit &optional status"},
+ {LispMacro, Lisp_Quote, "quote object"},
+ {LispFunction, Lisp_Rational, "rational number"},
+ {LispFunction, Lisp_Rationalp, "rationalp object"},
+ {LispFunction, Lisp_Read, "read &optional input-stream eof-error-p eof-value recursive-p"},
+ {LispFunction, Lisp_ReadChar, "read-char &optional input-stream eof-error-p eof-value recursive-p"},
+ {LispFunction, Lisp_ReadCharNoHang, "read-char-no-hang &optional input-stream eof-error-p eof-value recursive-p"},
+ {LispFunction, Lisp_ReadLine, "read-line &optional input-stream eof-error-p eof-value recursive-p", 1},
+ {LispFunction, Lisp_Realpart, "realpart number"},
+ {LispFunction, Lisp_Replace, "replace sequence1 sequence2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_ReadFromString, "read-from-string string &optional eof-error-p eof-value &key start end preserve-whitespace", 1},
+ {LispFunction, Lisp_Require, "require module &optional pathname"},
+ {LispFunction, Lisp_Rem, "rem number divisor"},
+ {LispFunction, Lisp_Remhash, "remhash key hash-table"},
+ {LispFunction, Lisp_Remove, "remove item sequence &key from-end test test-not start end count key"},
+ {LispFunction, Lisp_RemoveDuplicates, "remove-duplicates sequence &key from-end test test-not start end key"},
+ {LispFunction, Lisp_RemoveIf, "remove-if predicate sequence &key from-end start end count key"},
+ {LispFunction, Lisp_RemoveIfNot, "remove-if-not predicate sequence &key from-end start end count key"},
+ {LispFunction, Lisp_Remprop, "remprop symbol indicator"},
+ {LispFunction, Lisp_RenameFile, "rename-file filename new-name", 1},
+ {LispMacro, Lisp_Return, "return &optional result", 1, 0, Com_Return},
+ {LispMacro, Lisp_ReturnFrom, "return-from name &optional result", 1, 0, Com_ReturnFrom},
+ {LispFunction, Lisp_Reverse, "reverse sequence"},
+ {LispFunction, Lisp_Round, "round number &optional divisor", 1},
+ {LispFunction, Lisp_Fround, "fround number &optional divisor", 1},
+ {LispFunction, Lisp_Rplaca, "rplaca place value", 0, 0, Com_Rplac_},
+ {LispFunction, Lisp_Rplacd, "rplacd place value", 0, 0, Com_Rplac_},
+ {LispFunction, Lisp_Search, "search sequence1 sequence2 &key from-end test test-not key start1 start2 end1 end2"},
+ {LispFunction, Lisp_Set, "set symbol value"},
+ {LispFunction, Lisp_SetDifference, "set-difference list1 list2 &key test test-not key"},
+ {LispFunction, Lisp_SetExclusiveOr, "set-exclusive-or list1 list2 &key test test-not key"},
+ {LispFunction, Lisp_NsetExclusiveOr, "nset-exclusive-or list1 list2 &key test test-not key"},
+ {LispMacro, Lisp_Setf, "setf &rest form"},
+ {LispMacro, Lisp_Psetf, "psetf &rest form"},
+ {LispMacro, Lisp_SetQ, "setq &rest form", 0, 0, Com_Setq},
+ {LispMacro, Lisp_Psetq, "psetq &rest form"},
+ {LispFunction, Lisp_Sleep, "sleep seconds"},
+ {LispFunction, Lisp_Sort, "sort sequence predicate &key key"},
+ {LispFunction, Lisp_Sqrt, "sqrt number"},
+ {LispFunction, Lisp_Elt, "svref sequence index"},
+ {LispFunction, Lisp_Sort, "stable-sort sequence predicate &key key"},
+ {LispFunction, Lisp_Streamp, "streamp object"},
+ {LispFunction, Lisp_String, "string object"},
+ {LispFunction, Lisp_Stringp, "stringp object"},
+ {LispFunction, Lisp_StringEqual_, "string= string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringLess, "string< string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringGreater, "string> string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringLessEqual, "string<= string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringGreaterEqual, "string>= string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringNotEqual_, "string/= string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringConcat, "string-concat &rest strings"},
+ {LispFunction, Lisp_StringEqual, "string-equal string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringGreaterp, "string-greaterp string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringNotEqual, "string-not-equal string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringNotGreaterp, "string-not-greaterp string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringNotLessp, "string-not-lessp string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringLessp, "string-lessp string1 string2 &key start1 end1 start2 end2"},
+ {LispFunction, Lisp_StringTrim, "string-trim character-bag string"},
+ {LispFunction, Lisp_StringLeftTrim, "string-left-trim character-bag string"},
+ {LispFunction, Lisp_StringRightTrim, "string-right-trim character-bag string"},
+ {LispFunction, Lisp_StringUpcase, "string-upcase string &key start end"},
+ {LispFunction, Lisp_NstringUpcase, "nstring-upcase string &key start end"},
+ {LispFunction, Lisp_StringDowncase, "string-downcase string &key start end"},
+ {LispFunction, Lisp_NstringDowncase, "nstring-downcase string &key start end"},
+ {LispFunction, Lisp_StringCapitalize, "string-capitalize string &key start end"},
+ {LispFunction, Lisp_NstringCapitalize, "nstring-capitalize string &key start end"},
+ {LispFunction, Lisp_Subseq, "subseq sequence start &optional end"},
+ {LispFunction, Lisp_Subsetp, "subsetp list1 list2 &key test test-not key"},
+ {LispFunction, Lisp_Substitute, "substitute newitem olditem sequence &key from-end test test-not start end count key"},
+ {LispFunction, Lisp_SubstituteIf, "substitute-if newitem test sequence &key from-end start end count key"},
+ {LispFunction, Lisp_SubstituteIfNot, "substitute-if-not newitem test sequence &key from-end start end count key"},
+ {LispFunction, Lisp_SymbolFunction, "symbol-function symbol"},
+ {LispFunction, Lisp_SymbolName, "symbol-name symbol"},
+ {LispFunction, Lisp_Symbolp, "symbolp object"},
+ {LispFunction, Lisp_SymbolPlist, "symbol-plist symbol"},
+ {LispFunction, Lisp_SymbolPackage, "symbol-package symbol"},
+ {LispFunction, Lisp_SymbolValue, "symbol-value symbol"},
+ {LispMacro, Lisp_Tagbody, "tagbody &rest body", 0, 0, Com_Tagbody},
+ {LispFunction, Lisp_Terpri, "terpri &optional output-stream"},
+ {LispFunction, Lisp_Typep, "typep object type"},
+ {LispMacro, Lisp_The, "the value-type form"},
+ {LispMacro, Lisp_Throw, "throw tag result", 1},
+ {LispMacro, Lisp_Time, "time form"},
+ {LispFunction, Lisp_Truename, "truename pathname"},
+ {LispFunction, Lisp_TreeEqual, "tree-equal tree-1 tree-2 &key test test-not"},
+ {LispFunction, Lisp_Truncate, "truncate number &optional divisor", 1},
+ {LispFunction, Lisp_Ftruncate, "ftruncate number &optional divisor", 1},
+ {LispFunction, Lisp_Unexport, "unexport symbols &optional package"},
+ {LispFunction, Lisp_Union, "union list1 list2 &key test test-not key"},
+ {LispFunction, Lisp_Nunion, "nunion list1 list2 &key test test-not key"},
+ {LispMacro, Lisp_Unless, "unless test &rest body", 1, 0, Com_Unless},
+ {LispFunction, Lisp_UserHomedirPathname, "user-homedir-pathname &optional host"},
+ {LispMacro, Lisp_UnwindProtect, "unwind-protect protect &rest cleanup"},
+ {LispFunction, Lisp_UpperCaseP, "upper-case-p character"},
+ {LispFunction, Lisp_Values, "values &rest objects", 1},
+ {LispFunction, Lisp_ValuesList, "values-list list", 1},
+ {LispFunction, Lisp_Vector, "vector &rest objects"},
+ {LispMacro, Lisp_When, "when test &rest body", 1, 0, Com_When},
+ {LispFunction, Lisp_Write, " write object &key case circle escape length level lines pretty readably right-margin stream"},
+ {LispFunction, Lisp_WriteChar, "write-char string &optional output-stream"},
+ {LispFunction, Lisp_WriteLine, "write-line string &optional output-stream &key start end"},
+ {LispFunction, Lisp_WriteString, "write-string string &optional output-stream &key start end"},
+ {LispFunction, Lisp_XeditCharStore, "lisp::char-store string index value", 0, 1},
+ {LispFunction, Lisp_XeditEltStore, "lisp::elt-store sequence index value", 0, 1},
+ {LispFunction, Lisp_XeditMakeStruct, "lisp::make-struct atom &rest init", 0, 1},
+ {LispFunction, Lisp_XeditPut, " lisp::put symbol indicator value", 0, 1},
+ {LispFunction, Lisp_XeditPuthash, "lisp::puthash key hash-table value", 0, 1},
+ {LispFunction, Lisp_XeditSetSymbolPlist, "lisp::set-symbol-plist symbol list", 0, 1},
+ {LispFunction, Lisp_XeditStructAccess, "lisp::struct-access atom struct", 0, 1},
+ {LispFunction, Lisp_XeditStructType, "lisp::struct-type atom struct", 0, 1},
+ {LispFunction, Lisp_XeditStructStore, "lisp::struct-store atom struct value", 0, 1},
+ {LispFunction, Lisp_XeditVectorStore, "lisp::vector-store array &rest values", 0, 1},
+ {LispFunction, Lisp_XeditDocumentationStore, "lisp::documentation-store symbol type string", 0, 1},
+ {LispFunction, Lisp_Zerop, "zerop number"},
+};
+
+static LispBuiltin extbuiltins[] = {
+ {LispFunction, Lisp_Getenv, "getenv name"},
+ {LispFunction, Lisp_MakePipe, "make-pipe command-line &key direction element-type external-format"},
+ {LispFunction, Lisp_PipeBroken, "pipe-broken pipe-stream"},
+ {LispFunction, Lisp_PipeErrorStream, "pipe-error-stream pipe-stream"},
+ {LispFunction, Lisp_PipeInputDescriptor, "pipe-input-descriptor pipe-stream"},
+ {LispFunction, Lisp_PipeErrorDescriptor, "pipe-error-descriptor pipe-stream"},
+ {LispFunction, Lisp_Recomp, "re-comp pattern &key nospec icase nosub newline"},
+ {LispFunction, Lisp_Reexec, "re-exec regex string &key count start end notbol noteol"},
+ {LispFunction, Lisp_Rep, "re-p object"},
+ {LispFunction, Lisp_Setenv, "setenv name value &optional overwrite"},
+ {LispFunction, Lisp_Unsetenv, "unsetenv name"},
+ {LispFunction, Lisp_NstringTrim, "nstring-trim character-bag string"},
+ {LispFunction, Lisp_NstringLeftTrim, "nstring-left-trim character-bag string"},
+ {LispFunction, Lisp_NstringRightTrim, "nstring-right-trim character-bag string"},
+ {LispMacro, Lisp_Until, "until test &rest body", 0, 0, Com_Until},
+ {LispMacro, Lisp_While, "while test &rest body", 0, 0, Com_While},
+};
+
+/* byte code function argument list for functions that don't change it's
+ * &REST argument list. */
+extern LispObj x_cons[8];
+
+/*
+ * Implementation
+ */
+static int
+LispGetPageSize(void)
+{
+ static int pagesize = -1;
+
+ if (pagesize != -1)
+ return pagesize;
+
+ /* Try each supported method in the preferred order */
+
+#if defined(_SC_PAGESIZE) || defined(HAS_SC_PAGESIZE)
+ pagesize = sysconf(_SC_PAGESIZE);
+#endif
+
+#ifdef _SC_PAGE_SIZE
+ if (pagesize == -1)
+ pagesize = sysconf(_SC_PAGE_SIZE);
+#endif
+
+#ifdef HAS_GETPAGESIZE
+ if (pagesize == -1)
+ pagesize = getpagesize();
+#endif
+
+#ifdef PAGE_SIZE
+ if (pagesize == -1)
+ pagesize = PAGE_SIZE;
+#endif
+
+ if (pagesize < sizeof(LispObj) * 16)
+ pagesize = sizeof(LispObj) * 16; /* need a reasonable sane size */
+
+ return pagesize;
+}
+
+void
+LispDestroy(char *fmt, ...)
+{
+ static char Error[] = "*** ";
+
+ if (!lisp__data.destroyed) {
+ char string[128];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(string, sizeof(string), fmt, ap);
+ va_end(ap);
+
+ if (!lisp__data.ignore_errors) {
+ if (Stderr->column)
+ LispFputc(Stderr, '\n');
+ LispFputs(Stderr, Error);
+ LispFputs(Stderr, string);
+ LispFputc(Stderr, '\n');
+ LispFflush(Stderr);
+ }
+ else
+ lisp__data.error_condition = STRING(string);
+
+#ifdef DEBUGGER
+ if (lisp__data.debugging) {
+ LispDebugger(LispDebugCallWatch, NIL, NIL);
+ LispDebugger(LispDebugCallFatal, NIL, NIL);
+ }
+#endif
+
+ lisp__data.destroyed = 1;
+ LispBlockUnwind(NULL);
+ if (lisp__data.errexit)
+ exit(1);
+ }
+
+#ifdef DEBUGGER
+ if (lisp__data.debugging) {
+ /* when stack variables could be changed, this must be also changed! */
+ lisp__data.debug_level = -1;
+ lisp__data.debug = LispDebugUnspec;
+ }
+#endif
+
+ while (lisp__data.mem.level) {
+ --lisp__data.mem.level;
+ if (lisp__data.mem.mem[lisp__data.mem.level])
+ free(lisp__data.mem.mem[lisp__data.mem.level]);
+ }
+ lisp__data.mem.index = 0;
+
+ /* If the package was changed and an error happened */
+ PACKAGE = lisp__data.savepackage;
+ lisp__data.pack = lisp__data.savepack;
+
+ LispTopLevel();
+
+ if (!lisp__data.running) {
+ static char Fatal[] = "*** Fatal: nowhere to longjmp.\n";
+
+ LispFputs(Stderr, Fatal);
+ LispFflush(Stderr);
+ abort();
+ }
+
+ siglongjmp(lisp__data.jmp, 1);
+}
+
+void
+LispContinuable(char *fmt, ...)
+{
+ va_list ap;
+ char string[128];
+ static char Error[] = "*** Error: ";
+
+ if (Stderr->column)
+ LispFputc(Stderr, '\n');
+ LispFputs(Stderr, Error);
+ va_start(ap, fmt);
+ vsnprintf(string, sizeof(string), fmt, ap);
+ va_end(ap);
+ LispFputs(Stderr, string);
+ LispFputc(Stderr, '\n');
+ LispFputs(Stderr, "Type 'continue' if you want to proceed: ");
+ LispFflush(Stderr);
+
+ /* NOTE: does not check if stdin is a tty */
+ if (LispFgets(Stdin, string, sizeof(string)) &&
+ strcmp(string, "continue\n") == 0)
+ return;
+
+ LispDestroy("aborted on continuable error");
+}
+
+void
+LispMessage(char *fmt, ...)
+{
+ va_list ap;
+ char string[128];
+
+ if (Stderr->column)
+ LispFputc(Stderr, '\n');
+ va_start(ap, fmt);
+ vsnprintf(string, sizeof(string), fmt, ap);
+ va_end(ap);
+ LispFputs(Stderr, string);
+ LispFputc(Stderr, '\n');
+ LispFflush(Stderr);
+}
+
+void
+LispWarning(char *fmt, ...)
+{
+ va_list ap;
+ char string[128];
+ static char Warning[] = "*** Warning: ";
+
+ if (Stderr->column)
+ LispFputc(Stderr, '\n');
+ LispFputs(Stderr, Warning);
+ va_start(ap, fmt);
+ vsnprintf(string, sizeof(string), fmt, ap);
+ va_end(ap);
+ LispFputs(Stderr, string);
+ LispFputc(Stderr, '\n');
+ LispFflush(Stderr);
+}
+
+void
+LispTopLevel(void)
+{
+ int count;
+
+ COD = NIL;
+#ifdef DEBUGGER
+ if (lisp__data.debugging) {
+ DBG = NIL;
+ if (lisp__data.debug == LispDebugFinish)
+ lisp__data.debug = LispDebugUnspec;
+ lisp__data.debug_level = -1;
+ lisp__data.debug_step = 0;
+ }
+#endif
+ gcpro = 0;
+ lisp__data.block.block_level = 0;
+ if (lisp__data.block.block_size) {
+ while (lisp__data.block.block_size)
+ free(lisp__data.block.block[--lisp__data.block.block_size]);
+ free(lisp__data.block.block);
+ lisp__data.block.block = NULL;
+ }
+
+ lisp__data.destroyed = lisp__data.ignore_errors = 0;
+
+ if (CONSP(lisp__data.input_list)) {
+ LispUngetInfo **info, *unget = lisp__data.unget[0];
+
+ while (CONSP(lisp__data.input_list))
+ lisp__data.input_list = CDR(lisp__data.input_list);
+ SINPUT = lisp__data.input_list;
+ while (lisp__data.nunget > 1)
+ free(lisp__data.unget[--lisp__data.nunget]);
+ if ((info = realloc(lisp__data.unget, sizeof(LispUngetInfo*))) != NULL)
+ lisp__data.unget = info;
+ lisp__data.unget[0] = unget;
+ lisp__data.iunget = 0;
+ lisp__data.eof = 0;
+ }
+
+ for (count = 0; lisp__data.mem.level;) {
+ --lisp__data.mem.level;
+ if (lisp__data.mem.mem[lisp__data.mem.level]) {
+ ++count;
+#if 0
+ printf("LEAK: %p\n", lisp__data.mem.mem[lisp__data.mem.level]);
+#endif
+ }
+ }
+ lisp__data.mem.index = 0;
+ if (count)
+ LispWarning("%d raw memory pointer(s) left. Probably a leak.", count);
+
+ lisp__data.stack.base = lisp__data.stack.length =
+ lisp__data.env.lex = lisp__data.env.length = lisp__data.env.head = 0;
+ RETURN_COUNT = 0;
+ lisp__data.protect.length = 0;
+
+ lisp__data.savepackage = PACKAGE;
+ lisp__data.savepack = lisp__data.pack;
+
+ lisp__disable_int = lisp__interrupted = 0;
+}
+
+void
+LispGC(LispObj *car, LispObj *cdr)
+{
+ Lisp__GC(car, cdr);
+}
+
+static void
+Lisp__GC(LispObj *car, LispObj *cdr)
+{
+ register LispObj *entry, *last, *freeobj, **pentry, **eentry;
+ register int nfree;
+ unsigned i, j;
+ LispAtom *atom;
+ struct timeval start, end;
+#ifdef DEBUG
+ long sec, msec;
+ int count = objseg.nfree;
+#else
+ long msec;
+#endif
+
+ if (gcpro)
+ return;
+
+ DISABLE_INTERRUPTS();
+
+ nfree = 0;
+ freeobj = NIL;
+
+ ++lisp__data.gc.count;
+
+#ifdef DEBUG
+ gettimeofday(&start, NULL);
+#else
+ if (lisp__data.gc.timebits)
+ gettimeofday(&start, NULL);
+#endif
+
+ /* Need to measure timings again to check if it is not better/faster
+ * to just mark these fields as any other data, as the interface was
+ * changed to properly handle circular lists in the function body itself.
+ */
+ if (lisp__data.gc.immutablebits) {
+ for (j = 0; j < objseg.nsegs; j++) {
+ for (entry = objseg.objects[j], last = entry + segsize;
+ entry < last; entry++)
+ entry->prot = 0;
+ }
+ }
+
+ /* Protect all packages */
+ for (entry = PACK; CONSP(entry); entry = CDR(entry)) {
+ LispObj *package = CAR(entry);
+ LispPackage *pack = package->data.package.package;
+
+ /* Protect cons cell */
+ entry->mark = 1;
+
+ /* Protect the package cell */
+ package->mark = 1;
+
+ /* Protect package name */
+ package->data.package.name->mark = 1;
+
+ /* Protect package nicknames */
+ LispMark(package->data.package.nicknames);
+
+ /* Protect global symbols */
+ for (pentry = pack->glb.pairs, eentry = pentry + pack->glb.length;
+ pentry < eentry; pentry++)
+ LispMark((*pentry)->data.atom->property->value);
+
+ /* 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);
+ }
+ }
+ atom = atom->next;
+ }
+ }
+ }
+
+ /* protect environment */
+ for (pentry = lisp__data.env.values,
+ eentry = pentry + lisp__data.env.length;
+ pentry < eentry; pentry++)
+ LispMark(*pentry);
+
+ /* protect multiple return values */
+ for (pentry = lisp__data.returns.values,
+ eentry = pentry + lisp__data.returns.count;
+ pentry < eentry; pentry++)
+ LispMark(*pentry);
+
+ /* protect stack of arguments to builtin functions */
+ for (pentry = lisp__data.stack.values,
+ eentry = pentry + lisp__data.stack.length;
+ pentry < eentry; pentry++)
+ LispMark(*pentry);
+
+ /* protect temporary data used by builtin functions */
+ for (pentry = lisp__data.protect.objects,
+ eentry = pentry + lisp__data.protect.length;
+ pentry < eentry; pentry++)
+ LispMark(*pentry);
+
+ for (i = 0; i < sizeof(x_cons) / sizeof(x_cons[0]); i++)
+ x_cons[i].mark = 0;
+
+ LispMark(COD);
+#ifdef DEBUGGER
+ LispMark(DBG);
+ LispMark(BRK);
+#endif
+ LispMark(PRO);
+ LispMark(lisp__data.input_list);
+ LispMark(lisp__data.output_list);
+ LispMark(car);
+ LispMark(cdr);
+
+ for (j = 0; j < objseg.nsegs; j++) {
+ for (entry = objseg.objects[j], last = entry + segsize;
+ entry < last; entry++) {
+ if (entry->prot)
+ continue;
+ else if (entry->mark)
+ entry->mark = 0;
+ else {
+ switch (XOBJECT_TYPE(entry)) {
+ case LispString_t:
+ free(THESTR(entry));
+ entry->type = LispCons_t;
+ break;
+ case LispStream_t:
+ switch (entry->data.stream.type) {
+ case LispStreamString:
+ free(SSTREAMP(entry)->string);
+ free(SSTREAMP(entry));
+ break;
+ case LispStreamFile:
+ if (FSTREAMP(entry))
+ LispFclose(FSTREAMP(entry));
+ break;
+ case LispStreamPipe:
+ /* XXX may need special handling if child hangs */
+ if (PSTREAMP(entry)) {
+ if (IPSTREAMP(entry))
+ LispFclose(IPSTREAMP(entry));
+ if (OPSTREAMP(entry))
+ LispFclose(OPSTREAMP(entry));
+ /* don't bother with error stream, will also
+ * freed in this GC call, maybe just out
+ * of order */
+ if (PIDPSTREAMP(entry) > 0) {
+ kill(PIDPSTREAMP(entry), SIGTERM);
+ waitpid(PIDPSTREAMP(entry), NULL, 0);
+ }
+ free(PSTREAMP(entry));
+ }
+ break;
+ default:
+ break;
+ }
+ entry->type = LispCons_t;
+ break;
+ case LispBignum_t:
+ mpi_clear(entry->data.mp.integer);
+ free(entry->data.mp.integer);
+ entry->type = LispCons_t;
+ break;
+ case LispBigratio_t:
+ mpr_clear(entry->data.mp.ratio);
+ free(entry->data.mp.ratio);
+ entry->type = LispCons_t;
+ break;
+ case LispLambda_t:
+ if (!SYMBOLP(entry->data.lambda.name))
+ LispFreeArgList((LispArgList*)
+ entry->data.lambda.name->data.opaque.data);
+ entry->type = LispCons_t;
+ break;
+ case LispRegex_t:
+ refree(entry->data.regex.regex);
+ free(entry->data.regex.regex);
+ entry->type = LispCons_t;
+ break;
+ case LispBytecode_t:
+ free(entry->data.bytecode.bytecode->code);
+ free(entry->data.bytecode.bytecode);
+ entry->type = LispCons_t;
+ break;
+ case LispHashTable_t:
+ LispFreeHashTable(entry->data.hash.table);
+ entry->type = LispCons_t;
+ break;
+ case LispCons_t:
+ break;
+ default:
+ entry->type = LispCons_t;
+ break;
+ }
+ CDR(entry) = freeobj;
+ freeobj = entry;
+ ++nfree;
+ }
+ }
+ }
+
+ objseg.nfree = nfree;
+ objseg.freeobj = freeobj;
+
+ lisp__data.gc.immutablebits = 0;
+
+#ifdef DEBUG
+ gettimeofday(&end, NULL);
+ sec = end.tv_sec - start.tv_sec;
+ msec = end.tv_usec - start.tv_usec;
+ if (msec < 0) {
+ --sec;
+ msec += 1000000;
+ }
+ LispMessage("gc: "
+ "%ld sec, %ld msec, "
+ "%d recovered, %d free, %d protected, %d total",
+ sec, msec,
+ objseg.nfree - count, objseg.nfree,
+ objseg.nobjs - objseg.nfree, objseg.nobjs);
+#else
+ if (lisp__data.gc.timebits) {
+ gettimeofday(&end, NULL);
+ if ((msec = end.tv_usec - start.tv_usec) < 0)
+ msec += 1000000;
+ lisp__data.gc.gctime += msec;
+ }
+#endif
+
+ ENABLE_INTERRUPTS();
+}
+
+static INLINE void
+LispCheckMemLevel(void)
+{
+ int i;
+
+ /* Check for a free slot before the end. */
+ for (i = lisp__data.mem.index; i < lisp__data.mem.level; i++)
+ if (lisp__data.mem.mem[i] == NULL) {
+ lisp__data.mem.index = i;
+ return;
+ }
+
+ /* Check for a free slot in the beginning */
+ for (i = 0; i < lisp__data.mem.index; i++)
+ if (lisp__data.mem.mem[i] == NULL) {
+ lisp__data.mem.index = i;
+ return;
+ }
+
+ lisp__data.mem.index = lisp__data.mem.level;
+ ++lisp__data.mem.level;
+ if (lisp__data.mem.index < lisp__data.mem.space)
+ /* There is free space to store pointer. */
+ return;
+ else {
+ void **ptr = (void**)realloc(lisp__data.mem.mem,
+ (lisp__data.mem.space + 16) *
+ sizeof(void*));
+
+ if (ptr == NULL)
+ LispDestroy("out of memory");
+ lisp__data.mem.mem = ptr;
+ lisp__data.mem.space += 16;
+ }
+}
+
+void
+LispMused(void *pointer)
+{
+ int i;
+
+ DISABLE_INTERRUPTS();
+ for (i = lisp__data.mem.index; i >= 0; i--)
+ if (lisp__data.mem.mem[i] == pointer) {
+ lisp__data.mem.mem[i] = NULL;
+ lisp__data.mem.index = i;
+ goto mused_done;
+ }
+
+ for (i = lisp__data.mem.level - 1; i > lisp__data.mem.index; i--)
+ if (lisp__data.mem.mem[i] == pointer) {
+ lisp__data.mem.mem[i] = NULL;
+ lisp__data.mem.index = i;
+ break;
+ }
+
+mused_done:
+ ENABLE_INTERRUPTS();
+}
+
+void *
+LispMalloc(size_t size)
+{
+ void *pointer;
+
+ DISABLE_INTERRUPTS();
+ LispCheckMemLevel();
+ if ((pointer = malloc(size)) == NULL)
+ LispDestroy("out of memory, couldn't allocate %lu bytes",
+ (unsigned long)size);
+
+ lisp__data.mem.mem[lisp__data.mem.index] = pointer;
+ ENABLE_INTERRUPTS();
+
+ return (pointer);
+}
+
+void *
+LispCalloc(size_t nmemb, size_t size)
+{
+ void *pointer;
+
+ DISABLE_INTERRUPTS();
+ LispCheckMemLevel();
+ if ((pointer = calloc(nmemb, size)) == NULL)
+ LispDestroy("out of memory, couldn't allocate %lu bytes",
+ (unsigned long)size);
+
+ lisp__data.mem.mem[lisp__data.mem.index] = pointer;
+ ENABLE_INTERRUPTS();
+
+ return (pointer);
+}
+
+void *
+LispRealloc(void *pointer, size_t size)
+{
+ void *ptr;
+ int i;
+
+ DISABLE_INTERRUPTS();
+ if (pointer != NULL) {
+ for (i = lisp__data.mem.index; i >= 0; i--)
+ if (lisp__data.mem.mem[i] == pointer)
+ goto index_found;
+
+ for (i = lisp__data.mem.index + 1; i < lisp__data.mem.level; i++)
+ if (lisp__data.mem.mem[i] == pointer)
+ goto index_found;
+
+ }
+ LispCheckMemLevel();
+ i = lisp__data.mem.index;
+
+index_found:
+ if ((ptr = realloc(pointer, size)) == NULL)
+ LispDestroy("out of memory, couldn't realloc");
+
+ lisp__data.mem.mem[i] = ptr;
+ ENABLE_INTERRUPTS();
+
+ return (ptr);
+}
+
+char *
+LispStrdup(char *str)
+{
+ char *ptr = LispMalloc(strlen(str) + 1);
+
+ strcpy(ptr, str);
+
+ return (ptr);
+}
+
+void
+LispFree(void *pointer)
+{
+ int i;
+
+ DISABLE_INTERRUPTS();
+ for (i = lisp__data.mem.index; i >= 0; i--)
+ if (lisp__data.mem.mem[i] == pointer) {
+ lisp__data.mem.mem[i] = NULL;
+ lisp__data.mem.index = i;
+ goto free_done;
+ }
+
+ for (i = lisp__data.mem.level - 1; i > lisp__data.mem.index; i--)
+ if (lisp__data.mem.mem[i] == pointer) {
+ lisp__data.mem.mem[i] = NULL;
+ lisp__data.mem.index = i;
+ break;
+ }
+
+free_done:
+ free(pointer);
+ ENABLE_INTERRUPTS();
+}
+
+LispObj *
+LispSetVariable(LispObj *var, LispObj *val, char *fname, int eval)
+{
+ if (!SYMBOLP(var))
+ LispDestroy("%s: %s is not a symbol", fname, STROBJ(var));
+ if (eval)
+ val = EVAL(val);
+
+ return (LispSetVar(var, val));
+}
+
+int
+LispRegisterOpaqueType(char *desc)
+{
+ 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);
+
+ return (opaque->type = ++lisp__data.opaque);
+}
+
+char *
+LispIntToOpaqueType(int type)
+{
+ int i;
+ 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;
+ }
+ }
+ LispDestroy("Opaque type %d not registered", type);
+ }
+
+ return (Snil);
+}
+
+int
+LispDoHashString(char *string)
+{
+ 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;
+
+ return (entry->string);
+}
+
+LispAtom *
+LispDoGetAtom(char *str, int perm)
+{
+ 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);
+
+ 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;
+
+ return (atom);
+}
+
+static void
+LispAllocAtomProperty(LispAtom *atom)
+{
+ LispProperty *property;
+
+ if (atom->property != NOPROPERTY)
+ LispDestroy("internal error at ALLOC-ATOM-PROPERTY");
+
+ property = LispCalloc(1, sizeof(LispProperty));
+ LispMused(property);
+ atom->property = property;
+ property->package = lisp__data.pack;
+ if (atom->package == NULL)
+ atom->package = PACKAGE;
+
+ LispIncrementAtomReference(atom);
+}
+
+static void
+LispIncrementAtomReference(LispAtom *atom)
+{
+ if (atom->property != NOPROPERTY)
+ /* if atom->property is NOPROPERTY, this is an unbound symbol */
+ ++atom->property->refcount;
+}
+
+/* Assumes atom property is not NOPROPERTY */
+static void
+LispDecrementAtomReference(LispAtom *atom)
+{
+ if (atom->property == NOPROPERTY)
+ /* if atom->property is NOPROPERTY, this is an unbound symbol */
+ return;
+
+ --atom->property->refcount;
+
+ if (atom->property->refcount < 0)
+ LispDestroy("internal error at DECREMENT-ATOM-REFERENCE");
+
+ if (atom->property->refcount == 0) {
+ LispRemAtomAllProperties(atom);
+ free(atom->property);
+ atom->property = NOPROPERTY;
+ }
+}
+
+static void
+LispRemAtomAllProperties(LispAtom *atom)
+{
+ if (atom->property != NOPROPERTY) {
+ if (atom->a_object)
+ LispRemAtomObjectProperty(atom);
+ if (atom->a_function) {
+ lisp__data.gc.immutablebits = 1;
+ LispRemAtomFunctionProperty(atom);
+ }
+ else if (atom->a_compiled) {
+ lisp__data.gc.immutablebits = 1;
+ LispRemAtomCompiledProperty(atom);
+ }
+ else if (atom->a_builtin) {
+ lisp__data.gc.immutablebits = 1;
+ LispRemAtomBuiltinProperty(atom);
+ }
+ if (atom->a_defsetf) {
+ lisp__data.gc.immutablebits = 1;
+ LispRemAtomSetfProperty(atom);
+ }
+ if (atom->a_defstruct) {
+ lisp__data.gc.immutablebits = 1;
+ LispRemAtomStructProperty(atom);
+ }
+ }
+}
+
+void
+LispSetAtomObjectProperty(LispAtom *atom, LispObj *object)
+{
+ if (atom->property == NOPROPERTY)
+ LispAllocAtomProperty(atom);
+ else if (atom->watch) {
+ if (atom->object == lisp__data.package) {
+ if (!PACKAGEP(object))
+ LispDestroy("Symbol %s must be a package, not %s",
+ ATOMID(lisp__data.package), STROBJ(object));
+ lisp__data.pack = object->data.package.package;
+ }
+ }
+
+ atom->a_object = 1;
+ SETVALUE(atom, object);
+}
+
+static void
+LispRemAtomObjectProperty(LispAtom *atom)
+{
+ if (atom->a_object) {
+ atom->a_object = 0;
+ atom->property->value = NULL;
+ }
+}
+
+void
+LispSetAtomCompiledProperty(LispAtom *atom, LispObj *bytecode)
+{
+ if (atom->property == NOPROPERTY)
+ LispAllocAtomProperty(atom);
+
+ lisp__data.gc.immutablebits = 1;
+ if (atom->a_builtin) {
+ atom->a_builtin = 0;
+ LispFreeArgList(atom->property->alist);
+ }
+ else
+ atom->a_function = 0;
+ atom->a_compiled = 1;
+ atom->property->fun.function = bytecode;
+}
+
+void
+LispRemAtomCompiledProperty(LispAtom *atom)
+{
+ if (atom->a_compiled) {
+ lisp__data.gc.immutablebits = 1;
+ atom->property->fun.function = NULL;
+ atom->a_compiled = 0;
+ LispFreeArgList(atom->property->alist);
+ atom->property->alist = NULL;
+ }
+}
+
+void
+LispSetAtomFunctionProperty(LispAtom *atom, LispObj *function,
+ LispArgList *alist)
+{
+ if (atom->property == NOPROPERTY)
+ LispAllocAtomProperty(atom);
+
+ lisp__data.gc.immutablebits = 1;
+ if (atom->a_function == 0 && atom->a_builtin == 0 && atom->a_compiled == 0)
+ atom->a_function = 1;
+ else {
+ if (atom->a_builtin) {
+ atom->a_builtin = 0;
+ LispFreeArgList(atom->property->alist);
+ }
+ else
+ atom->a_compiled = 0;
+ atom->a_function = 1;
+ }
+
+ atom->property->fun.function = function;
+ atom->property->alist = alist;
+}
+
+void
+LispRemAtomFunctionProperty(LispAtom *atom)
+{
+ if (atom->a_function) {
+ lisp__data.gc.immutablebits = 1;
+ atom->property->fun.function = NULL;
+ atom->a_function = 0;
+ LispFreeArgList(atom->property->alist);
+ atom->property->alist = NULL;
+ }
+}
+
+void
+LispSetAtomBuiltinProperty(LispAtom *atom, LispBuiltin *builtin,
+ LispArgList *alist)
+{
+ if (atom->property == NOPROPERTY)
+ LispAllocAtomProperty(atom);
+
+ lisp__data.gc.immutablebits = 1;
+ if (atom->a_builtin == 0 && atom->a_function == 0)
+ atom->a_builtin = 1;
+ else {
+ if (atom->a_function) {
+ atom->a_function = 0;
+ LispFreeArgList(atom->property->alist);
+ }
+ }
+
+ atom->property->fun.builtin = builtin;
+ atom->property->alist = alist;
+}
+
+void
+LispRemAtomBuiltinProperty(LispAtom *atom)
+{
+ if (atom->a_builtin) {
+ lisp__data.gc.immutablebits = 1;
+ atom->property->fun.function = NULL;
+ atom->a_builtin = 0;
+ LispFreeArgList(atom->property->alist);
+ atom->property->alist = NULL;
+ }
+}
+
+void
+LispSetAtomSetfProperty(LispAtom *atom, LispObj *setf, LispArgList *alist)
+{
+ if (atom->property == NOPROPERTY)
+ LispAllocAtomProperty(atom);
+
+ lisp__data.gc.immutablebits = 1;
+ if (atom->a_defsetf)
+ LispFreeArgList(atom->property->salist);
+
+ atom->a_defsetf = 1;
+ atom->property->setf = setf;
+ atom->property->salist = alist;
+}
+
+void
+LispRemAtomSetfProperty(LispAtom *atom)
+{
+ if (atom->a_defsetf) {
+ lisp__data.gc.immutablebits = 1;
+ atom->property->setf = NULL;
+ atom->a_defsetf = 0;
+ LispFreeArgList(atom->property->salist);
+ atom->property->salist = NULL;
+ }
+}
+
+void
+LispSetAtomStructProperty(LispAtom *atom, LispObj *def, int fun)
+{
+ if (fun > 0xff)
+ /* Not suported by the bytecode compiler... */
+ LispDestroy("SET-ATOM-STRUCT-PROPERTY: "
+ "more than 256 fields not supported");
+
+ if (atom->property == NOPROPERTY)
+ LispAllocAtomProperty(atom);
+
+ lisp__data.gc.immutablebits = 1;
+ atom->a_defstruct = 1;
+ atom->property->structure.definition = def;
+ atom->property->structure.function = fun;
+}
+
+void
+LispRemAtomStructProperty(LispAtom *atom)
+{
+ if (atom->a_defstruct) {
+ lisp__data.gc.immutablebits = 1;
+ atom->property->structure.definition = NULL;
+ atom->a_defstruct = 0;
+ }
+}
+
+LispAtom *
+LispGetAtom(char *str)
+{
+ return (LispDoGetAtom(str, 0));
+}
+
+LispAtom *
+LispGetPermAtom(char *str)
+{
+ return (LispDoGetAtom(str, 1));
+}
+
+#define GET_PROPERTY 0
+#define ADD_PROPERTY 1
+#define REM_PROPERTY 2
+static LispObj *
+LispAtomPropertyFunction(LispAtom *atom, LispObj *key, int function)
+{
+ LispObj *list = NIL, *result = NIL;
+
+ if (function == ADD_PROPERTY) {
+ if (atom->property == NOPROPERTY)
+ LispAllocAtomProperty(atom);
+ if (atom->property->properties == NULL) {
+ atom->a_property = 1;
+ atom->property->properties = NIL;
+ }
+ }
+
+ if (atom->a_property) {
+ LispObj *base;
+
+ for (base = list = atom->property->properties;
+ CONSP(list);
+ list = CDR(list)) {
+ if (key == CAR(list)) {
+ result = CDR(list);
+ break;
+ }
+ base = list;
+ list = CDR(list);
+ if (!CONSP(list))
+ LispDestroy("%s: %s has an odd property list length",
+ STROBJ(atom->object),
+ function == REM_PROPERTY ? "REMPROP" : "GET");
+ }
+ if (CONSP(list) && function == REM_PROPERTY) {
+ if (!CONSP(CDR(list)))
+ LispDestroy("REMPROP: %s has an odd property list length",
+ STROBJ(atom->object));
+ if (base == list)
+ atom->property->properties = CDDR(list);
+ else
+ RPLACD(CDR(base), CDDR(list));
+ }
+ }
+
+ if (!CONSP(list)) {
+ if (function == ADD_PROPERTY) {
+ atom->property->properties =
+ CONS(key, CONS(NIL, atom->property->properties));
+ result = CDR(atom->property->properties);
+ }
+ }
+ else if (function == REM_PROPERTY)
+ result = T;
+
+ return (result);
+}
+
+LispObj *
+LispGetAtomProperty(LispAtom *atom, LispObj *key)
+{
+ return (LispAtomPropertyFunction(atom, key, GET_PROPERTY));
+}
+
+LispObj *
+LispPutAtomProperty(LispAtom *atom, LispObj *key, LispObj *value)
+{
+ LispObj *result = LispAtomPropertyFunction(atom, key, ADD_PROPERTY);
+
+ RPLACA(result, value);
+
+ return (result);
+}
+
+LispObj *
+LispRemAtomProperty(LispAtom *atom, LispObj *key)
+{
+ return (LispAtomPropertyFunction(atom, key, REM_PROPERTY));
+}
+
+LispObj *
+LispReplaceAtomPropertyList(LispAtom *atom, LispObj *list)
+{
+ if (atom->property == NOPROPERTY)
+ LispAllocAtomProperty(atom);
+ if (atom->property->properties == NULL)
+ atom->a_property = 1;
+ atom->property->properties = list;
+
+ return (list);
+}
+#undef GET_PROPERTY
+#undef ADD_PROPERTY
+#undef REM_PROPERTY
+
+
+/* Used to make sure that when defining a function like:
+ * (defun my-function (... &key key1 key2 key3 ...)
+ * key1, key2, and key3 will be in the keyword package
+ */
+static LispObj *
+LispCheckKeyword(LispObj *keyword)
+{
+ if (KEYWORDP(keyword))
+ return (keyword);
+
+ return (KEYWORD(ATOMID(keyword)));
+}
+
+void
+LispUseArgList(LispArgList *alist)
+{
+ if (alist->normals.num_symbols)
+ LispMused(alist->normals.symbols);
+ if (alist->optionals.num_symbols) {
+ LispMused(alist->optionals.symbols);
+ LispMused(alist->optionals.defaults);
+ LispMused(alist->optionals.sforms);
+ }
+ if (alist->keys.num_symbols) {
+ LispMused(alist->keys.symbols);
+ LispMused(alist->keys.defaults);
+ LispMused(alist->keys.sforms);
+ LispMused(alist->keys.keys);
+ }
+ if (alist->auxs.num_symbols) {
+ LispMused(alist->auxs.symbols);
+ LispMused(alist->auxs.initials);
+ }
+ LispMused(alist);
+}
+
+void
+LispFreeArgList(LispArgList *alist)
+{
+ if (alist->normals.num_symbols)
+ LispFree(alist->normals.symbols);
+ if (alist->optionals.num_symbols) {
+ LispFree(alist->optionals.symbols);
+ LispFree(alist->optionals.defaults);
+ LispFree(alist->optionals.sforms);
+ }
+ if (alist->keys.num_symbols) {
+ LispFree(alist->keys.symbols);
+ LispFree(alist->keys.defaults);
+ LispFree(alist->keys.sforms);
+ LispFree(alist->keys.keys);
+ }
+ if (alist->auxs.num_symbols) {
+ LispFree(alist->auxs.symbols);
+ LispFree(alist->auxs.initials);
+ }
+ LispFree(alist);
+}
+
+static LispObj *
+LispCheckNeedProtect(LispObj *object)
+{
+ if (object) {
+ switch (OBJECT_TYPE(object)) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFunction_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ return (NULL);
+ default:
+ return (object);
+ }
+ }
+ return (NULL);
+}
+
+LispObj *
+LispListProtectedArguments(LispArgList *alist)
+{
+ int i;
+ GC_ENTER();
+ LispObj *arguments, *cons, *obj, *prev;
+
+ arguments = cons = prev = NIL;
+ for (i = 0; i < alist->optionals.num_symbols; i++) {
+ if ((obj = LispCheckNeedProtect(alist->optionals.defaults[i])) != NULL) {
+ if (arguments == NIL) {
+ arguments = cons = prev = CONS(obj, NIL);
+ GC_PROTECT(arguments);
+ }
+ else {
+ RPLACD(cons, CONS(obj, NIL));
+ prev = cons;
+ cons = CDR(cons);
+ }
+ }
+ }
+ for (i = 0; i < alist->keys.num_symbols; i++) {
+ if ((obj = LispCheckNeedProtect(alist->keys.defaults[i])) != NULL) {
+ if (arguments == NIL) {
+ arguments = cons = prev = CONS(obj, NIL);
+ GC_PROTECT(arguments);
+ }
+ else {
+ RPLACD(cons, CONS(obj, NIL));
+ prev = cons;
+ cons = CDR(cons);
+ }
+ }
+ }
+ for (i = 0; i < alist->auxs.num_symbols; i++) {
+ if ((obj = LispCheckNeedProtect(alist->auxs.initials[i])) != NULL) {
+ if (arguments == NIL) {
+ arguments = cons = prev = CONS(obj, NIL);
+ GC_PROTECT(arguments);
+ }
+ else {
+ RPLACD(cons, CONS(obj, NIL));
+ prev = cons;
+ cons = CDR(cons);
+ }
+ }
+ }
+ GC_LEAVE();
+
+ /* Don't add a NIL cell at the end, to save some space */
+ if (arguments != NIL) {
+ if (arguments == cons)
+ arguments = CAR(cons);
+ else
+ CDR(prev) = CAR(cons);
+ }
+
+ return (arguments);
+}
+
+LispArgList *
+LispCheckArguments(LispFunType type, LispObj *list, char *name, int builtin)
+{
+ static char *types[4] = {"LAMBDA-LIST", "FUNCTION", "MACRO", "SETF-METHOD"};
+ static char *fnames[4] = {"LAMBDA", "DEFUN", "DEFMACRO", "DEFSETF"};
+#define IKEY 0
+#define IOPTIONAL 1
+#define IREST 2
+#define IAUX 3
+ static char *keys[4] = {"&KEY", "&OPTIONAL", "&REST", "&AUX"};
+ int rest, optional, key, aux, count;
+ LispArgList *alist;
+ LispObj *spec, *sform, *defval, *default_value;
+ char description[8], *desc;
+
+/* If LispRealloc fails, the previous memory will be released
+ * in LispTopLevel, unless LispMused was called on the pointer */
+#define REALLOC_OBJECTS(pointer, count) \
+ pointer = LispRealloc(pointer, (count) * sizeof(LispObj*))
+
+ alist = LispCalloc(1, sizeof(LispArgList));
+ if (!CONSP(list)) {
+ if (list != NIL)
+ LispDestroy("%s %s: %s cannot be a %s argument list",
+ fnames[type], name, STROBJ(list), types[type]);
+ alist->description = GETATOMID("");
+
+ return (alist);
+ }
+
+ default_value = builtin ? UNSPEC : NIL;
+
+ description[0] = '\0';
+ desc = description;
+ rest = optional = key = aux = 0;
+ for (; CONSP(list); list = CDR(list)) {
+ spec = CAR(list);
+
+ if (CONSP(spec)) {
+ if (builtin)
+ LispDestroy("builtin function argument cannot have default value");
+ if (aux) {
+ if (!SYMBOLP(CAR(spec)) ||
+ (CDR(spec) != NIL && CDDR(spec) != NIL))
+ LispDestroy("%s %s: bad &AUX argument %s",
+ fnames[type], name, STROBJ(spec));
+ defval = CDR(spec) != NIL ? CADR(spec) : NIL;
+ count = alist->auxs.num_symbols;
+ REALLOC_OBJECTS(alist->auxs.symbols, count + 1);
+ REALLOC_OBJECTS(alist->auxs.initials, count + 1);
+ alist->auxs.symbols[count] = CAR(spec);
+ alist->auxs.initials[count] = defval;
+ ++alist->auxs.num_symbols;
+ if (count == 0)
+ *desc++ = 'a';
+ ++alist->num_arguments;
+ }
+ else if (rest)
+ LispDestroy("%s %s: syntax error parsing %s",
+ fnames[type], name, keys[IREST]);
+ else if (key) {
+ LispObj *akey = CAR(spec);
+
+ defval = default_value;
+ sform = NULL;
+ if (CONSP(akey)) {
+ /* check for special case, as in:
+ * (defun a (&key ((key name) 'default-value)) name)
+ * (a 'key 'test) => TEST
+ * (a) => DEFAULT-VALUE
+ */
+ if (!SYMBOLP(CAR(akey)) || !CONSP(CDR(akey)) ||
+ !SYMBOLP(CADR(akey)) || CDDR(akey) != NIL ||
+ (CDR(spec) != NIL && CDDR(spec) != NIL))
+ LispDestroy("%s %s: bad special &KEY %s",
+ fnames[type], name, STROBJ(spec));
+ if (CDR(spec) != NIL)
+ defval = CADR(spec);
+ spec = CADR(akey);
+ akey = CAR(akey);
+ }
+ else {
+ akey = NULL;
+
+ if (!SYMBOLP(CAR(spec)))
+ LispDestroy("%s %s: %s cannot be a %s argument name",
+ fnames[type], name,
+ STROBJ(CAR(spec)), types[type]);
+ /* check if default value provided, and optionally a `svar' */
+ else if (CDR(spec) != NIL && (!CONSP(CDR(spec)) ||
+ (CDDR(spec) != NIL &&
+ (!SYMBOLP(CAR(CDDR(spec))) ||
+ CDR(CDDR(spec)) != NIL))))
+ LispDestroy("%s %s: bad argument specification %s",
+ fnames[type], name, STROBJ(spec));
+ if (CONSP(CDR(spec))) {
+ defval = CADR(spec);
+ if (CONSP(CDDR(spec)))
+ sform = CAR(CDDR(spec));
+ }
+ /* Add to keyword package, and set the keyword in the
+ * argument list, so that a function argument keyword
+ * will reference the same object, and make comparison
+ * simpler. */
+ spec = LispCheckKeyword(CAR(spec));
+ }
+
+ count = alist->keys.num_symbols;
+ REALLOC_OBJECTS(alist->keys.keys, count + 1);
+ REALLOC_OBJECTS(alist->keys.defaults, count + 1);
+ REALLOC_OBJECTS(alist->keys.sforms, count + 1);
+ REALLOC_OBJECTS(alist->keys.symbols, count + 1);
+ alist->keys.symbols[count] = spec;
+ alist->keys.defaults[count] = defval;
+ alist->keys.sforms[count] = sform;
+ alist->keys.keys[count] = akey;
+ ++alist->keys.num_symbols;
+ if (count == 0)
+ *desc++ = 'k';
+ alist->num_arguments += 1 + (sform != NULL);
+ }
+ else if (optional) {
+ defval = default_value;
+ sform = NULL;
+
+ if (!SYMBOLP(CAR(spec)))
+ LispDestroy("%s %s: %s cannot be a %s argument name",
+ fnames[type], name,
+ STROBJ(CAR(spec)), types[type]);
+ /* check if default value provided, and optionally a `svar' */
+ else if (CDR(spec) != NIL && (!CONSP(CDR(spec)) ||
+ (CDDR(spec) != NIL &&
+ (!SYMBOLP(CAR(CDDR(spec))) ||
+ CDR(CDDR(spec)) != NIL))))
+ LispDestroy("%s %s: bad argument specification %s",
+ fnames[type], name, STROBJ(spec));
+ if (CONSP(CDR(spec))) {
+ defval = CADR(spec);
+ if (CONSP(CDDR(spec)))
+ sform = CAR(CDDR(spec));
+ }
+ spec = CAR(spec);
+
+ count = alist->optionals.num_symbols;
+ REALLOC_OBJECTS(alist->optionals.symbols, count + 1);
+ REALLOC_OBJECTS(alist->optionals.defaults, count + 1);
+ REALLOC_OBJECTS(alist->optionals.sforms, count + 1);
+ alist->optionals.symbols[count] = spec;
+ alist->optionals.defaults[count] = defval;
+ alist->optionals.sforms[count] = sform;
+ ++alist->optionals.num_symbols;
+ if (count == 0)
+ *desc++ = 'o';
+ alist->num_arguments += 1 + (sform != NULL);
+ }
+
+ /* Normal arguments cannot have default value */
+ else
+ LispDestroy("%s %s: syntax error parsing %s",
+ fnames[type], name, STROBJ(spec));
+ }
+
+ /* spec must be an atom, excluding keywords */
+ else if (!SYMBOLP(spec) || KEYWORDP(spec))
+ LispDestroy("%s %s: %s cannot be a %s argument",
+ fnames[type], name, STROBJ(spec), types[type]);
+ else {
+ Atom_id atom = ATOMID(spec);
+
+ if (atom[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));
+ if (key)
+ LispDestroy("%s %s: %s not allowed after %s",
+ fnames[type], name, keys[IREST], keys[IKEY]);
+ rest = 1;
+ continue;
+ }
+
+ else if (atom == Skey) {
+ if (rest || aux)
+ LispDestroy("%s %s: %s not allowed after %s",
+ fnames[type], name, ATOMID(spec),
+ rest ? keys[IREST] : keys[IAUX]);
+ key = 1;
+ continue;
+ }
+
+ else if (atom == Soptional) {
+ if (rest || optional || aux || key)
+ LispDestroy("%s %s: %s not allowed after %s",
+ fnames[type], name, ATOMID(spec),
+ rest ? keys[IREST] :
+ optional ?
+ keys[IOPTIONAL] :
+ aux ? keys[IAUX] : keys[IKEY]);
+ optional = 1;
+ continue;
+ }
+
+ else if (atom == Saux) {
+ /* &AUX must be the last keyword parameter */
+ if (aux)
+ LispDestroy("%s %s: syntax error parsing %s",
+ fnames[type], name, ATOMID(spec));
+ else if (builtin)
+ LispDestroy("builtin function cannot have &AUX arguments");
+ aux = 1;
+ continue;
+ }
+
+ /* Untill more lambda-list keywords supported, don't allow
+ * argument names starting with the '&' character */
+ else
+ LispDestroy("%s %s: %s not allowed/implemented",
+ fnames[type], name, ATOMID(spec));
+ }
+
+ /* Add argument to alist */
+ if (aux) {
+ count = alist->auxs.num_symbols;
+ REALLOC_OBJECTS(alist->auxs.symbols, count + 1);
+ REALLOC_OBJECTS(alist->auxs.initials, count + 1);
+ alist->auxs.symbols[count] = spec;
+ alist->auxs.initials[count] = default_value;
+ ++alist->auxs.num_symbols;
+ if (count == 0)
+ *desc++ = 'a';
+ ++alist->num_arguments;
+ }
+ else if (rest) {
+ alist->rest = spec;
+ *desc++ = 'r';
+ ++alist->num_arguments;
+ }
+ else if (key) {
+ /* Add to keyword package, and set the keyword in the
+ * argument list, so that a function argument keyword
+ * will reference the same object, and make comparison
+ * simpler. */
+ spec = LispCheckKeyword(spec);
+ count = alist->keys.num_symbols;
+ REALLOC_OBJECTS(alist->keys.keys, count + 1);
+ REALLOC_OBJECTS(alist->keys.defaults, count + 1);
+ REALLOC_OBJECTS(alist->keys.sforms, count + 1);
+ REALLOC_OBJECTS(alist->keys.symbols, count + 1);
+ alist->keys.symbols[count] = spec;
+ alist->keys.defaults[count] = default_value;
+ alist->keys.sforms[count] = NULL;
+ alist->keys.keys[count] = NULL;
+ ++alist->keys.num_symbols;
+ if (count == 0)
+ *desc++ = 'k';
+ ++alist->num_arguments;
+ }
+ else if (optional) {
+ count = alist->optionals.num_symbols;
+ REALLOC_OBJECTS(alist->optionals.symbols, count + 1);
+ REALLOC_OBJECTS(alist->optionals.defaults, count + 1);
+ REALLOC_OBJECTS(alist->optionals.sforms, count + 1);
+ alist->optionals.symbols[count] = spec;
+ alist->optionals.defaults[count] = default_value;
+ alist->optionals.sforms[count] = NULL;
+ ++alist->optionals.num_symbols;
+ if (count == 0)
+ *desc++ = 'o';
+ ++alist->num_arguments;
+ }
+ else {
+ count = alist->normals.num_symbols;
+ REALLOC_OBJECTS(alist->normals.symbols, count + 1);
+ alist->normals.symbols[count] = spec;
+ ++alist->normals.num_symbols;
+ if (count == 0)
+ *desc++ = '.';
+ ++alist->num_arguments;
+ }
+ }
+ }
+
+ /* Check for dotted argument list */
+ if (list != NIL)
+ LispDestroy("%s %s: %s cannot end %s arguments",
+ fnames[type], name, STROBJ(list), types[type]);
+
+ *desc = '\0';
+ alist->description = LispGetAtomString(description, 0);
+
+ return (alist);
+}
+
+void
+LispAddBuiltinFunction(LispBuiltin *builtin)
+{
+ static LispObj stream;
+ static LispString string;
+ static int first = 1;
+ LispObj *name, *obj, *list, *cons, *code;
+ LispAtom *atom;
+ LispArgList *alist;
+ int length = lisp__data.protect.length;
+
+ if (first) {
+ stream.type = LispStream_t;
+ stream.data.stream.source.string = &string;
+ stream.data.stream.pathname = NIL;
+ stream.data.stream.type = LispStreamString;
+ stream.data.stream.readable = 1;
+ stream.data.stream.writable = 0;
+ string.output = 0;
+ first = 0;
+ }
+ string.string = builtin->declaration;
+ string.length = strlen(builtin->declaration);
+ string.input = 0;
+
+ code = COD;
+ LispPushInput(&stream);
+ name = LispRead();
+ list = cons = CONS(name, NIL);
+ if (length + 1 >= lisp__data.protect.space)
+ LispMoreProtects();
+ lisp__data.protect.objects[lisp__data.protect.length++] = list;
+ while ((obj = LispRead()) != NULL) {
+ RPLACD(cons, CONS(obj, NIL));
+ cons = CDR(cons);
+ }
+ LispPopInput(&stream);
+
+ atom = name->data.atom;
+ alist = LispCheckArguments(builtin->type, CDR(list), atom->string, 1);
+ builtin->symbol = CAR(list);
+ LispSetAtomBuiltinProperty(atom, builtin, alist);
+ LispUseArgList(alist);
+
+ /* Make function a extern symbol, unless told to not do so */
+ if (!builtin->internal)
+ LispExportSymbol(name);
+
+ lisp__data.protect.length = length;
+ COD = code; /* LispRead protect data in COD */
+}
+
+void
+LispAllocSeg(LispObjSeg *seg, int cellcount)
+{
+ unsigned int i;
+ LispObj **list, *obj;
+
+ DISABLE_INTERRUPTS();
+ while (seg->nfree < cellcount) {
+ if ((obj = (LispObj*)calloc(1, sizeof(LispObj) * segsize)) == NULL) {
+ ENABLE_INTERRUPTS();
+ LispDestroy("out of memory");
+ }
+ if ((list = (LispObj**)realloc(seg->objects,
+ sizeof(LispObj*) * (seg->nsegs + 1))) == NULL) {
+ free(obj);
+ ENABLE_INTERRUPTS();
+ LispDestroy("out of memory");
+ }
+ seg->objects = list;
+ seg->objects[seg->nsegs] = obj;
+
+ seg->nfree += segsize;
+ seg->nobjs += segsize;
+ for (i = 1; i < segsize; i++, obj++) {
+ /* Objects of type cons are the most used, save some time
+ * by not setting it's type in LispNewCons. */
+ obj->type = LispCons_t;
+ CDR(obj) = obj + 1;
+ }
+ obj->type = LispCons_t;
+ CDR(obj) = seg->freeobj;
+ seg->freeobj = seg->objects[seg->nsegs];
+ ++seg->nsegs;
+ }
+#ifdef DEBUG
+ LispMessage("gc: %d cell(s) allocated at %d segment(s)",
+ seg->nobjs, seg->nsegs);
+#endif
+ ENABLE_INTERRUPTS();
+}
+
+static INLINE void
+LispMark(register LispObj *object)
+{
+mark_again:
+ switch (OBJECT_TYPE(object)) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ case LispFunction_t:
+ return;
+ case LispLambda_t:
+ if (OPAQUEP(object->data.lambda.name))
+ object->data.lambda.name->mark = 1;
+ object->mark = 1;
+ LispMark(object->data.lambda.data);
+ object = object->data.lambda.code;
+ goto mark_cons;
+ case LispQuote_t:
+ case LispBackquote_t:
+ case LispFunctionQuote_t:
+ object->mark = 1;
+ object = object->data.quote;
+ goto mark_again;
+ case LispPathname_t:
+ object->mark = 1;
+ object = object->data.pathname;
+ goto mark_again;
+ case LispComma_t:
+ object->mark = 1;
+ object = object->data.comma.eval;
+ goto mark_again;
+ case LispComplex_t:
+ if (POINTERP(object->data.complex.real))
+ object->data.complex.real->mark = 1;
+ if (POINTERP(object->data.complex.imag))
+ object->data.complex.imag->mark = 1;
+ break;
+ case LispCons_t:
+mark_cons:
+ for (; CONSP(object) && !object->mark; object = CDR(object)) {
+ object->mark = 1;
+ switch (OBJECT_TYPE(CAR(object))) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ case LispPackage_t: /* protected in gc */
+ break;
+ case LispInteger_t:
+ case LispDFloat_t:
+ case LispString_t:
+ case LispRatio_t:
+ case LispOpaque_t:
+ case LispBignum_t:
+ case LispBigratio_t:
+ CAR(object)->mark = 1;
+ break;
+ default:
+ LispMark(CAR(object));
+ break;
+ }
+ }
+ if (POINTERP(object) && !object->mark)
+ goto mark_again;
+ return;
+ case LispArray_t:
+ LispMark(object->data.array.list);
+ object->mark = 1;
+ object = object->data.array.dim;
+ goto mark_cons;
+ case LispStruct_t:
+ object->mark = 1;
+ object = object->data.struc.fields;
+ goto mark_cons;
+ case LispStream_t:
+mark_stream:
+ LispMark(object->data.stream.pathname);
+ if (object->data.stream.type == LispStreamPipe) {
+ object->mark = 1;
+ object = object->data.stream.source.program->errorp;
+ goto mark_stream;
+ }
+ break;
+ case LispRegex_t:
+ object->data.regex.pattern->mark = 1;
+ break;
+ case LispBytecode_t:
+ object->mark = 1;
+ object = object->data.bytecode.code;
+ goto mark_again;
+ case LispHashTable_t: {
+ unsigned long i;
+ LispHashEntry *entry = object->data.hash.table->entries,
+ *last = entry + object->data.hash.table->num_entries;
+
+ if (object->mark)
+ return;
+ object->mark = 1;
+ for (; entry < last; entry++) {
+ for (i = 0; i < entry->count; i++) {
+ switch (OBJECT_TYPE(entry->keys[i])) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ case LispFunction_t:
+ case LispPackage_t:
+ break;
+ case LispInteger_t:
+ case LispDFloat_t:
+ case LispString_t:
+ case LispRatio_t:
+ case LispOpaque_t:
+ case LispBignum_t:
+ case LispBigratio_t:
+ entry->keys[i]->mark = 1;
+ break;
+ default:
+ LispMark(entry->keys[i]);
+ break;
+ }
+ switch (OBJECT_TYPE(entry->values[i])) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ case LispFunction_t:
+ case LispPackage_t:
+ break;
+ case LispInteger_t:
+ case LispDFloat_t:
+ case LispString_t:
+ case LispRatio_t:
+ case LispOpaque_t:
+ case LispBignum_t:
+ case LispBigratio_t:
+ entry->values[i]->mark = 1;
+ break;
+ default:
+ LispMark(entry->values[i]);
+ break;
+ }
+ }
+ }
+ } return;
+ default:
+ break;
+ }
+ object->mark = 1;
+}
+
+static INLINE void
+LispProt(register LispObj *object)
+{
+prot_again:
+ switch (OBJECT_TYPE(object)) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ case LispFunction_t:
+ return;
+ case LispLambda_t:
+ if (OPAQUEP(object->data.lambda.name))
+ object->data.lambda.name->prot = 1;
+ object->prot = 1;
+ LispProt(object->data.lambda.data);
+ object = object->data.lambda.code;
+ goto prot_cons;
+ case LispQuote_t:
+ case LispBackquote_t:
+ case LispFunctionQuote_t:
+ object->prot = 1;
+ object = object->data.quote;
+ goto prot_again;
+ case LispPathname_t:
+ object->prot = 1;
+ object = object->data.pathname;
+ goto prot_again;
+ case LispComma_t:
+ object->prot = 1;
+ object = object->data.comma.eval;
+ goto prot_again;
+ case LispComplex_t:
+ if (POINTERP(object->data.complex.real))
+ object->data.complex.real->prot = 1;
+ if (POINTERP(object->data.complex.imag))
+ object->data.complex.imag->prot = 1;
+ break;
+ case LispCons_t:
+prot_cons:
+ for (; CONSP(object) && !object->prot; object = CDR(object)) {
+ object->prot = 1;
+ switch (OBJECT_TYPE(CAR(object))) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ case LispFunction_t:
+ case LispPackage_t: /* protected in gc */
+ break;
+ case LispInteger_t:
+ case LispDFloat_t:
+ case LispString_t:
+ case LispRatio_t:
+ case LispOpaque_t:
+ case LispBignum_t:
+ case LispBigratio_t:
+ CAR(object)->prot = 1;
+ break;
+ default:
+ LispProt(CAR(object));
+ break;
+ }
+ }
+ if (POINTERP(object) && !object->prot)
+ goto prot_again;
+ return;
+ case LispArray_t:
+ LispProt(object->data.array.list);
+ object->prot = 1;
+ object = object->data.array.dim;
+ goto prot_cons;
+ case LispStruct_t:
+ object->prot = 1;
+ object = object->data.struc.fields;
+ goto prot_cons;
+ case LispStream_t:
+prot_stream:
+ LispProt(object->data.stream.pathname);
+ if (object->data.stream.type == LispStreamPipe) {
+ object->prot = 1;
+ object = object->data.stream.source.program->errorp;
+ goto prot_stream;
+ }
+ break;
+ case LispRegex_t:
+ object->data.regex.pattern->prot = 1;
+ break;
+ case LispBytecode_t:
+ object->prot = 1;
+ object = object->data.bytecode.code;
+ goto prot_again;
+ case LispHashTable_t: {
+ unsigned long i;
+ LispHashEntry *entry = object->data.hash.table->entries,
+ *last = entry + object->data.hash.table->num_entries;
+
+ if (object->prot)
+ return;
+ object->prot = 1;
+ for (; entry < last; entry++) {
+ for (i = 0; i < entry->count; i++) {
+ switch (OBJECT_TYPE(entry->keys[i])) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ case LispFunction_t:
+ case LispPackage_t:
+ break;
+ case LispInteger_t:
+ case LispDFloat_t:
+ case LispString_t:
+ case LispRatio_t:
+ case LispOpaque_t:
+ case LispBignum_t:
+ case LispBigratio_t:
+ entry->keys[i]->prot = 1;
+ break;
+ default:
+ LispProt(entry->keys[i]);
+ break;
+ }
+ switch (OBJECT_TYPE(entry->values[i])) {
+ case LispNil_t:
+ case LispAtom_t:
+ case LispFixnum_t:
+ case LispSChar_t:
+ case LispFunction_t:
+ case LispPackage_t:
+ break;
+ case LispInteger_t:
+ case LispDFloat_t:
+ case LispString_t:
+ case LispRatio_t:
+ case LispOpaque_t:
+ case LispBignum_t:
+ case LispBigratio_t:
+ entry->values[i]->prot = 1;
+ break;
+ default:
+ LispProt(entry->values[i]);
+ break;
+ }
+ }
+ }
+ } return;
+ default:
+ break;
+ }
+ object->prot = 1;
+}
+
+void
+LispProtect(LispObj *key, LispObj *list)
+{
+ PRO = CONS(CONS(key, list), PRO);
+}
+
+void
+LispUProtect(LispObj *key, LispObj *list)
+{
+ LispObj *prev, *obj;
+
+ for (prev = obj = PRO; obj != NIL; prev = obj, obj = CDR(obj))
+ if (CAR(CAR(obj)) == key && CDR(CAR(obj)) == list) {
+ if (obj == PRO)
+ PRO = CDR(PRO);
+ else
+ CDR(prev) = CDR(obj);
+ return;
+ }
+
+ LispDestroy("no match for %s, at UPROTECT", STROBJ(key));
+}
+
+static LispObj *
+Lisp__New(LispObj *car, LispObj *cdr)
+{
+ int cellcount;
+ LispObj *obj;
+
+ Lisp__GC(car, cdr);
+#if 0
+ lisp__data.gc.average = (objseg.nfree + lisp__data.gc.average) >> 1;
+ if (lisp__data.gc.average < minfree) {
+ if (lisp__data.gc.expandbits < 6)
+ ++lisp__data.gc.expandbits;
+ }
+ else if (lisp__data.gc.expandbits)
+ --lisp__data.gc.expandbits;
+ /* For 32 bit computers, where sizeof(LispObj) == 16,
+ * minfree is set to 1024, and expandbits limited to 6,
+ * the maximum extra memory requested here should be 1Mb
+ */
+ cellcount = minfree << lisp__data.gc.expandbits;
+#else
+ /* Try to keep at least 3 times more free cells than the de number
+ * of used cells in the freelist, to amenize the cost of the gc time,
+ * in the, currently, very simple gc strategy code. */
+ cellcount = (objseg.nobjs - objseg.nfree) * 3;
+ cellcount = cellcount + (minfree - (cellcount % minfree));
+#endif
+
+ if (objseg.freeobj == NIL || objseg.nfree < cellcount)
+ LispAllocSeg(&objseg, cellcount);
+
+ obj = objseg.freeobj;
+ objseg.freeobj = CDR(obj);
+
+ return (obj);
+}
+
+LispObj *
+LispNew(LispObj *car, LispObj *cdr)
+{
+ LispObj *obj = objseg.freeobj;
+
+ if (obj == NIL)
+ obj = Lisp__New(car, cdr);
+ else
+ objseg.freeobj = CDR(obj);
+
+ return (obj);
+}
+
+LispObj *
+LispNewAtom(char *str, int intern)
+{
+ LispObj *object;
+ LispAtom *atom = LispDoGetAtom(str, 0);
+
+ if (atom->object) {
+ if (intern && atom->package == NULL)
+ atom->package = PACKAGE;
+
+ return (atom->object);
+ }
+
+ if (atomseg.freeobj == NIL)
+ LispAllocSeg(&atomseg, pagesize);
+ object = atomseg.freeobj;
+ atomseg.freeobj = CDR(object);
+ --atomseg.nfree;
+
+ object->type = LispAtom_t;
+ object->data.atom = atom;
+ atom->object = object;
+ if (intern)
+ atom->package = PACKAGE;
+
+ return (object);
+}
+
+LispObj *
+LispNewStaticAtom(char *str)
+{
+ LispObj *object;
+ LispAtom *atom = LispDoGetAtom(str, 1);
+
+ object = LispNewSymbol(atom);
+
+ return (object);
+}
+
+LispObj *
+LispNewSymbol(LispAtom *atom)
+{
+ if (atom->object) {
+ if (atom->package == NULL)
+ atom->package = PACKAGE;
+
+ return (atom->object);
+ }
+ else {
+ LispObj *symbol;
+
+ if (atomseg.freeobj == NIL)
+ LispAllocSeg(&atomseg, pagesize);
+ symbol = atomseg.freeobj;
+ atomseg.freeobj = CDR(symbol);
+ --atomseg.nfree;
+
+ symbol->type = LispAtom_t;
+ symbol->data.atom = atom;
+ atom->object = symbol;
+ atom->package = PACKAGE;
+
+ return (symbol);
+ }
+}
+
+/* function representation is created on demand and never released,
+ * even if the function is undefined and never defined again */
+LispObj *
+LispNewFunction(LispObj *symbol)
+{
+ LispObj *function;
+
+ if (symbol->data.atom->function)
+ return (symbol->data.atom->function);
+
+ if (symbol->data.atom->package == NULL)
+ symbol->data.atom->package = PACKAGE;
+
+ if (atomseg.freeobj == NIL)
+ LispAllocSeg(&atomseg, pagesize);
+ function = atomseg.freeobj;
+ atomseg.freeobj = CDR(function);
+ --atomseg.nfree;
+
+ function->type = LispFunction_t;
+ function->data.atom = symbol->data.atom;
+ symbol->data.atom->function = function;
+
+ return (function);
+}
+
+/* symbol name representation is created on demand and never released */
+LispObj *
+LispSymbolName(LispObj *symbol)
+{
+ LispObj *name;
+ LispAtom *atom = symbol->data.atom;
+
+ if (atom->name)
+ return (atom->name);
+
+ if (atomseg.freeobj == NIL)
+ LispAllocSeg(&atomseg, pagesize);
+ name = atomseg.freeobj;
+ atomseg.freeobj = CDR(name);
+ --atomseg.nfree;
+
+ name->type = LispString_t;
+ THESTR(name) = atom->string;
+ STRLEN(name) = strlen(atom->string);
+ name->data.string.writable = 0;
+ atom->name = name;
+
+ return (name);
+}
+
+LispObj *
+LispNewFunctionQuote(LispObj *object)
+{
+ LispObj *quote = LispNew(object, NIL);
+
+ quote->type = LispFunctionQuote_t;
+ quote->data.quote = object;
+
+ return (quote);
+}
+
+LispObj *
+LispNewDFloat(double value)
+{
+ LispObj *dfloat = objseg.freeobj;
+
+ if (dfloat == NIL)
+ dfloat = Lisp__New(NIL, NIL);
+ else
+ objseg.freeobj = CDR(dfloat);
+
+ dfloat->type = LispDFloat_t;
+ dfloat->data.dfloat = value;
+
+ return (dfloat);
+}
+
+LispObj *
+LispNewString(char *str, long length, int alloced)
+{
+ char *cstring;
+ LispObj *string = objseg.freeobj;
+
+ if (string == NIL)
+ string = Lisp__New(NIL, NIL);
+ else
+ objseg.freeobj = CDR(string);
+
+ if (alloced)
+ cstring = str;
+ else {
+ cstring = LispMalloc(length + 1);
+ memcpy(cstring, str, length);
+ cstring[length] = '\0';
+ }
+ LispMused(cstring);
+ string->type = LispString_t;
+ THESTR(string) = cstring;
+ STRLEN(string) = length;
+ string->data.string.writable = 1;
+
+ return (string);
+}
+
+LispObj *
+LispNewComplex(LispObj *realpart, LispObj *imagpart)
+{
+ LispObj *complexp = objseg.freeobj;
+
+ if (complexp == NIL)
+ complexp = Lisp__New(realpart, imagpart);
+ else
+ objseg.freeobj = CDR(complexp);
+
+ complexp->type = LispComplex_t;
+ complexp->data.complex.real = realpart;
+ complexp->data.complex.imag = imagpart;
+
+ return (complexp);
+}
+
+LispObj *
+LispNewInteger(long integer)
+{
+ if (integer > MOST_POSITIVE_FIXNUM || integer < MOST_NEGATIVE_FIXNUM) {
+ LispObj *object = objseg.freeobj;
+
+ if (object == NIL)
+ object = Lisp__New(NIL, NIL);
+ else
+ objseg.freeobj = CDR(object);
+
+ object->type = LispInteger_t;
+ object->data.integer = integer;
+
+ return (object);
+ }
+ return (FIXNUM(integer));
+}
+
+LispObj *
+LispNewRatio(long num, long den)
+{
+ LispObj *ratio = objseg.freeobj;
+
+ if (ratio == NIL)
+ ratio = Lisp__New(NIL, NIL);
+ else
+ objseg.freeobj = CDR(ratio);
+
+ ratio->type = LispRatio_t;
+ ratio->data.ratio.numerator = num;
+ ratio->data.ratio.denominator = den;
+
+ return (ratio);
+}
+
+LispObj *
+LispNewVector(LispObj *objects)
+{
+ GC_ENTER();
+ long count;
+ LispObj *array, *dimension;
+
+ for (count = 0, array = objects; CONSP(array); count++, array = CDR(array))
+ ;
+
+ GC_PROTECT(objects);
+ dimension = CONS(FIXNUM(count), NIL);
+ array = LispNew(objects, dimension);
+ array->type = LispArray_t;
+ array->data.array.list = objects;
+ array->data.array.dim = dimension;
+ array->data.array.rank = 1;
+ array->data.array.type = LispNil_t;
+ array->data.array.zero = count == 0;
+ GC_LEAVE();
+
+ return (array);
+}
+
+LispObj *
+LispNewQuote(LispObj *object)
+{
+ LispObj *quote = LispNew(object, NIL);
+
+ quote->type = LispQuote_t;
+ quote->data.quote = object;
+
+ return (quote);
+}
+
+LispObj *
+LispNewBackquote(LispObj *object)
+{
+ LispObj *backquote = LispNew(object, NIL);
+
+ backquote->type = LispBackquote_t;
+ backquote->data.quote = object;
+
+ return (backquote);
+}
+
+LispObj *
+LispNewComma(LispObj *object, int atlist)
+{
+ LispObj *comma = LispNew(object, NIL);
+
+ comma->type = LispComma_t;
+ comma->data.comma.eval = object;
+ comma->data.comma.atlist = atlist;
+
+ return (comma);
+}
+
+LispObj *
+LispNewCons(LispObj *car, LispObj *cdr)
+{
+ LispObj *cons = objseg.freeobj;
+
+ if (cons == NIL)
+ cons = Lisp__New(car, cdr);
+ else
+ objseg.freeobj = CDR(cons);
+
+ CAR(cons) = car;
+ CDR(cons) = cdr;
+
+ return (cons);
+}
+
+LispObj *
+LispNewLambda(LispObj *name, LispObj *code, LispObj *data, LispFunType type)
+{
+ LispObj *fun = LispNew(data, code);
+
+ fun->type = LispLambda_t;
+ fun->funtype = type;
+ fun->data.lambda.name = name;
+ fun->data.lambda.code = code;
+ fun->data.lambda.data = data;
+
+ return (fun);
+}
+
+LispObj *
+LispNewStruct(LispObj *fields, LispObj *def)
+{
+ LispObj *struc = LispNew(fields, def);
+
+ struc->type = LispStruct_t;
+ struc->data.struc.fields = fields;
+ struc->data.struc.def = def;
+
+ return (struc);
+}
+
+LispObj *
+LispNewOpaque(void *data, int type)
+{
+ LispObj *opaque = LispNew(NIL, NIL);
+
+ opaque->type = LispOpaque_t;
+ opaque->data.opaque.data = data;
+ opaque->data.opaque.type = type;
+
+ return (opaque);
+}
+
+/* string argument must be static, or allocated */
+LispObj *
+LispNewKeyword(char *string)
+{
+ LispObj *keyword;
+
+ if (PACKAGE != lisp__data.keyword) {
+ LispObj *savepackage;
+ LispPackage *savepack;
+
+ /* Save package environment */
+ savepackage = PACKAGE;
+ savepack = lisp__data.pack;
+
+ /* Change package environment */
+ PACKAGE = lisp__data.keyword;
+ lisp__data.pack = lisp__data.key;
+
+ /* Create symbol in keyword package */
+ keyword = LispNewStaticAtom(string);
+
+ /* Restore package environment */
+ PACKAGE = savepackage;
+ lisp__data.pack = savepack;
+ }
+ else
+ /* Just create symbol in keyword package */
+ keyword = LispNewStaticAtom(string);
+
+ /* Export keyword symbol */
+ LispExportSymbol(keyword);
+
+ /* All keywords are constants */
+ keyword->data.atom->constant = 1;
+
+ /* XXX maybe should bound the keyword to itself, but that would
+ * require allocating a LispProperty structure for every keyword */
+
+ return (keyword);
+}
+
+LispObj *
+LispNewPathname(LispObj *obj)
+{
+ LispObj *path = LispNew(obj, NIL);
+
+ path->type = LispPathname_t;
+ path->data.pathname = obj;
+
+ return (path);
+}
+
+LispObj *
+LispNewStringStream(char *string, int flags, long length, int alloced)
+{
+ LispObj *stream = LispNew(NIL, NIL);
+
+ SSTREAMP(stream) = LispCalloc(1, sizeof(LispString));
+ if (alloced)
+ SSTREAMP(stream)->string = string;
+ else {
+ SSTREAMP(stream)->string = LispMalloc(length + 1);
+ memcpy(SSTREAMP(stream)->string, string, length);
+ SSTREAMP(stream)->string[length] = '\0';
+ }
+
+ stream->type = LispStream_t;
+
+ SSTREAMP(stream)->length = length;
+ LispMused(SSTREAMP(stream));
+ LispMused(SSTREAMP(stream)->string);
+ stream->data.stream.type = LispStreamString;
+ stream->data.stream.readable = (flags & STREAM_READ) != 0;
+ stream->data.stream.writable = (flags & STREAM_WRITE) != 0;
+ SSTREAMP(stream)->space = length + 1;
+
+ stream->data.stream.pathname = NIL;
+
+ return (stream);
+}
+
+LispObj *
+LispNewFileStream(LispFile *file, LispObj *path, int flags)
+{
+ LispObj *stream = LispNew(NIL, NIL);
+
+ stream->type = LispStream_t;
+ FSTREAMP(stream) = file;
+ stream->data.stream.pathname = path;
+ stream->data.stream.type = LispStreamFile;
+ stream->data.stream.readable = (flags & STREAM_READ) != 0;
+ stream->data.stream.writable = (flags & STREAM_WRITE) != 0;
+
+ return (stream);
+}
+
+LispObj *
+LispNewPipeStream(LispPipe *program, LispObj *path, int flags)
+{
+ LispObj *stream = LispNew(NIL, NIL);
+
+ stream->type = LispStream_t;
+ PSTREAMP(stream) = program;
+ stream->data.stream.pathname = path;
+ stream->data.stream.type = LispStreamPipe;
+ stream->data.stream.readable = (flags & STREAM_READ) != 0;
+ stream->data.stream.writable = (flags & STREAM_WRITE) != 0;
+
+ return (stream);
+}
+
+LispObj *
+LispNewStandardStream(LispFile *file, LispObj *description, int flags)
+{
+ LispObj *stream = LispNew(NIL, NIL);
+
+ stream->type = LispStream_t;
+ FSTREAMP(stream) = file;
+ stream->data.stream.pathname = description;
+ stream->data.stream.type = LispStreamStandard;
+ stream->data.stream.readable = (flags & STREAM_READ) != 0;
+ stream->data.stream.writable = (flags & STREAM_WRITE) != 0;
+
+ return (stream);
+}
+
+LispObj *
+LispNewBignum(mpi *bignum)
+{
+ LispObj *integer = LispNew(NIL, NIL);
+
+ integer->type = LispBignum_t;
+ integer->data.mp.integer = bignum;
+ LispMused(bignum->digs);
+ LispMused(bignum);
+
+ return (integer);
+}
+
+LispObj *
+LispNewBigratio(mpr *bigratio)
+{
+ LispObj *ratio = LispNew(NIL, NIL);
+
+ ratio->type = LispBigratio_t;
+ ratio->data.mp.ratio = bigratio;
+ LispMused(mpr_num(bigratio)->digs);
+ LispMused(mpr_den(bigratio)->digs);
+ LispMused(bigratio);
+
+ return (ratio);
+}
+
+/* name must be of type LispString_t */
+LispObj *
+LispNewPackage(LispObj *name, LispObj *nicknames)
+{
+ LispObj *package = LispNew(name, nicknames);
+ LispPackage *pack = LispCalloc(1, sizeof(LispPackage));
+
+ package->type = LispPackage_t;
+ package->data.package.name = name;
+ package->data.package.nicknames = nicknames;
+ package->data.package.package = pack;
+
+ LispMused(pack);
+
+ return (package);
+}
+
+LispObj *
+LispSymbolFunction(LispObj *symbol)
+{
+ LispAtom *atom = symbol->data.atom;
+
+ if ((atom->a_builtin &&
+ atom->property->fun.builtin->type == LispFunction) ||
+ (atom->a_function &&
+ atom->property->fun.function->funtype == LispFunction) ||
+ (atom->a_defstruct &&
+ atom->property->structure.function != STRUCT_NAME) ||
+ /* XXX currently bytecode is only generated for functions */
+ atom->a_compiled)
+ symbol = FUNCTION(symbol);
+ else
+ LispDestroy("SYMBOL-FUNCTION: %s is not a function", STROBJ(symbol));
+
+ return (symbol);
+}
+
+
+static INLINE LispObj *
+LispGetVarPack(LispObj *symbol)
+{
+ int ii;
+ char *string;
+ LispAtom *atom;
+ LispProperty *property;
+
+ string = ATOMID(symbol);
+ property = symbol->data.atom->property;
+ ii = STRHASH(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);
+}
+
+/* package must be of type LispPackage_t */
+void
+LispUsePackage(LispObj *package)
+{
+ unsigned i;
+ LispAtom *atom;
+ LispPackage *pack;
+ LispObj **pentry, **eentry;
+
+ /* Already using its own symbols... */
+ if (package == PACKAGE)
+ return;
+
+ /* Check if package not already in use-package list */
+ for (pentry = lisp__data.pack->use.pairs,
+ eentry = pentry + lisp__data.pack->use.length;
+ pentry < eentry; pentry++)
+ if (*pentry == package)
+ return;
+
+ /* Remember this package is in the use-package list */
+ if (lisp__data.pack->use.length + 1 >= lisp__data.pack->use.space) {
+ LispObj **pairs = realloc(lisp__data.pack->use.pairs,
+ (lisp__data.pack->use.space + 1) *
+ sizeof(LispObj*));
+
+ if (pairs == NULL)
+ LispDestroy("out of memory");
+
+ lisp__data.pack->use.pairs = pairs;
+ ++lisp__data.pack->use.space;
+ }
+ lisp__data.pack->use.pairs[lisp__data.pack->use.length++] = package;
+
+ /* Import all extern symbols from 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;
+ }
+ }
+}
+
+/* symbol must be of type LispAtom_t */
+void
+LispImportSymbol(LispObj *symbol)
+{
+ int increment;
+ LispAtom *atom;
+ LispObj *current;
+
+ current = LispGetVarPack(symbol);
+ if (current == NULL || current->data.atom->property == NOPROPERTY) {
+ /* No conflicts */
+
+ if (symbol->data.atom->a_object) {
+ /* If it is a bounded variable */
+ if (lisp__data.pack->glb.length + 1 >= lisp__data.pack->glb.space)
+ LispMoreGlobals(lisp__data.pack);
+ lisp__data.pack->glb.pairs[lisp__data.pack->glb.length++] = symbol;
+ }
+
+ /* Create copy of atom in current package */
+ atom = LispDoGetAtom(ATOMID(symbol), 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. */
+
+ /* And reference the same properties */
+ atom->property = symbol->data.atom->property;
+
+ increment = 1;
+ }
+ else if (current->data.atom->property != symbol->data.atom->property) {
+ /* Symbol already exists in the current package,
+ * but does not reference the same variable */
+ LispContinuable("Symbol %s already defined in package %s. Redefine?",
+ ATOMID(symbol), THESTR(PACKAGE->data.package.name));
+
+ atom = current->data.atom;
+
+ /* Continued from error, redefine variable */
+ LispDecrementAtomReference(atom);
+ atom->property = symbol->data.atom->property;
+
+ atom->a_object = atom->a_function = atom->a_builtin =
+ atom->a_property = atom->a_defsetf = atom->a_defstruct = 0;
+
+ increment = 1;
+ }
+ else {
+ /* Symbol is already available in the current package, just update */
+ atom = current->data.atom;
+
+ increment = 0;
+ }
+
+ /* If importing an important system variable */
+ atom->watch = symbol->data.atom->watch;
+
+ /* Update constant flag */
+ atom->constant = symbol->data.atom->constant;
+
+ /* Set home-package and unique-atom associated with symbol */
+ atom->package = symbol->data.atom->package;
+ atom->object = symbol->data.atom->object;
+
+ if (symbol->data.atom->a_object)
+ atom->a_object = 1;
+ if (symbol->data.atom->a_function)
+ atom->a_function = 1;
+ else if (symbol->data.atom->a_builtin)
+ atom->a_builtin = 1;
+ else if (symbol->data.atom->a_compiled)
+ atom->a_compiled = 1;
+ if (symbol->data.atom->a_property)
+ atom->a_property = 1;
+ if (symbol->data.atom->a_defsetf)
+ atom->a_defsetf = 1;
+ if (symbol->data.atom->a_defstruct)
+ atom->a_defstruct = 1;
+
+ if (increment)
+ /* Increase reference count, more than one package using the symbol */
+ LispIncrementAtomReference(symbol->data.atom);
+}
+
+/* symbol must be of type LispAtom_t */
+void
+LispExportSymbol(LispObj *symbol)
+{
+ /* This does not automatically export symbols to another package using
+ * the symbols of the current package */
+ symbol->data.atom->ext = 1;
+}
+
+#ifdef __GNUC__
+LispObj *
+LispGetVar(LispObj *atom)
+{
+ return (LispDoGetVar(atom));
+}
+
+static INLINE LispObj *
+LispDoGetVar(LispObj *atom)
+#else
+#define LispDoGetVar LispGetVar
+LispObj *
+LispGetVar(LispObj *atom)
+#endif
+{
+ LispAtom *name;
+ int i, base, offset;
+ Atom_id id;
+
+ name = atom->data.atom;
+ if (name->constant && name->package == lisp__data.keyword)
+ return (atom);
+
+ /* XXX offset should be stored elsewhere, it is unique, like the string
+ * pointer. Unless a multi-thread interface is implemented (where
+ * multiple stacks would be required, the offset value should be
+ * stored with the string, so that a few cpu cicles could be saved
+ * by initializing the value to -1, and only searching for the symbol
+ * binding if it is not -1, and if no binding is found, because the
+ * lexical scope was left, reset offset to -1. */
+ offset = name->offset;
+ id = name->string;
+ base = lisp__data.env.lex;
+ i = lisp__data.env.head - 1;
+
+ if (offset <= i && (offset >= base || name->dyn) &&
+ lisp__data.env.names[offset] == id)
+ return (lisp__data.env.values[offset]);
+
+ for (; i >= base; i--)
+ if (lisp__data.env.names[i] == id) {
+ name->offset = i;
+
+ return (lisp__data.env.values[i]);
+ }
+
+ if (name->dyn) {
+ /* Keep searching as maybe a rebound dynamic variable */
+ for (; i >= 0; i--)
+ if (lisp__data.env.names[i] == id) {
+ name->offset = i;
+
+ return (lisp__data.env.values[i]);
+ }
+
+ if (name->a_object) {
+ /* Check for a symbol defined as special, but not yet bound. */
+ if (name->property->value == UNBOUND)
+ return (NULL);
+
+ return (name->property->value);
+ }
+ }
+
+ return (name->a_object ? name->property->value : NULL);
+}
+
+#ifdef DEBUGGER
+/* Same code as LispDoGetVar, but returns the address of the pointer to
+ * the object value. Used only by the debugger */
+void *
+LispGetVarAddr(LispObj *atom)
+{
+ LispAtom *name;
+ int i, base;
+ Atom_id id;
+
+ name = atom->data.atom;
+ if (name->constant && name->package == lisp__data.keyword)
+ return (&atom);
+
+ id = name->string;
+
+ i = lisp__data.env.head - 1;
+ for (base = lisp__data.env.lex; i >= base; i--)
+ if (lisp__data.env.names[i] == id)
+ return (&(lisp__data.env.values[i]));
+
+ if (name->dyn) {
+ for (; i >= 0; i--)
+ if (lisp__data.env.names[i] == id)
+ return (&(lisp__data.env.values[i]));
+
+ if (name->a_object) {
+ /* Check for a symbol defined as special, but not yet bound */
+ if (name->property->value == UNBOUND)
+ return (NULL);
+
+ return (&(name->property->value));
+ }
+ }
+
+ return (name->a_object ? &(name->property->value) : NULL);
+}
+#endif
+
+/* Only removes global variables. To be called by makunbound
+ * Local variables are unbounded once their block is closed anyway.
+ */
+void
+LispUnsetVar(LispObj *atom)
+{
+ LispAtom *name = atom->data.atom;
+
+ if (name->package) {
+ int i;
+ LispPackage *pack = name->package->data.package.package;
+
+ for (i = pack->glb.length - 1; i > 0; i--)
+ if (pack->glb.pairs[i] == atom) {
+ LispRemAtomObjectProperty(name);
+ --pack->glb.length;
+ if (i < pack->glb.length)
+ memmove(pack->glb.pairs + i, pack->glb.pairs + i + 1,
+ sizeof(LispObj*) * (pack->glb.length - i));
+
+ /* unset hint about dynamically binded variable */
+ if (name->dyn)
+ name->dyn = 0;
+ break;
+ }
+ }
+}
+
+LispObj *
+LispAddVar(LispObj *atom, LispObj *obj)
+{
+ if (lisp__data.env.length >= lisp__data.env.space)
+ LispMoreEnvironment();
+
+ LispDoAddVar(atom, obj);
+
+ return (obj);
+}
+
+static INLINE void
+LispDoAddVar(LispObj *symbol, LispObj *value)
+{
+ LispAtom *atom = symbol->data.atom;
+
+ atom->offset = lisp__data.env.length;
+ lisp__data.env.values[lisp__data.env.length] = value;
+ lisp__data.env.names[lisp__data.env.length++] = atom->string;
+}
+
+LispObj *
+LispSetVar(LispObj *atom, LispObj *obj)
+{
+ LispPackage *pack;
+ LispAtom *name;
+ int i, base, offset;
+ Atom_id id;
+
+ name = atom->data.atom;
+ offset = name->offset;
+ id = name->string;
+ base = lisp__data.env.lex;
+ i = lisp__data.env.head - 1;
+
+ if (offset <= i && (offset >= base || name->dyn) &&
+ lisp__data.env.names[offset] == id)
+ return (lisp__data.env.values[offset] = obj);
+
+ for (; i >= base; i--)
+ if (lisp__data.env.names[i] == id) {
+ name->offset = i;
+
+ return (lisp__data.env.values[i] = obj);
+ }
+
+ if (name->dyn) {
+ for (; i >= 0; i--)
+ if (lisp__data.env.names[i] == id)
+ return (lisp__data.env.values[i] = obj);
+
+ if (name->watch) {
+ LispSetAtomObjectProperty(name, obj);
+
+ return (obj);
+ }
+
+ return (SETVALUE(name, obj));
+ }
+
+ if (name->a_object) {
+ if (name->watch) {
+ LispSetAtomObjectProperty(name, obj);
+
+ return (obj);
+ }
+
+ return (SETVALUE(name, obj));
+ }
+
+ LispSetAtomObjectProperty(name, obj);
+
+ pack = name->package->data.package.package;
+ if (pack->glb.length >= pack->glb.space)
+ LispMoreGlobals(pack);
+
+ pack->glb.pairs[pack->glb.length++] = atom;
+
+ return (obj);
+}
+
+void
+LispProclaimSpecial(LispObj *atom, LispObj *value, LispObj *doc)
+{
+ int i = 0, dyn, glb;
+ LispAtom *name;
+ LispPackage *pack;
+
+ glb = 0;
+ name = atom->data.atom;
+ pack = name->package->data.package.package;
+ dyn = name->dyn;
+
+ if (!dyn) {
+ /* Note: don't check if a local variable already is using the symbol */
+ for (i = pack->glb.length - 1; i >= 0; i--)
+ if (pack->glb.pairs[i] == atom) {
+ glb = 1;
+ break;
+ }
+ }
+
+ if (dyn) {
+ if (name->property->value == UNBOUND && value)
+ /* if variable was just made special, but not bounded */
+ LispSetAtomObjectProperty(name, value);
+ }
+ else if (glb)
+ /* Already a global variable, but not marked as special.
+ * Set hint about dynamically binded variable. */
+ name->dyn = 1;
+ else {
+ /* create new special variable */
+ LispSetAtomObjectProperty(name, value ? value : UNBOUND);
+
+ if (pack->glb.length >= pack->glb.space)
+ LispMoreGlobals(pack);
+
+ pack->glb.pairs[pack->glb.length] = atom;
+ ++pack->glb.length;
+ /* set hint about possibly dynamically binded variable */
+ name->dyn = 1;
+ }
+
+ if (doc != NIL)
+ LispAddDocumentation(atom, doc, LispDocVariable);
+}
+
+void
+LispDefconstant(LispObj *atom, LispObj *value, LispObj *doc)
+{
+ int i;
+ LispAtom *name = atom->data.atom;
+ LispPackage *pack = name->package->data.package.package;
+
+ /* Unset hint about dynamically binded variable, if set. */
+ name->dyn = 0;
+
+ /* Check if variable is bounded as a global variable */
+ for (i = pack->glb.length - 1; i >= 0; i--)
+ if (pack->glb.pairs[i] == atom)
+ break;
+
+ if (i < 0) {
+ /* Not a global variable */
+ if (pack->glb.length >= pack->glb.space)
+ LispMoreGlobals(pack);
+
+ pack->glb.pairs[pack->glb.length] = atom;
+ ++pack->glb.length;
+ }
+
+ /* If already a constant variable */
+ if (name->constant && name->a_object && name->property->value != value)
+ LispWarning("constant %s is being redefined", STROBJ(atom));
+ else
+ name->constant = 1;
+
+ /* Set constant value */
+ LispSetAtomObjectProperty(name, value);
+
+ if (doc != NIL)
+ LispAddDocumentation(atom, doc, LispDocVariable);
+}
+
+void
+LispAddDocumentation(LispObj *symbol, LispObj *documentation, LispDocType_t type)
+{
+ int length;
+ char *string;
+ LispAtom *atom;
+ LispObj *object;
+
+ if (!SYMBOLP(symbol) || !STRINGP(documentation))
+ LispDestroy("DOCUMENTATION: invalid argument");
+
+ atom = symbol->data.atom;
+ if (atom->documentation[type])
+ LispRemDocumentation(symbol, type);
+
+ /* allocate documentation in atomseg */
+ if (atomseg.freeobj == NIL)
+ LispAllocSeg(&atomseg, pagesize);
+ length = STRLEN(documentation);
+ string = LispMalloc(length);
+ memcpy(string, THESTR(documentation), length);
+ string[length] = '\0';
+ object = atomseg.freeobj;
+ atomseg.freeobj = CDR(object);
+ --atomseg.nfree;
+
+ object->type = LispString_t;
+ THESTR(object) = string;
+ STRLEN(object) = length;
+ object->data.string.writable = 0;
+ atom->documentation[type] = object;
+ LispMused(string);
+}
+
+void
+LispRemDocumentation(LispObj *symbol, LispDocType_t type)
+{
+ LispAtom *atom;
+
+ if (!SYMBOLP(symbol))
+ LispDestroy("DOCUMENTATION: invalid argument");
+
+ atom = symbol->data.atom;
+ if (atom->documentation[type]) {
+ /* reclaim object to atomseg */
+ free(THESTR(atom->documentation[type]));
+ CDR(atom->documentation[type]) = atomseg.freeobj;
+ atomseg.freeobj = atom->documentation[type];
+ atom->documentation[type] = NULL;
+ ++atomseg.nfree;
+ }
+}
+
+LispObj *
+LispGetDocumentation(LispObj *symbol, LispDocType_t type)
+{
+ LispAtom *atom;
+
+ if (!SYMBOLP(symbol))
+ LispDestroy("DOCUMENTATION: invalid argument");
+
+ atom = symbol->data.atom;
+
+ return (atom->documentation[type] ? atom->documentation[type] : NIL);
+}
+
+LispObj *
+LispReverse(LispObj *list)
+{
+ LispObj *tmp, *res = NIL;
+
+ while (list != NIL) {
+ tmp = CDR(list);
+ CDR(list) = res;
+ res = list;
+ list = tmp;
+ }
+
+ return (res);
+}
+
+LispBlock *
+LispBeginBlock(LispObj *tag, LispBlockType type)
+{
+ LispBlock *block;
+ unsigned blevel = lisp__data.block.block_level + 1;
+
+ if (blevel > lisp__data.block.block_size) {
+ LispBlock **blk;
+
+ if (blevel > MAX_STACK_DEPTH)
+ LispDestroy("stack overflow");
+
+ DISABLE_INTERRUPTS();
+ blk = realloc(lisp__data.block.block, sizeof(LispBlock*) * (blevel + 1));
+
+ block = NULL;
+ if (blk == NULL || (block = malloc(sizeof(LispBlock))) == NULL) {
+ ENABLE_INTERRUPTS();
+ LispDestroy("out of memory");
+ }
+ lisp__data.block.block = blk;
+ lisp__data.block.block[lisp__data.block.block_size] = block;
+ lisp__data.block.block_size = blevel;
+ ENABLE_INTERRUPTS();
+ }
+ block = lisp__data.block.block[lisp__data.block.block_level];
+ if (type == LispBlockCatch && !CONSTANTP(tag)) {
+ tag = EVAL(tag);
+ lisp__data.protect.objects[lisp__data.protect.length++] = tag;
+ }
+ block->type = type;
+ block->tag = tag;
+ block->stack = lisp__data.stack.length;
+ block->protect = lisp__data.protect.length;
+ block->block_level = lisp__data.block.block_level;
+
+ lisp__data.block.block_level = blevel;
+
+#ifdef DEBUGGER
+ if (lisp__data.debugging) {
+ block->debug_level = lisp__data.debug_level;
+ block->debug_step = lisp__data.debug_step;
+ }
+#endif
+
+ return (block);
+}
+
+void
+LispEndBlock(LispBlock *block)
+{
+ lisp__data.protect.length = block->protect;
+ lisp__data.block.block_level = block->block_level;
+
+#ifdef DEBUGGER
+ if (lisp__data.debugging) {
+ if (lisp__data.debug_level >= block->debug_level) {
+ while (lisp__data.debug_level > block->debug_level) {
+ DBG = CDR(DBG);
+ --lisp__data.debug_level;
+ }
+ }
+ lisp__data.debug_step = block->debug_step;
+ }
+#endif
+}
+
+void
+LispBlockUnwind(LispBlock *block)
+{
+ LispBlock *unwind;
+ int blevel = lisp__data.block.block_level;
+
+ while (blevel > 0) {
+ unwind = lisp__data.block.block[--blevel];
+ if (unwind->type == LispBlockProtect) {
+ BLOCKJUMP(unwind);
+ }
+ if (unwind == block)
+ /* jump above unwind block */
+ break;
+ }
+}
+
+static LispObj *
+LispEvalBackquoteObject(LispObj *argument, int list, int quote)
+{
+ LispObj *result = argument, *object;
+
+ if (!POINTERP(argument))
+ return (argument);
+
+ else if (XCOMMAP(argument)) {
+ /* argument may need to be evaluated */
+
+ int atlist;
+
+ if (!list && argument->data.comma.atlist)
+ /* cannot append, not in a list */
+ LispDestroy("EVAL: ,@ only allowed on lists");
+
+ --quote;
+ if (quote < 0)
+ LispDestroy("EVAL: comma outside of backquote");
+
+ result = object = argument->data.comma.eval;
+ atlist = COMMAP(object) && object->data.comma.atlist;
+
+ if (POINTERP(result) && (XCOMMAP(result) || XBACKQUOTEP(result)))
+ /* nested commas, reduce 1 level, or backquote,
+ * don't call LispEval or quote argument will be reset */
+ result = LispEvalBackquoteObject(object, 0, quote);
+
+ else if (quote == 0)
+ /* just evaluate it */
+ result = EVAL(result);
+
+ if (quote != 0)
+ result = result == object ? argument : COMMA(result, atlist);
+ }
+
+ else if (XBACKQUOTEP(argument)) {
+ object = argument->data.quote;
+
+ result = LispEvalBackquote(object, quote + 1);
+ if (quote)
+ result = result == object ? argument : BACKQUOTE(result);
+ }
+
+ else if (XQUOTEP(argument) && POINTERP(argument->data.quote) &&
+ (XCOMMAP(argument->data.quote) ||
+ XBACKQUOTEP(argument->data.quote) ||
+ XCONSP(argument->data.quote))) {
+ /* ensures `',sym to be the same as `(quote ,sym) */
+ object = argument->data.quote;
+
+ result = LispEvalBackquote(argument->data.quote, quote);
+ result = result == object ? argument : QUOTE(result);
+ }
+
+ return (result);
+}
+
+LispObj *
+LispEvalBackquote(LispObj *argument, int quote)
+{
+ int protect;
+ LispObj *result, *object, *cons, *cdr;
+
+ if (!CONSP(argument))
+ return (LispEvalBackquoteObject(argument, 0, quote));
+
+ result = cdr = NIL;
+ protect = lisp__data.protect.length;
+
+ /* always generate a new list for the result, even if nothing
+ * is evaluated. It is not expected to use backqoutes when
+ * not required. */
+
+ /* reserve a GC protected slot for the result */
+ if (protect + 1 >= lisp__data.protect.space)
+ LispMoreProtects();
+ lisp__data.protect.objects[lisp__data.protect.length++] = NIL;
+
+ for (cons = argument; ; cons = CDR(cons)) {
+ /* if false, last argument, and if cons is not NIL, a dotted list */
+ int list = CONSP(cons), insert;
+
+ if (list)
+ object = CAR(cons);
+ else
+ object = cons;
+
+ if (COMMAP(object))
+ /* need to insert list elements in result, not just cons it? */
+ insert = object->data.comma.atlist;
+ else
+ insert = 0;
+
+ /* evaluate object, if required */
+ if (CONSP(object))
+ object = LispEvalBackquote(object, quote);
+ else
+ object = LispEvalBackquoteObject(object, insert, quote);
+
+ if (result == NIL) {
+ /* if starting result list */
+ if (!insert) {
+ if (list)
+ result = cdr = CONS(object, NIL);
+ else
+ result = cdr = object;
+ /* gc protect result */
+ lisp__data.protect.objects[protect] = result;
+ }
+ else {
+ if (!CONSP(object)) {
+ result = cdr = object;
+ /* gc protect result */
+ lisp__data.protect.objects[protect] = result;
+ }
+ else {
+ result = cdr = CONS(CAR(object), NIL);
+ /* gc protect result */
+ lisp__data.protect.objects[protect] = result;
+
+ /* add remaining elements to result */
+ for (object = CDR(object);
+ CONSP(object);
+ object = CDR(object)) {
+ RPLACD(cdr, CONS(CAR(object), NIL));
+ cdr = CDR(cdr);
+ }
+ if (object != NIL) {
+ /* object was a dotted list */
+ RPLACD(cdr, object);
+ cdr = CDR(cdr);
+ }
+ }
+ }
+ }
+ else {
+ if (!CONSP(cdr))
+ LispDestroy("EVAL: cannot append to %s", STROBJ(cdr));
+
+ if (!insert) {
+ if (list) {
+ RPLACD(cdr, CONS(object, NIL));
+ cdr = CDR(cdr);
+ }
+ else {
+ RPLACD(cdr, object);
+ cdr = object;
+ }
+ }
+ else {
+ if (!CONSP(object)) {
+ RPLACD(cdr, object);
+ /* if object is NIL, it is a empty list appended, not
+ * creating a dotted list. */
+ if (object != NIL)
+ cdr = object;
+ }
+ else {
+ for (; CONSP(object); object = CDR(object)) {
+ RPLACD(cdr, CONS(CAR(object), NIL));
+ cdr = CDR(cdr);
+ }
+ if (object != NIL) {
+ /* object was a dotted list */
+ RPLACD(cdr, object);
+ cdr = CDR(cdr);
+ }
+ }
+ }
+ }
+
+ /* if last argument list element processed */
+ if (!list)
+ break;
+ }
+
+ lisp__data.protect.length = protect;
+
+ return (result);
+}
+
+void
+LispMoreEnvironment(void)
+{
+ Atom_id *names;
+ LispObj **values;
+
+ DISABLE_INTERRUPTS();
+ names = realloc(lisp__data.env.names,
+ (lisp__data.env.space + 256) * sizeof(Atom_id));
+ if (names != NULL) {
+ values = realloc(lisp__data.env.values,
+ (lisp__data.env.space + 256) * sizeof(LispObj*));
+ if (values != NULL) {
+ lisp__data.env.names = names;
+ lisp__data.env.values = values;
+ lisp__data.env.space += 256;
+ ENABLE_INTERRUPTS();
+ return;
+ }
+ else
+ free(names);
+ }
+ ENABLE_INTERRUPTS();
+ LispDestroy("out of memory");
+}
+
+void
+LispMoreStack(void)
+{
+ LispObj **values;
+
+ DISABLE_INTERRUPTS();
+ values = realloc(lisp__data.stack.values,
+ (lisp__data.stack.space + 256) * sizeof(LispObj*));
+ if (values == NULL) {
+ ENABLE_INTERRUPTS();
+ LispDestroy("out of memory");
+ }
+ lisp__data.stack.values = values;
+ lisp__data.stack.space += 256;
+ ENABLE_INTERRUPTS();
+}
+
+void
+LispMoreGlobals(LispPackage *pack)
+{
+ LispObj **pairs;
+
+ DISABLE_INTERRUPTS();
+ pairs = realloc(pack->glb.pairs,
+ (pack->glb.space + 256) * sizeof(LispObj*));
+ if (pairs == NULL) {
+ ENABLE_INTERRUPTS();
+ LispDestroy("out of memory");
+ }
+ pack->glb.pairs = pairs;
+ pack->glb.space += 256;
+ ENABLE_INTERRUPTS();
+}
+
+void
+LispMoreProtects(void)
+{
+ LispObj **objects;
+
+ DISABLE_INTERRUPTS();
+ objects = realloc(lisp__data.protect.objects,
+ (lisp__data.protect.space + 256) * sizeof(LispObj*));
+ if (objects == NULL) {
+ ENABLE_INTERRUPTS();
+ LispDestroy("out of memory");
+ }
+ lisp__data.protect.objects = objects;
+ lisp__data.protect.space += 256;
+ ENABLE_INTERRUPTS();
+}
+
+static int
+LispMakeEnvironment(LispArgList *alist, LispObj *values,
+ LispObj *name, int eval, int builtin)
+{
+ char *desc;
+ int i, count, base;
+ LispObj **symbols, **defaults, **sforms;
+
+#define BUILTIN_ARGUMENT(value) \
+ lisp__data.stack.values[lisp__data.stack.length++] = value
+
+/* If the index value is from register variables, this
+ * can save some cpu time. Useful for normal arguments
+ * that are the most common, and thus the ones that
+ * consume more time in LispMakeEnvironment. */
+#define BUILTIN_NO_EVAL_ARGUMENT(index, value) \
+ lisp__data.stack.values[index] = value
+
+#define NORMAL_ARGUMENT(symbol, value) \
+ LispDoAddVar(symbol, value)
+
+ if (builtin) {
+ base = lisp__data.stack.length;
+ if (base + alist->num_arguments > lisp__data.stack.space) {
+ do
+ LispMoreStack();
+ while (base + alist->num_arguments > lisp__data.stack.space);
+ }
+ }
+ else {
+ base = lisp__data.env.length;
+ if (base + alist->num_arguments > lisp__data.env.space) {
+ do
+ LispMoreEnvironment();
+ while (base + alist->num_arguments > lisp__data.env.space);
+ }
+ }
+
+ desc = alist->description;
+ switch (*desc++) {
+ case '.':
+ goto normal_label;
+ case 'o':
+ goto optional_label;
+ case 'k':
+ goto key_label;
+ case 'r':
+ goto rest_label;
+ case 'a':
+ goto aux_label;
+ default:
+ goto done_label;
+ }
+
+
+ /* Code below is done in several almost identical loops, to avoid
+ * checking the value of the arguments eval and builtin too much times */
+
+
+ /* Normal arguments */
+normal_label:
+ i = 0;
+ count = alist->normals.num_symbols;
+ if (builtin) {
+ if (eval) {
+ for (; i < count && CONSP(values); i++, values = CDR(values)) {
+ BUILTIN_ARGUMENT(EVAL(CAR(values)));
+ }
+ }
+ else {
+ for (; i < count && CONSP(values); i++, values = CDR(values)) {
+ BUILTIN_NO_EVAL_ARGUMENT(base + i, CAR(values));
+ }
+ /* macro BUILTIN_NO_EVAL_ARGUMENT does not update
+ * lisp__data.stack.length, as there is no risk of GC while
+ * adding the arguments. */
+ lisp__data.stack.length += i;
+ }
+ }
+ else {
+ symbols = alist->normals.symbols;
+ if (eval) {
+ for (; i < count && CONSP(values); i++, values = CDR(values)) {
+ NORMAL_ARGUMENT(symbols[i], EVAL(CAR(values)));
+ }
+ }
+ else {
+ for (; i < count && CONSP(values); i++, values = CDR(values)) {
+ NORMAL_ARGUMENT(symbols[i], CAR(values));
+ }
+ }
+ }
+ if (i < count)
+ LispDestroy("%s: too few arguments", STROBJ(name));
+
+ switch (*desc++) {
+ case 'o':
+ goto optional_label;
+ case 'k':
+ goto key_label;
+ case 'r':
+ goto rest_label;
+ case 'a':
+ goto aux_label;
+ default:
+ goto done_label;
+ }
+
+ /* &OPTIONAL */
+optional_label:
+ i = 0;
+ count = alist->optionals.num_symbols;
+ defaults = alist->optionals.defaults;
+ sforms = alist->optionals.sforms;
+ if (builtin) {
+ if (eval) {
+ for (; i < count && CONSP(values); i++, values = CDR(values))
+ BUILTIN_ARGUMENT(EVAL(CAR(values)));
+ for (; i < count; i++)
+ BUILTIN_ARGUMENT(UNSPEC);
+ }
+ else {
+ for (; i < count && CONSP(values); i++, values = CDR(values))
+ BUILTIN_ARGUMENT(CAR(values));
+ for (; i < count; i++)
+ BUILTIN_ARGUMENT(UNSPEC);
+ }
+ }
+ else {
+ symbols = alist->optionals.symbols;
+ if (eval) {
+ for (; i < count && CONSP(values); i++, values = CDR(values)) {
+ NORMAL_ARGUMENT(symbols[i], EVAL(CAR(values)));
+ if (sforms[i]) {
+ NORMAL_ARGUMENT(sforms[i], T);
+ }
+ }
+ }
+ else {
+ for (; i < count && CONSP(values); i++, values = CDR(values)) {
+ NORMAL_ARGUMENT(symbols[i], CAR(values));
+ if (sforms[i]) {
+ NORMAL_ARGUMENT(sforms[i], T);
+ }
+ }
+ }
+
+ /* default arguments are evaluated for macros */
+ for (; i < count; i++) {
+ if (!CONSTANTP(defaults[i])) {
+ int head = lisp__data.env.head;
+ int lex = lisp__data.env.lex;
+
+ lisp__data.env.lex = base;
+ lisp__data.env.head = lisp__data.env.length;
+ NORMAL_ARGUMENT(symbols[i], EVAL(defaults[i]));
+ lisp__data.env.head = head;
+ lisp__data.env.lex = lex;
+ }
+ else {
+ NORMAL_ARGUMENT(symbols[i], defaults[i]);
+ }
+ if (sforms[i]) {
+ NORMAL_ARGUMENT(sforms[i], NIL);
+ }
+ }
+ }
+ switch (*desc++) {
+ case 'k':
+ goto key_label;
+ case 'r':
+ goto rest_label;
+ case 'a':
+ goto aux_label;
+ default:
+ goto done_label;
+ }
+
+ /* &KEY */
+key_label:
+ {
+ int argc, nused;
+ LispObj *val, *karg, **keys;
+
+ /* Count number of remaining arguments */
+ for (karg = values, argc = 0; CONSP(karg); karg = CDR(karg), argc++) {
+ karg = CDR(karg);
+ if (!CONSP(karg))
+ LispDestroy("%s: &KEY needs arguments as pairs",
+ STROBJ(name));
+ }
+
+
+ /* OPTIMIZATION:
+ * Builtin functions require that the keyword be in the keyword package.
+ * User functions don't need the arguments being pushed in the stack
+ * in the declared order (bytecode expects it...).
+ * XXX Error checking should be done elsewhere, code may be looping
+ * and doing error check here may consume too much cpu time.
+ * XXX Would also be good to already have the arguments specified in
+ * the correct order.
+ */
+
+
+ nused = 0;
+ val = NIL;
+ count = alist->keys.num_symbols;
+ symbols = alist->keys.symbols;
+ defaults = alist->keys.defaults;
+ sforms = alist->keys.sforms;
+ if (builtin) {
+
+ /* Arguments must be created in the declared order */
+ i = 0;
+ if (eval) {
+ for (; i < count; i++) {
+ for (karg = values; CONSP(karg); karg = CDDR(karg)) {
+ /* This is only true if both point to the
+ * same symbol in the keyword package. */
+ if (symbols[i] == CAR(karg)) {
+ if (karg == values)
+ values = CDDR(values);
+ ++nused;
+ BUILTIN_ARGUMENT(EVAL(CADR(karg)));
+ goto keyword_builtin_eval_used_label;
+ }
+ }
+ BUILTIN_ARGUMENT(UNSPEC);
+keyword_builtin_eval_used_label:;
+ }
+ }
+ else {
+ for (; i < count; i++) {
+ for (karg = values; CONSP(karg); karg = CDDR(karg)) {
+ if (symbols[i] == CAR(karg)) {
+ if (karg == values)
+ values = CDDR(values);
+ ++nused;
+ BUILTIN_ARGUMENT(CADR(karg));
+ goto keyword_builtin_used_label;
+ }
+ }
+ BUILTIN_ARGUMENT(UNSPEC);
+keyword_builtin_used_label:;
+ }
+ }
+
+ if (argc != nused) {
+ /* Argument(s) may be incorrectly specified, or specified
+ * twice (what is not an error). */
+ for (karg = values; CONSP(karg); karg = CDDR(karg)) {
+ val = CAR(karg);
+ if (KEYWORDP(val)) {
+ for (i = 0; i < count; i++)
+ if (symbols[i] == val)
+ break;
+ }
+ else
+ /* Just make the error test true */
+ i = count;
+
+ if (i == count)
+ goto invalid_keyword_label;
+ }
+ }
+ }
+
+#if 0
+ else {
+ /* The base offset of the atom in the stack, to check for
+ * keywords specified twice. */
+ LispObj *symbol;
+ int offset = lisp__data.env.length;
+
+ keys = alist->keys.keys;
+ for (karg = values; CONSP(karg); karg = CDDR(karg)) {
+ symbol = CAR(karg);
+ if (SYMBOLP(symbol)) {
+ /* Must be a keyword, but even if it is a keyword, may
+ * be a typo, so assume it is correct. If it is not
+ * in the argument list, it is an error. */
+ for (i = 0; i < count; i++) {
+ if (!keys[i] && symbols[i] == symbol) {
+ LispAtom *atom = symbol->data.atom;
+
+ /* Symbol found in the argument list. */
+ if (atom->offset >= offset &&
+ atom->offset < offset + nused &&
+ lisp__data.env.names[atom->offset] ==
+ atom->string)
+ /* Specified more than once... */
+ goto keyword_duplicated_label;
+ break;
+ }
+ }
+ }
+ else {
+ Atom_id id;
+
+ if (!QUOTEP(symbol) || !SYMBOLP(val = symbol->data.quote)) {
+ /* Bad argument. */
+ val = symbol;
+ goto invalid_keyword_label;
+ }
+
+ id = ATOMID(val);
+ for (i = 0; i < count; i++) {
+ if (keys[i] && ATOMID(keys[i]) == id) {
+ LispAtom *atom = val->data.atom;
+
+ /* Symbol found in the argument list. */
+ if (atom->offset >= offset &&
+ atom->offset < offset + nused &&
+ lisp__data.env.names[atom->offset] ==
+ atom->string)
+ /* Specified more than once... */
+ goto keyword_duplicated_label;
+ break;
+ }
+ }
+ }
+ if (i == count) {
+ /* Argument specification not found. */
+ val = symbol;
+ goto invalid_keyword_label;
+ }
+ ++nused;
+ if (eval) {
+ NORMAL_ARGUMENT(symbols[i], EVAL(CADR(karg)));
+ }
+ else {
+ NORMAL_ARGUMENT(symbols[i], CADR(karg));
+ }
+ if (sforms[i]) {
+ NORMAL_ARGUMENT(sforms[i], T);
+ }
+keyword_duplicated_label:;
+ }
+
+ /* Add variables that were not specified in the function call. */
+ if (nused < count) {
+ int j;
+
+ for (i = 0; i < count; i++) {
+ Atom_id id = ATOMID(symbols[i]);
+
+ for (j = offset + nused - 1; j >= offset; j--) {
+ if (lisp__data.env.names[j] == id)
+ break;
+ }
+
+ if (j < offset) {
+ /* Argument not specified. Use default value */
+
+ /* default arguments are evaluated for macros */
+ if (!CONSTANTP(defaults[i])) {
+ int head = lisp__data.env.head;
+ int lex = lisp__data.env.lex;
+
+ lisp__data.env.lex = base;
+ lisp__data.env.head = lisp__data.env.length;
+ NORMAL_ARGUMENT(symbols[i], EVAL(defaults[i]));
+ lisp__data.env.head = head;
+ lisp__data.env.lex = lex;
+ }
+ else {
+ NORMAL_ARGUMENT(symbols[i], defaults[i]);
+ }
+ if (sforms[i]) {
+ NORMAL_ARGUMENT(sforms[i], NIL);
+ }
+ }
+ }
+ }
+ }
+#else
+ else {
+ int varset;
+
+ sforms = alist->keys.sforms;
+ keys = alist->keys.keys;
+
+ /* Add variables */
+ for (i = 0; i < alist->keys.num_symbols; i++) {
+ val = defaults[i];
+ varset = 0;
+ if (keys[i]) {
+ Atom_id atom = ATOMID(keys[i]);
+
+ /* Special keyword specification, need to compare ATOMID
+ * and keyword specification must be a quoted object */
+ for (karg = values; CONSP(karg); karg = CDR(karg)) {
+ val = CAR(karg);
+ if (QUOTEP(val) && atom == ATOMID(val->data.quote)) {
+ val = CADR(karg);
+ varset = 1;
+ ++nused;
+ break;
+ }
+ karg = CDR(karg);
+ }
+ }
+
+ else {
+ /* Normal keyword specification, can compare object pointers,
+ * as they point to the same object in the keyword package */
+ for (karg = values; CONSP(karg); karg = CDR(karg)) {
+ /* Don't check if argument is a valid keyword or
+ * special quoted keyword */
+ if (symbols[i] == CAR(karg)) {
+ val = CADR(karg);
+ varset = 1;
+ ++nused;
+ break;
+ }
+ karg = CDR(karg);
+ }
+ }
+
+ /* Add the variable to environment */
+ if (varset) {
+ NORMAL_ARGUMENT(symbols[i], eval ? EVAL(val) : val);
+ if (sforms[i]) {
+ NORMAL_ARGUMENT(sforms[i], T);
+ }
+ }
+ else {
+ /* default arguments are evaluated for macros */
+ if (!CONSTANTP(val)) {
+ int head = lisp__data.env.head;
+ int lex = lisp__data.env.lex;
+
+ lisp__data.env.lex = base;
+ lisp__data.env.head = lisp__data.env.length;
+ NORMAL_ARGUMENT(symbols[i], EVAL(val));
+ lisp__data.env.head = head;
+ lisp__data.env.lex = lex;
+ }
+ else {
+ NORMAL_ARGUMENT(symbols[i], val);
+ }
+ if (sforms[i]) {
+ NORMAL_ARGUMENT(sforms[i], NIL);
+ }
+ }
+ }
+
+ if (argc != nused) {
+ /* Argument(s) may be incorrectly specified, or specified
+ * twice (what is not an error). */
+ for (karg = values; CONSP(karg); karg = CDDR(karg)) {
+ val = CAR(karg);
+ if (KEYWORDP(val)) {
+ for (i = 0; i < count; i++)
+ if (symbols[i] == val)
+ break;
+ }
+ else if (QUOTEP(val) && SYMBOLP(val->data.quote)) {
+ Atom_id atom = ATOMID(val->data.quote);
+
+ for (i = 0; i < count; i++)
+ if (ATOMID(keys[i]) == atom)
+ break;
+ }
+ else
+ /* Just make the error test true */
+ i = count;
+
+ if (i == count)
+ goto invalid_keyword_label;
+ }
+ }
+ }
+#endif
+ goto check_aux_label;
+
+invalid_keyword_label:
+ {
+ /* If not in argument specification list... */
+ char function_name[36];
+
+ strcpy(function_name, STROBJ(name));
+ LispDestroy("%s: %s is an invalid keyword",
+ function_name, STROBJ(val));
+ }
+ }
+
+check_aux_label:
+ if (*desc == 'a') {
+ /* &KEY uses all remaining arguments */
+ values = NIL;
+ goto aux_label;
+ }
+ goto finished_label;
+
+ /* &REST */
+rest_label:
+ if (!CONSP(values)) {
+ if (builtin) {
+ BUILTIN_ARGUMENT(values);
+ }
+ else {
+ NORMAL_ARGUMENT(alist->rest, values);
+ }
+ values = NIL;
+ }
+ /* always allocate a new list, don't know if it will be retained */
+ else if (eval) {
+ LispObj *cons;
+
+ cons = CONS(EVAL(CAR(values)), NIL);
+ if (builtin) {
+ BUILTIN_ARGUMENT(cons);
+ }
+ else {
+ NORMAL_ARGUMENT(alist->rest, cons);
+ }
+ values = CDR(values);
+ for (; CONSP(values); values = CDR(values)) {
+ RPLACD(cons, CONS(EVAL(CAR(values)), NIL));
+ cons = CDR(cons);
+ }
+ }
+ else {
+ LispObj *cons;
+
+ cons = CONS(CAR(values), NIL);
+ if (builtin) {
+ BUILTIN_ARGUMENT(cons);
+ }
+ else {
+ NORMAL_ARGUMENT(alist->rest, cons);
+ }
+ values = CDR(values);
+ for (; CONSP(values); values = CDR(values)) {
+ RPLACD(cons, CONS(CAR(values), NIL));
+ cons = CDR(cons);
+ }
+ }
+ if (*desc != 'a')
+ goto finished_label;
+
+ /* &AUX */
+aux_label:
+ i = 0;
+ count = alist->auxs.num_symbols;
+ defaults = alist->auxs.initials;
+ symbols = alist->auxs.symbols;
+ {
+ int lex = lisp__data.env.lex;
+
+ lisp__data.env.lex = base;
+ lisp__data.env.head = lisp__data.env.length;
+ for (; i < count; i++) {
+ NORMAL_ARGUMENT(symbols[i], EVAL(defaults[i]));
+ ++lisp__data.env.head;
+ }
+ lisp__data.env.lex = lex;
+ }
+
+done_label:
+ if (CONSP(values))
+ LispDestroy("%s: too many arguments", STROBJ(name));
+
+finished_label:
+ if (builtin)
+ lisp__data.stack.base = base;
+ else {
+ lisp__data.env.head = lisp__data.env.length;
+ }
+#undef BULTIN_ARGUMENT
+#undef NORMAL_ARGUMENT
+#undef BUILTIN_NO_EVAL_ARGUMENT
+
+ return (base);
+}
+
+LispObj *
+LispFuncall(LispObj *function, LispObj *arguments, int eval)
+{
+ LispAtom *atom;
+ LispArgList *alist;
+ LispBuiltin *builtin;
+ LispObj *lambda, *result;
+ int macro, base;
+
+#ifdef DEBUGGER
+ if (lisp__data.debugging)
+ LispDebugger(LispDebugCallBegin, function, arguments);
+#endif
+
+ switch (OBJECT_TYPE(function)) {
+ case LispFunction_t:
+ function = function->data.atom->object;
+ case LispAtom_t:
+ atom = function->data.atom;
+ if (atom->a_builtin) {
+ builtin = atom->property->fun.builtin;
+
+ if (eval)
+ eval = builtin->type != LispMacro;
+ base = LispMakeEnvironment(atom->property->alist,
+ arguments, function, eval, 1);
+ if (builtin->multiple_values) {
+ RETURN_COUNT = 0;
+ result = builtin->function(builtin);
+ }
+ else {
+ result = builtin->function(builtin);
+ RETURN_COUNT = 0;
+ }
+ lisp__data.stack.base = lisp__data.stack.length = base;
+ }
+ else if (atom->a_compiled) {
+ int lex = lisp__data.env.lex;
+ lambda = atom->property->fun.function;
+ alist = atom->property->alist;
+
+ base = LispMakeEnvironment(alist, arguments, function, eval, 0);
+ lisp__data.env.lex = base;
+ result = LispExecuteBytecode(lambda);
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = base;
+ }
+ else if (atom->a_function) {
+ lambda = atom->property->fun.function;
+ macro = lambda->funtype == LispMacro;
+ alist = atom->property->alist;
+
+ lambda = lambda->data.lambda.code;
+ if (eval)
+ eval = !macro;
+ base = LispMakeEnvironment(alist, arguments, function, eval, 0);
+ result = LispRunFunMac(function, lambda, macro, base);
+ }
+ else if (atom->a_defstruct &&
+ atom->property->structure.function != STRUCT_NAME) {
+ LispObj cons;
+
+ if (atom->property->structure.function == STRUCT_CONSTRUCTOR)
+ atom = Omake_struct->data.atom;
+ else if (atom->property->structure.function == STRUCT_CHECK)
+ atom = Ostruct_type->data.atom;
+ else
+ atom = Ostruct_access->data.atom;
+ builtin = atom->property->fun.builtin;
+
+ cons.type = LispCons_t;
+ cons.data.cons.cdr = arguments;
+ if (eval) {
+ LispObj quote;
+
+ quote.type = LispQuote_t;
+ quote.data.quote = function;
+ cons.data.cons.car = &quote;
+ base = LispMakeEnvironment(atom->property->alist,
+ &cons, function, 1, 1);
+ }
+ else {
+ cons.data.cons.car = function;
+ base = LispMakeEnvironment(atom->property->alist,
+ &cons, function, 0, 1);
+ }
+ result = builtin->function(builtin);
+ RETURN_COUNT = 0;
+ lisp__data.stack.length = base;
+ }
+ else {
+ LispDestroy("EVAL: the function %s is not defined",
+ STROBJ(function));
+ /*NOTREACHED*/
+ result = NIL;
+ }
+ break;
+ case LispLambda_t:
+ lambda = function->data.lambda.code;
+ alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
+ base = LispMakeEnvironment(alist, arguments, function, eval, 0);
+ result = LispRunFunMac(function, lambda, 0, base);
+ break;
+ case LispCons_t:
+ if (CAR(function) == Olambda) {
+ function = EVAL(function);
+ if (LAMBDAP(function)) {
+ GC_ENTER();
+
+ GC_PROTECT(function);
+ lambda = function->data.lambda.code;
+ alist = (LispArgList*)function->data.lambda.name->data.opaque.data;
+ base = LispMakeEnvironment(alist, arguments, NIL, eval, 0);
+ result = LispRunFunMac(NIL, lambda, 0, base);
+ GC_LEAVE();
+ break;
+ }
+ }
+ default:
+ LispDestroy("EVAL: %s is invalid as a function",
+ STROBJ(function));
+ /*NOTREACHED*/
+ result = NIL;
+ break;
+ }
+
+#ifdef DEBUGGER
+ if (lisp__data.debugging)
+ LispDebugger(LispDebugCallEnd, function, result);
+#endif
+
+ return (result);
+}
+
+LispObj *
+LispEval(LispObj *object)
+{
+ LispObj *result;
+
+ switch (OBJECT_TYPE(object)) {
+ case LispAtom_t:
+ if ((result = LispDoGetVar(object)) == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(object));
+ break;
+ case LispCons_t:
+ result = LispFuncall(CAR(object), CDR(object), 1);
+ break;
+ case LispQuote_t:
+ result = object->data.quote;
+ break;
+ case LispFunctionQuote_t:
+ result = object->data.quote;
+ if (SYMBOLP(result))
+ result = LispSymbolFunction(result);
+ else if (CONSP(result) && CAR(result) == Olambda)
+ result = EVAL(result);
+ else
+ LispDestroy("FUNCTION: %s is not a function", STROBJ(result));
+ break;
+ case LispBackquote_t:
+ result = LispEvalBackquote(object->data.quote, 1);
+ break;
+ case LispComma_t:
+ LispDestroy("EVAL: comma outside of backquote");
+ default:
+ result = object;
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+LispApply1(LispObj *function, LispObj *argument)
+{
+ LispObj arguments;
+
+ arguments.type = LispCons_t;
+ arguments.data.cons.car = argument;
+ arguments.data.cons.cdr = NIL;
+
+ return (LispFuncall(function, &arguments, 0));
+}
+
+LispObj *
+LispApply2(LispObj *function, LispObj *argument1, LispObj *argument2)
+{
+ LispObj arguments, cdr;
+
+ arguments.type = cdr.type = LispCons_t;
+ arguments.data.cons.car = argument1;
+ arguments.data.cons.cdr = &cdr;
+ cdr.data.cons.car = argument2;
+ cdr.data.cons.cdr = NIL;
+
+ return (LispFuncall(function, &arguments, 0));
+}
+
+LispObj *
+LispApply3(LispObj *function, LispObj *arg1, LispObj *arg2, LispObj *arg3)
+{
+ LispObj arguments, car, cdr;
+
+ arguments.type = car.type = cdr.type = LispCons_t;
+ arguments.data.cons.car = arg1;
+ arguments.data.cons.cdr = &car;
+ car.data.cons.car = arg2;
+ car.data.cons.cdr = &cdr;
+ cdr.data.cons.car = arg3;
+ cdr.data.cons.cdr = NIL;
+
+ return (LispFuncall(function, &arguments, 0));
+}
+
+static LispObj *
+LispRunFunMac(LispObj *name, LispObj *code, int macro, int base)
+{
+ LispObj *result = NIL;
+
+ if (!macro) {
+ int lex = lisp__data.env.lex;
+ int did_jump = 1, *pdid_jump;
+ LispObj **pcode, **presult;
+ LispBlock *block;
+
+ block = LispBeginBlock(name, LispBlockClosure);
+ lisp__data.env.lex = base;
+ if (setjmp(block->jmp) == 0) {
+ for (pcode = &code, presult = &result, pdid_jump = &did_jump;
+ CONSP(code); code = CDR(code))
+ result = EVAL(CAR(code));
+ did_jump = 0;
+ }
+ LispEndBlock(block);
+ if (did_jump)
+ result = lisp__data.block.block_ret;
+ lisp__data.env.lex = lex;
+ lisp__data.env.head = lisp__data.env.length = base;
+ }
+ else {
+ GC_ENTER();
+
+ for (; CONSP(code); code = CDR(code))
+ result = EVAL(CAR(code));
+ /* FIXME this does not work if macro has &aux variables,
+ * but there are several other missing features, like
+ * destructuring and more lambda list keywords still missing.
+ * TODO later.
+ */
+ lisp__data.env.head = lisp__data.env.length = base;
+
+ GC_PROTECT(result);
+ result = EVAL(result);
+ GC_LEAVE();
+ }
+
+ return (result);
+}
+
+LispObj *
+LispRunSetf(LispArgList *alist, LispObj *setf, LispObj *place, LispObj *value)
+{
+ GC_ENTER();
+ LispObj *store, *code, *expression, *result, quote;
+ int base;
+
+ code = setf->data.lambda.code;
+ store = setf->data.lambda.data;
+
+ quote.type = LispQuote_t;
+ quote.data.quote = value;
+ LispDoAddVar(CAR(store), &quote);
+ ++lisp__data.env.head;
+ base = LispMakeEnvironment(alist, place, Oexpand_setf_method, 0, 0);
+
+ /* build expansion macro */
+ expression = NIL;
+ for (; CONSP(code); code = CDR(code))
+ expression = EVAL(CAR(code));
+
+ /* Minus 1 to pop the added variable */
+ lisp__data.env.head = lisp__data.env.length = base - 1;
+
+ /* protect expansion, and executes it */
+ GC_PROTECT(expression);
+ result = EVAL(expression);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+LispRunSetfMacro(LispAtom *atom, LispObj *arguments, LispObj *value)
+{
+ int base;
+ GC_ENTER();
+ LispObj *place, *body, *result, quote;
+
+ place = NIL;
+ base = LispMakeEnvironment(atom->property->alist,
+ arguments, atom->object, 0, 0);
+ body = atom->property->fun.function->data.lambda.code;
+
+ /* expand macro body */
+ for (; CONSP(body); body = CDR(body))
+ place = EVAL(CAR(body));
+
+ /* protect expansion */
+ GC_PROTECT(place);
+
+ /* restore environment */
+ lisp__data.env.head = lisp__data.env.length = base;
+
+ /* value is already evaluated */
+ quote.type = LispQuote_t;
+ quote.data.quote = value;
+
+ /* call setf again */
+ result = APPLY2(Osetf, place, &quote);
+
+ GC_LEAVE();
+
+ return (result);
+}
+
+char *
+LispStrObj(LispObj *object)
+{
+ static int first = 1;
+ static char buffer[34];
+ static LispObj stream;
+ static LispString string;
+
+ if (first) {
+ stream.type = LispStream_t;
+ stream.data.stream.source.string = &string;
+ stream.data.stream.pathname = NIL;
+ stream.data.stream.type = LispStreamString;
+ stream.data.stream.readable = 0;
+ stream.data.stream.writable = 1;
+
+ string.string = buffer;
+ string.fixed = 1;
+ string.space = sizeof(buffer) - 1;
+ first = 0;
+ }
+
+ string.length = string.output = 0;
+
+ LispWriteObject(&stream, object);
+
+ /* make sure string is nul terminated */
+ string.string[string.length] = '\0';
+ if (string.length >= 32) {
+ if (buffer[0] == '(')
+ strcpy(buffer + 27, "...)");
+ else
+ strcpy(buffer + 28, "...");
+ }
+
+ return (buffer);
+}
+
+void
+LispPrint(LispObj *object, LispObj *stream, int newline)
+{
+ if (stream != NIL && !STREAMP(stream)) {
+ LispDestroy("PRINT: %s is not a stream", STROBJ(stream));
+ }
+ if (newline && LispGetColumn(stream))
+ LispWriteChar(stream, '\n');
+ LispWriteObject(stream, object);
+ if (stream == NIL || (stream->data.stream.type == LispStreamStandard &&
+ stream->data.stream.source.file == Stdout))
+ LispFflush(Stdout);
+}
+
+void
+LispUpdateResults(LispObj *cod, LispObj *res)
+{
+ LispSetVar(RUN[2], LispGetVar(RUN[1]));
+ LispSetVar(RUN[1], LispGetVar(RUN[0]));
+ LispSetVar(RUN[0], cod);
+
+ LispSetVar(RES[2], LispGetVar(RES[1]));
+ LispSetVar(RES[1], LispGetVar(RES[0]));
+ LispSetVar(RES[0], res);
+}
+
+#ifdef SIGNALRETURNSINT
+int
+#else
+void
+#endif
+LispSignalHandler(int signum)
+{
+ LispSignal(signum);
+#ifdef SIGNALRETURNSINT
+ return (0);
+#endif
+}
+
+void
+LispSignal(int signum)
+{
+ char *errstr;
+ char buffer[32];
+
+ if (lisp__disable_int) {
+ lisp__interrupted = signum;
+ return;
+ }
+ switch (signum) {
+ case SIGINT:
+ errstr = "interrupted";
+ break;
+ case SIGFPE:
+ errstr = "floating point exception";
+ break;
+ default:
+ sprintf(buffer, "signal %d received", signum);
+ errstr = buffer;
+ break;
+ }
+ LispDestroy(errstr);
+}
+
+void
+LispDisableInterrupts(void)
+{
+ ++lisp__disable_int;
+}
+
+void
+LispEnableInterrupts(void)
+{
+ --lisp__disable_int;
+ if (lisp__disable_int <= 0 && lisp__interrupted)
+ LispSignal(lisp__interrupted);
+}
+
+void
+LispMachine(void)
+{
+ LispObj *cod, *obj;
+
+ lisp__data.sigint = signal(SIGINT, LispSignalHandler);
+ lisp__data.sigfpe = signal(SIGFPE, LispSignalHandler);
+
+ /*CONSTCOND*/
+ while (1) {
+ if (sigsetjmp(lisp__data.jmp, 1) == 0) {
+ lisp__data.running = 1;
+ if (lisp__data.interactive && lisp__data.prompt) {
+ LispFputs(Stdout, lisp__data.prompt);
+ LispFflush(Stdout);
+ }
+ if ((cod = LispRead()) != NULL) {
+ obj = EVAL(cod);
+ if (lisp__data.interactive) {
+ if (RETURN_COUNT >= 0)
+ LispPrint(obj, NIL, 1);
+ if (RETURN_COUNT > 0) {
+ int i;
+
+ for (i = 0; i < RETURN_COUNT; i++)
+ LispPrint(RETURN(i), NIL, 1);
+ }
+ LispUpdateResults(cod, obj);
+ if (LispGetColumn(NIL))
+ LispWriteChar(NIL, '\n');
+ }
+ }
+ LispTopLevel();
+ }
+ if (lisp__data.eof)
+ break;
+ }
+
+ signal(SIGINT, lisp__data.sigint);
+ signal(SIGFPE, lisp__data.sigfpe);
+
+ lisp__data.running = 0;
+}
+
+void *
+LispExecute(char *str)
+{
+ static LispObj stream;
+ static LispString string;
+ static int first = 1;
+
+ int running = lisp__data.running;
+ LispObj *result, *cod, *obj, **presult = &result;
+
+ if (str == NULL || *str == '\0')
+ return (NIL);
+
+ *presult = NIL;
+
+ if (first) {
+ stream.type = LispStream_t;
+ stream.data.stream.source.string = &string;
+ stream.data.stream.pathname = NIL;
+ stream.data.stream.type = LispStreamString;
+ stream.data.stream.readable = 1;
+ stream.data.stream.writable = 0;
+ string.output = 0;
+ first = 0;
+ }
+ string.string = str;
+ string.length = strlen(str);
+ string.input = 0;
+
+ LispPushInput(&stream);
+ if (!running) {
+ lisp__data.running = 1;
+ if (sigsetjmp(lisp__data.jmp, 1) != 0)
+ return (NULL);
+ }
+
+ cod = COD;
+ /*CONSTCOND*/
+ while (1) {
+ if ((obj = LispRead()) != NULL) {
+ result = EVAL(obj);
+ COD = cod;
+ }
+ if (lisp__data.eof)
+ break;
+ }
+ LispPopInput(&stream);
+
+ lisp__data.running = running;
+
+ return (result);
+}
+
+void
+LispBegin(void)
+{
+ int i;
+ LispAtom *atom;
+ char results[4];
+ LispObj *object, *path, *ext;
+
+ pagesize = LispGetPageSize();
+ segsize = pagesize / sizeof(LispObj);
+
+ /* Initialize memory management */
+ lisp__data.mem.mem = (void**)calloc(lisp__data.mem.space = 16,
+ sizeof(void*));
+ lisp__data.mem.index = lisp__data.mem.level = 0;
+
+ /* Allow LispGetVar to check ATOMID() of unbound symbols */
+ UNBOUND->data.atom = (LispAtom*)LispCalloc(1, sizeof(LispAtom));
+ LispMused(UNBOUND->data.atom);
+ noproperty.value = UNBOUND;
+
+ if (Stdin == NULL)
+ Stdin = LispFdopen(0, FILE_READ);
+ if (Stdout == NULL)
+ Stdout = LispFdopen(1, FILE_WRITE | FILE_BUFFERED);
+ if (Stderr == NULL)
+ Stderr = LispFdopen(2, FILE_WRITE);
+
+ /* minimum number of free cells after GC
+ * if sizeof(LispObj) == 16, than a minfree of 1024 would try to keep
+ * at least 16Kb of free cells.
+ */
+ minfree = 1024;
+
+ MOD = COD = PRO = NIL;
+#ifdef DEBUGGER
+ DBG = BRK = NIL;
+#endif
+
+ /* allocate initial object cells */
+ LispAllocSeg(&objseg, minfree);
+ LispAllocSeg(&atomseg, pagesize);
+ lisp__data.gc.average = segsize;
+
+ /* Don't allow gc in initialization */
+ GCDisable();
+
+ /* Initialize package system, the current package is LISP. Order of
+ * initialization is very important here */
+ lisp__data.lisp = LispNewPackage(STRING("LISP"),
+ CONS(STRING("COMMON-LISP"), NIL));
+
+ /* Make LISP package the current one */
+ lisp__data.pack = lisp__data.savepack =
+ lisp__data.lisp->data.package.package;
+
+ /* Allocate space in LISP package */
+ LispMoreGlobals(lisp__data.pack);
+
+ /* Allocate space for multiple value return values */
+ lisp__data.returns.values = malloc(MULTIPLE_VALUES_LIMIT *
+ (sizeof(LispObj*)));
+
+ /* Create the first atom, do it "by hand" because macro "PACKAGE"
+ * cannot yet be used. */
+ atom = LispGetPermAtom("*PACKAGE*");
+ lisp__data.package = atomseg.freeobj;
+ atomseg.freeobj = CDR(atomseg.freeobj);
+ --atomseg.nfree;
+ lisp__data.package->type = LispAtom_t;
+ lisp__data.package->data.atom = atom;
+ atom->object = lisp__data.package;
+ atom->package = lisp__data.lisp;
+
+ /* Set package list, to be used by (gc) and (list-all-packages) */
+ PACK = CONS(lisp__data.lisp, NIL);
+
+ /* Make *PACKAGE* a special variable */
+ LispProclaimSpecial(lisp__data.package, lisp__data.lisp, NIL);
+
+ /* Value of macro "PACKAGE" is now properly available */
+
+ /* Changing *PACKAGE* is like calling (in-package) */
+ lisp__data.package->data.atom->watch = 1;
+
+ /* And available to other packages */
+ LispExportSymbol(lisp__data.package);
+
+ /* Initialize stacks */
+ LispMoreEnvironment();
+ LispMoreStack();
+
+ /* Create the KEYWORD package */
+ Skeyword = GETATOMID("KEYWORD");
+ object = LispNewPackage(STRING(Skeyword),
+ CONS(STRING(""), NIL));
+
+ /* Update list of packages */
+ PACK = CONS(object, PACK);
+
+ /* Allow easy access to the keyword package */
+ lisp__data.keyword = object;
+ lisp__data.key = object->data.package.package;
+
+ /* Initialize some static important symbols */
+ Olambda = STATIC_ATOM("LAMBDA");
+ LispExportSymbol(Olambda);
+ Okey = STATIC_ATOM("&KEY");
+ LispExportSymbol(Okey);
+ Orest = STATIC_ATOM("&REST");
+ LispExportSymbol(Orest);
+ Ooptional = STATIC_ATOM("&OPTIONAL");
+ LispExportSymbol(Ooptional);
+ Oaux = STATIC_ATOM("&AUX");
+ LispExportSymbol(Oaux);
+ Kunspecific = KEYWORD("UNSPECIFIC");
+ Oformat = STATIC_ATOM("FORMAT");
+ Oexpand_setf_method = STATIC_ATOM("EXPAND-SETF-METHOD");
+
+ Omake_struct = STATIC_ATOM("MAKE-STRUCT");
+ Ostruct_access = STATIC_ATOM("STRUCT-ACCESS");
+ Ostruct_store = STATIC_ATOM("STRUCT-STORE");
+ Ostruct_type = STATIC_ATOM("STRUCT-TYPE");
+ Smake_struct = ATOMID(Omake_struct);
+ Sstruct_access = ATOMID(Ostruct_access);
+ Sstruct_store = ATOMID(Ostruct_store);
+ Sstruct_type = ATOMID(Ostruct_type);
+
+ /* Initialize some static atom ids */
+ Snil = GETATOMID("NIL");
+ St = GETATOMID("T");
+ Saux = ATOMID(Oaux);
+ Skey = ATOMID(Okey);
+ Soptional = ATOMID(Ooptional);
+ Srest = ATOMID(Orest);
+ Sand = GETATOMID("AND");
+ Sor = GETATOMID("OR");
+ Snot = GETATOMID("NOT");
+ Satom = GETATOMID("ATOM");
+ Ssymbol = GETATOMID("SYMBOL");
+ Sinteger = GETATOMID("INTEGER");
+ Scharacter = GETATOMID("CHARACTER");
+ Sstring = GETATOMID("STRING");
+ Slist = GETATOMID("LIST");
+ Scons = GETATOMID("CONS");
+ Svector = GETATOMID("VECTOR");
+ Sarray = GETATOMID("ARRAY");
+ Sstruct = GETATOMID("STRUCT");
+ Sfunction = GETATOMID("FUNCTION");
+ Spathname = GETATOMID("PATHNAME");
+ Srational = GETATOMID("RATIONAL");
+ Sfloat = GETATOMID("FLOAT");
+ Scomplex = GETATOMID("COMPLEX");
+ Sopaque = GETATOMID("OPAQUE");
+ Sdefault = GETATOMID("DEFAULT");
+
+ LispArgList_t = LispRegisterOpaqueType("LispArgList*");
+
+ lisp__data.unget = malloc(sizeof(LispUngetInfo*));
+ lisp__data.unget[0] = calloc(1, sizeof(LispUngetInfo));
+ lisp__data.nunget = 1;
+
+ lisp__data.standard_input = ATOM2("*STANDARD-INPUT*");
+ SINPUT = STANDARDSTREAM(Stdin, lisp__data.standard_input, STREAM_READ);
+ lisp__data.interactive = 1;
+ LispProclaimSpecial(lisp__data.standard_input,
+ lisp__data.input_list = SINPUT, NIL);
+ LispExportSymbol(lisp__data.standard_input);
+
+ lisp__data.standard_output = ATOM2("*STANDARD-OUTPUT*");
+ SOUTPUT = STANDARDSTREAM(Stdout, lisp__data.standard_output, STREAM_WRITE);
+ LispProclaimSpecial(lisp__data.standard_output,
+ lisp__data.output_list = SOUTPUT, NIL);
+ LispExportSymbol(lisp__data.standard_output);
+
+ object = ATOM2("*STANDARD-ERROR*");
+ lisp__data.error_stream = STANDARDSTREAM(Stderr, object, STREAM_WRITE);
+ LispProclaimSpecial(object, lisp__data.error_stream, NIL);
+ LispExportSymbol(object);
+
+ lisp__data.modules = ATOM2("*MODULES*");
+ LispProclaimSpecial(lisp__data.modules, MOD, NIL);
+ LispExportSymbol(lisp__data.modules);
+
+ object = CONS(KEYWORD("UNIX"), CONS(KEYWORD("XEDIT"), NIL));
+ lisp__data.features = ATOM2("*FEATURES*");
+ LispProclaimSpecial(lisp__data.features, object, NIL);
+ LispExportSymbol(lisp__data.features);
+
+ object = ATOM2("MULTIPLE-VALUES-LIMIT");
+ LispDefconstant(object, FIXNUM(MULTIPLE_VALUES_LIMIT + 1), NIL);
+ LispExportSymbol(object);
+
+ /* Reenable gc */
+ GCEnable();
+
+ LispBytecodeInit();
+ LispPackageInit();
+ LispCoreInit();
+ LispMathInit();
+ LispPathnameInit();
+ LispStreamInit();
+ LispRegexInit();
+ LispWriteInit();
+
+ lisp__data.prompt = isatty(0) ? "> " : NULL;
+
+ lisp__data.errexit = !lisp__data.interactive;
+
+ if (lisp__data.interactive) {
+ /* add +, ++, +++, *, **, and *** */
+ for (i = 0; i < 3; i++) {
+ results[i] = '+';
+ results[i + 1] = '\0';
+ RUN[i] = ATOM(results);
+ LispSetVar(RUN[i], NIL);
+ LispExportSymbol(RUN[i]);
+ }
+ for (i = 0; i < 3; i++) {
+ results[i] = '*';
+ results[i + 1] = '\0';
+ RES[i] = ATOM(results);
+ LispSetVar(RES[i], NIL);
+ LispExportSymbol(RES[i]);
+ }
+ }
+ else
+ RUN[0] = RUN[1] = RUN[2] = RES[0] = RES[1] = RES[2] = NIL;
+
+ /* Add LISP builtin functions */
+ for (i = 0; i < sizeof(lispbuiltins) / sizeof(lispbuiltins[0]); i++)
+ LispAddBuiltinFunction(&lispbuiltins[i]);
+
+ EXECUTE("(require \"lisp\")");
+
+ object = ATOM2("*DEFAULT-PATHNAME-DEFAULTS*");
+#ifdef LISPDIR
+ {
+ int length;
+ char *pathname = LISPDIR;
+
+ length = strlen(pathname);
+ if (length && pathname[length - 1] != '/') {
+ pathname = LispMalloc(length + 2);
+
+ strcpy(pathname, LISPDIR);
+ strcpy(pathname + length, "/");
+ path = LSTRING2(pathname, length + 1);
+ }
+ else
+ path = LSTRING(pathname, length);
+ }
+#else
+ path = STRING("");
+#endif
+ GCDisable();
+ LispProclaimSpecial(object, APPLY1(Oparse_namestring, path), NIL);
+ LispExportSymbol(object);
+ GCEnable();
+
+ /* Create and make EXT the current package */
+ PACKAGE = ext = LispNewPackage(STRING("EXT"), NIL);
+ lisp__data.pack = lisp__data.savepack = PACKAGE->data.package.package;
+
+ /* Update list of packages */
+ PACK = CONS(ext, PACK);
+
+ /* Import LISP external symbols in EXT package */
+ LispUsePackage(lisp__data.lisp);
+
+ /* Add EXT non standard builtin functions */
+ for (i = 0; i < sizeof(extbuiltins) / sizeof(extbuiltins[0]); i++)
+ LispAddBuiltinFunction(&extbuiltins[i]);
+
+ /* Create and make USER the current package */
+ GCDisable();
+ PACKAGE = LispNewPackage(STRING("USER"),
+ CONS(STRING("COMMON-LISP-USER"), NIL));
+ GCEnable();
+ lisp__data.pack = lisp__data.savepack = PACKAGE->data.package.package;
+
+ /* Update list of packages */
+ PACK = CONS(PACKAGE, PACK);
+
+ /* USER package inherits all LISP external symbols */
+ LispUsePackage(lisp__data.lisp);
+ /* And all EXT external symbols */
+ LispUsePackage(ext);
+
+ LispTopLevel();
+}
+
+void
+LispEnd()
+{
+ /* XXX needs to free all used memory, not just close file descriptors */
+}
+
+void
+LispSetPrompt(char *prompt)
+{
+ lisp__data.prompt = prompt;
+}
+
+void
+LispSetInteractive(int interactive)
+{
+ lisp__data.interactive = !!interactive;
+}
+
+void
+LispSetExitOnError(int errexit)
+{
+ lisp__data.errexit = !!errexit;
+}
+
+void
+LispDebug(int enable)
+{
+ lisp__data.debugging = !!enable;
+
+#ifdef DEBUGGER
+ /* assumes we are at the toplevel */
+ DBG = BRK = NIL;
+ lisp__data.debug_level = -1;
+ lisp__data.debug_step = 0;
+#endif
+}
diff --git a/lisp/lisp.h b/lisp/lisp.h
new file mode 100644
index 0000000..f783c37
--- /dev/null
+++ b/lisp/lisp.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/lisp.h,v 1.5 2002/11/08 08:00:57 paulo Exp $ */
+
+#ifndef Lisp_lisp_h
+#define Lisp_lisp_h
+
+void LispBegin(void);
+void LispEnd(void);
+void *LispExecute(char*);
+void LispMachine(void);
+void LispSetPrompt(char*);
+void LispSetInteractive(int);
+void LispSetExitOnError(int);
+void LispDebug(int); /* argument is boolean to enable/disable */
+
+#endif /* Lisp_lisp_h */
diff --git a/lisp/lsp.c b/lisp/lsp.c
new file mode 100644
index 0000000..920dba0
--- /dev/null
+++ b/lisp/lsp.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/lsp.c,v 1.7 2002/11/23 08:26:49 paulo Exp $ */
+
+#include <stdio.h>
+#include <string.h>
+#include "lisp.h"
+
+#ifdef NEED_STRCASECMP
+int strcasecmp(const char *s1, const char *s2);
+int strncasecmp(const char *s1, const char *s2, size_t n);
+#endif
+#ifdef NEED_REALPATH
+#include <sys/param.h>
+#if defined(ISC)
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+#endif
+char *realpath(const char *pathname, char resolvedname[MAXPATHLEN]);
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+
+ LispBegin();
+
+ i = 1;
+ if (argc > 1 && strcmp(argv[1], "-d") == 0) {
+ LispDebug(1);
+ ++i;
+ }
+
+ if (i < argc) {
+ char buffer[2048];
+
+ for (; i < argc; i++) {
+ snprintf(buffer, sizeof(buffer),
+ "(load \"%s\" :if-does-not-exist :error)",
+ argv[i]);
+ LispExecute(buffer);
+ }
+ }
+ else
+ LispMachine();
+
+ LispEnd();
+
+ return (0);
+}
diff --git a/lisp/math.c b/lisp/math.c
new file mode 100644
index 0000000..fcadefa
--- /dev/null
+++ b/lisp/math.c
@@ -0,0 +1,1473 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/math.c,v 1.22 2002/11/23 21:41:52 paulo Exp $ */
+
+#include "math.h"
+#include "private.h"
+
+/*
+ * Prototypes
+ */
+static LispObj *LispDivide(LispBuiltin*, int, int);
+
+/*
+ * Initialization
+ */
+static LispObj *obj_zero, *obj_one;
+LispObj *Ocomplex, *Oequal_;
+
+LispObj *Oshort_float, *Osingle_float, *Odouble_float, *Olong_float;
+
+Atom_id Sdefault_float_format;
+
+/*
+ * Implementation
+ */
+#include "mathimp.c"
+
+void
+LispMathInit(void)
+{
+ LispObj *object, *result;
+
+ mp_set_malloc(LispMalloc);
+ mp_set_calloc(LispCalloc);
+ mp_set_realloc(LispRealloc);
+ mp_set_free(LispFree);
+
+ number_init();
+ obj_zero = FIXNUM(0);
+ obj_one = FIXNUM(1);
+
+ Oequal_ = STATIC_ATOM("=");
+ Ocomplex = STATIC_ATOM(Scomplex);
+ Oshort_float = STATIC_ATOM("SHORT-FLOAT");
+ LispExportSymbol(Oshort_float);
+ Osingle_float = STATIC_ATOM("SINGLE-FLOAT");
+ LispExportSymbol(Osingle_float);
+ Odouble_float = STATIC_ATOM("DOUBLE-FLOAT");
+ LispExportSymbol(Odouble_float);
+ Olong_float = STATIC_ATOM("LONG-FLOAT");
+ LispExportSymbol(Olong_float);
+
+ object = STATIC_ATOM("*DEFAULT-FLOAT-FORMAT*");
+ LispProclaimSpecial(object, Odouble_float, NIL);
+ LispExportSymbol(object);
+ Sdefault_float_format = ATOMID(object);
+
+ object = STATIC_ATOM("PI");
+ result = number_pi();
+ LispProclaimSpecial(object, result, NIL);
+ LispExportSymbol(object);
+
+ object = STATIC_ATOM("MOST-POSITIVE-FIXNUM");
+ LispDefconstant(object, FIXNUM(MOST_POSITIVE_FIXNUM), NIL);
+ LispExportSymbol(object);
+
+ object = STATIC_ATOM("MOST-NEGATIVE-FIXNUM");
+ LispDefconstant(object, FIXNUM(MOST_NEGATIVE_FIXNUM), NIL);
+ LispExportSymbol(object);
+}
+
+LispObj *
+Lisp_Mul(LispBuiltin *builtin)
+/*
+ * &rest numbers
+ */
+{
+ n_number num;
+ LispObj *number, *numbers;
+
+ numbers = ARGUMENT(0);
+
+ if (CONSP(numbers)) {
+ number = CAR(numbers);
+
+ numbers = CDR(numbers);
+ if (!CONSP(numbers)) {
+ CHECK_NUMBER(number);
+ return (number);
+ }
+ }
+ else
+ return (FIXNUM(1));
+
+ set_number_object(&num, number);
+ do {
+ mul_number_object(&num, CAR(numbers));
+ numbers = CDR(numbers);
+ } while (CONSP(numbers));
+
+ return (make_number_object(&num));
+}
+
+LispObj *
+Lisp_Plus(LispBuiltin *builtin)
+/*
+ + &rest numbers
+ */
+{
+ n_number num;
+ LispObj *number, *numbers;
+
+ numbers = ARGUMENT(0);
+
+ if (CONSP(numbers)) {
+ number = CAR(numbers);
+
+ numbers = CDR(numbers);
+ if (!CONSP(numbers)) {
+ CHECK_NUMBER(number);
+ return (number);
+ }
+ }
+ else
+ return (FIXNUM(0));
+
+ set_number_object(&num, number);
+ do {
+ add_number_object(&num, CAR(numbers));
+ numbers = CDR(numbers);
+ } while (CONSP(numbers));
+
+ return (make_number_object(&num));
+}
+
+LispObj *
+Lisp_Minus(LispBuiltin *builtin)
+/*
+ - number &rest more_numbers
+ */
+{
+ n_number num;
+ LispObj *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ number = ARGUMENT(0);
+
+ set_number_object(&num, number);
+ if (!CONSP(more_numbers)) {
+ neg_number(&num);
+
+ return (make_number_object(&num));
+ }
+ do {
+ sub_number_object(&num, CAR(more_numbers));
+ more_numbers = CDR(more_numbers);
+ } while (CONSP(more_numbers));
+
+ return (make_number_object(&num));
+}
+
+LispObj *
+Lisp_Div(LispBuiltin *builtin)
+/*
+ / number &rest more_numbers
+ */
+{
+ n_number num;
+ LispObj *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ number = ARGUMENT(0);
+
+ if (CONSP(more_numbers))
+ set_number_object(&num, number);
+ else {
+ num.complex = 0;
+ num.real.type = N_FIXNUM;
+ num.real.data.fixnum = 1;
+ goto div_one_argument;
+ }
+
+ for (;;) {
+ number = CAR(more_numbers);
+ more_numbers = CDR(more_numbers);
+
+div_one_argument:
+ div_number_object(&num, number);
+ if (!CONSP(more_numbers))
+ break;
+ }
+
+ return (make_number_object(&num));
+}
+
+LispObj *
+Lisp_OnePlus(LispBuiltin *builtin)
+/*
+ 1+ number
+ */
+{
+ n_number num;
+ LispObj *number;
+
+ number = ARGUMENT(0);
+ num.complex = 0;
+ num.real.type = N_FIXNUM;
+ num.real.data.fixnum = 1;
+ add_number_object(&num, number);
+
+ return (make_number_object(&num));
+}
+
+LispObj *
+Lisp_OneMinus(LispBuiltin *builtin)
+/*
+ 1- number
+ */
+{
+ n_number num;
+ LispObj *number;
+
+ number = ARGUMENT(0);
+ num.complex = 0;
+ num.real.type = N_FIXNUM;
+ num.real.data.fixnum = -1;
+ add_number_object(&num, number);
+
+ return (make_number_object(&num));
+}
+
+LispObj *
+Lisp_Less(LispBuiltin *builtin)
+/*
+ < number &rest more-numbers
+ */
+{
+ LispObj *compare, *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ compare = ARGUMENT(0);
+
+ if (CONSP(more_numbers)) {
+ do {
+ number = CAR(more_numbers);
+ if (cmp_object_object(compare, number, 1) >= 0)
+ return (NIL);
+ compare = number;
+ more_numbers = CDR(more_numbers);
+ } while (CONSP(more_numbers));
+ }
+ else {
+ CHECK_REAL(compare);
+ }
+
+ return (T);
+}
+
+LispObj *
+Lisp_LessEqual(LispBuiltin *builtin)
+/*
+ <= number &rest more-numbers
+ */
+{
+ LispObj *compare, *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ compare = ARGUMENT(0);
+
+ if (CONSP(more_numbers)) {
+ do {
+ number = CAR(more_numbers);
+ if (cmp_object_object(compare, number, 1) > 0)
+ return (NIL);
+ compare = number;
+ more_numbers = CDR(more_numbers);
+ } while (CONSP(more_numbers));
+ }
+ else {
+ CHECK_REAL(compare);
+ }
+
+ return (T);
+}
+
+LispObj *
+Lisp_Equal_(LispBuiltin *builtin)
+/*
+ = number &rest more-numbers
+ */
+{
+ LispObj *compare, *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ compare = ARGUMENT(0);
+
+ if (CONSP(more_numbers)) {
+ do {
+ number = CAR(more_numbers);
+ if (cmp_object_object(compare, number, 0) != 0)
+ return (NIL);
+ compare = number;
+ more_numbers = CDR(more_numbers);
+ } while (CONSP(more_numbers));
+ }
+ else {
+ CHECK_REAL(compare);
+ }
+
+ return (T);
+}
+
+LispObj *
+Lisp_Greater(LispBuiltin *builtin)
+/*
+ > number &rest more-numbers
+ */
+{
+ LispObj *compare, *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ compare = ARGUMENT(0);
+
+ if (CONSP(more_numbers)) {
+ do {
+ number = CAR(more_numbers);
+ if (cmp_object_object(compare, number, 1) <= 0)
+ return (NIL);
+ compare = number;
+ more_numbers = CDR(more_numbers);
+ } while (CONSP(more_numbers));
+ }
+ else {
+ CHECK_REAL(compare);
+ }
+
+ return (T);
+}
+
+LispObj *
+Lisp_GreaterEqual(LispBuiltin *builtin)
+/*
+ >= number &rest more-numbers
+ */
+{
+ LispObj *compare, *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ compare = ARGUMENT(0);
+
+ if (CONSP(more_numbers)) {
+ do {
+ number = CAR(more_numbers);
+ if (cmp_object_object(compare, number, 1) < 0)
+ return (NIL);
+ compare = number;
+ more_numbers = CDR(more_numbers);
+ } while (CONSP(more_numbers));
+ }
+ else {
+ CHECK_REAL(compare);
+ }
+
+ return (T);
+}
+
+LispObj *
+Lisp_NotEqual(LispBuiltin *builtin)
+/*
+ /= number &rest more-numbers
+ */
+{
+ LispObj *object, *compare, *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ number = ARGUMENT(0);
+
+ if (!CONSP(more_numbers)) {
+ CHECK_REAL(number);
+
+ return (T);
+ }
+
+ /* compare all numbers */
+ while (1) {
+ compare = number;
+ for (object = more_numbers; CONSP(object); object = CDR(object)) {
+ number = CAR(object);
+
+ if (cmp_object_object(compare, number, 0) == 0)
+ return (NIL);
+ }
+ if (CONSP(more_numbers)) {
+ number = CAR(more_numbers);
+ more_numbers = CDR(more_numbers);
+ }
+ else
+ break;
+ }
+
+ return (T);
+}
+
+LispObj *
+Lisp_Min(LispBuiltin *builtin)
+/*
+ min number &rest more-numbers
+ */
+{
+ LispObj *result, *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ result = ARGUMENT(0);
+
+ if (CONSP(more_numbers)) {
+ do {
+ number = CAR(more_numbers);
+ if (cmp_object_object(result, number, 1) > 0)
+ result = number;
+ more_numbers = CDR(more_numbers);
+ } while (CONSP(more_numbers));
+ }
+ else {
+ CHECK_REAL(result);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Max(LispBuiltin *builtin)
+/*
+ max number &rest more-numbers
+ */
+{
+ LispObj *result, *number, *more_numbers;
+
+ more_numbers = ARGUMENT(1);
+ result = ARGUMENT(0);
+
+ if (CONSP(more_numbers)) {
+ do {
+ number = CAR(more_numbers);
+ if (cmp_object_object(result, number, 1) < 0)
+ result = number;
+ more_numbers = CDR(more_numbers);
+ } while (CONSP(more_numbers));
+ }
+ else {
+ CHECK_REAL(result);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Abs(LispBuiltin *builtin)
+/*
+ abs number
+ */
+{
+ LispObj *result, *number;
+
+ result = number = ARGUMENT(0);
+
+ switch (OBJECT_TYPE(number)) {
+ case LispFixnum_t:
+ case LispInteger_t:
+ case LispBignum_t:
+ case LispDFloat_t:
+ case LispRatio_t:
+ case LispBigratio_t:
+ if (cmp_real_object(&zero, number) > 0) {
+ n_real real;
+
+ set_real_object(&real, number);
+ neg_real(&real);
+ result = make_real_object(&real);
+ }
+ break;
+ case LispComplex_t: {
+ n_number num;
+
+ set_number_object(&num, number);
+ abs_number(&num);
+ result = make_number_object(&num);
+ } break;
+ default:
+ fatal_builtin_object_error(builtin, number, NOT_A_NUMBER);
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Complex(LispBuiltin *builtin)
+/*
+ complex realpart &optional imagpart
+ */
+{
+ LispObj *realpart, *imagpart;
+
+ imagpart = ARGUMENT(1);
+ realpart = ARGUMENT(0);
+
+ CHECK_REAL(realpart);
+
+ if (imagpart == UNSPEC)
+ return (realpart);
+ else {
+ CHECK_REAL(imagpart);
+ }
+ if (!FLOATP(imagpart) && cmp_real_object(&zero, imagpart) == 0)
+ return (realpart);
+
+ return (COMPLEX(realpart, imagpart));
+}
+
+LispObj *
+Lisp_Complexp(LispBuiltin *builtin)
+/*
+ complexp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (COMPLEXP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Conjugate(LispBuiltin *builtin)
+/*
+ conjugate number
+ */
+{
+ n_number num;
+ LispObj *number, *realpart, *imagpart;
+
+ number = ARGUMENT(0);
+
+ CHECK_NUMBER(number);
+
+ if (REALP(number))
+ return (number);
+
+ realpart = OCXR(number);
+ num.complex = 0;
+ num.real.type = N_FIXNUM;
+ num.real.data.fixnum = -1;
+ mul_number_object(&num, OCXI(number));
+ imagpart = make_number_object(&num);
+
+ return (COMPLEX(realpart, imagpart));
+}
+
+LispObj *
+Lisp_Decf(LispBuiltin *builtin)
+/*
+ decf place &optional delta
+ */
+{
+ n_number num;
+ LispObj *place, *delta, *number;
+
+ delta = ARGUMENT(1);
+ place = ARGUMENT(0);
+
+ if (SYMBOLP(place)) {
+ number = LispGetVar(place);
+ if (number == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
+ }
+ else
+ number = EVAL(place);
+
+ if (delta != UNSPEC) {
+ LispObj *operand;
+
+ operand = EVAL(delta);
+ set_number_object(&num, number);
+ sub_number_object(&num, operand);
+ number = make_number_object(&num);
+ }
+ else {
+ num.complex = 0;
+ num.real.type = N_FIXNUM;
+ num.real.data.fixnum = -1;
+ add_number_object(&num, number);
+ number = make_number_object(&num);
+ }
+
+ if (SYMBOLP(place)) {
+ CHECK_CONSTANT(place);
+ LispSetVar(place, number);
+ }
+ else {
+ GC_ENTER();
+
+ GC_PROTECT(number);
+ (void)APPLY2(Osetf, place, number);
+ GC_LEAVE();
+ }
+
+ return (number);
+}
+
+LispObj *
+Lisp_Denominator(LispBuiltin *builtin)
+/*
+ denominator rational
+ */
+{
+ LispObj *result, *rational;
+
+ rational = ARGUMENT(0);
+
+ switch (OBJECT_TYPE(rational)) {
+ case LispFixnum_t:
+ case LispInteger_t:
+ case LispBignum_t:
+ result = FIXNUM(1);
+ break;
+ case LispRatio_t:
+ result = INTEGER(OFRD(rational));
+ break;
+ case LispBigratio_t:
+ if (mpi_fiti(OBRD(rational)))
+ result = INTEGER(mpi_geti(OBRD(rational)));
+ else {
+ mpi *den = XALLOC(mpi);
+
+ mpi_init(den);
+ mpi_set(den, OBRD(rational));
+ result = BIGNUM(den);
+ }
+ break;
+ default:
+ LispDestroy("%s: %s is not a rational number",
+ STRFUN(builtin), STROBJ(rational));
+ /*NOTREACHED*/
+ result = NIL;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Evenp(LispBuiltin *builtin)
+/*
+ evenp integer
+ */
+{
+ LispObj *result, *integer;
+
+ integer = ARGUMENT(0);
+
+ switch (OBJECT_TYPE(integer)) {
+ case LispFixnum_t:
+ result = FIXNUM_VALUE(integer) % 2 ? NIL : T;
+ break;
+ case LispInteger_t:
+ result = INT_VALUE(integer) % 2 ? NIL : T;
+ break;
+ case LispBignum_t:
+ result = mpi_remi(OBI(integer), 2) ? NIL : T;
+ break;
+ default:
+ fatal_builtin_object_error(builtin, integer, NOT_AN_INTEGER);
+ /*NOTREACHED*/
+ result = NIL;
+ }
+
+ return (result);
+}
+
+/* only one float format */
+LispObj *
+Lisp_Float(LispBuiltin *builtin)
+/*
+ float number &optional other
+ */
+{
+ LispObj *number, *other;
+
+ other = ARGUMENT(1);
+ number = ARGUMENT(0);
+
+ if (other != UNSPEC) {
+ CHECK_DFLOAT(other);
+ }
+
+ return (LispFloatCoerce(builtin, number));
+}
+
+LispObj *
+LispFloatCoerce(LispBuiltin *builtin, LispObj *number)
+{
+ double value;
+
+ switch (OBJECT_TYPE(number)) {
+ case LispFixnum_t:
+ value = FIXNUM_VALUE(number);
+ break;
+ case LispInteger_t:
+ value = INT_VALUE(number);
+ break;
+ case LispBignum_t:
+ value = mpi_getd(OBI(number));
+ break;
+ case LispDFloat_t:
+ return (number);
+ case LispRatio_t:
+ value = (double)OFRN(number) / (double)OFRD(number);
+ break;
+ case LispBigratio_t:
+ value = mpr_getd(OBR(number));
+ break;
+ default:
+ value = 0.0;
+ fatal_builtin_object_error(builtin, number, NOT_A_REAL_NUMBER);
+ break;
+ }
+
+ if (!finite(value))
+ fatal_error(FLOATING_POINT_OVERFLOW);
+
+ return (DFLOAT(value));
+}
+
+LispObj *
+Lisp_Floatp(LispBuiltin *builtin)
+/*
+ floatp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (FLOATP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Gcd(LispBuiltin *builtin)
+/*
+ gcd &rest integers
+ */
+{
+ n_real real;
+ LispObj *integers, *integer, *operand;
+
+ integers = ARGUMENT(0);
+
+ if (!CONSP(integers))
+ return (FIXNUM(0));
+
+ integer = CAR(integers);
+
+ CHECK_INTEGER(integer);
+ set_real_object(&real, integer);
+ integers = CDR(integers);
+
+ for (; CONSP(integers); integers = CDR(integers)) {
+ operand = CAR(integers);
+ gcd_real_object(&real, operand);
+ }
+ abs_real(&real);
+
+ return (make_real_object(&real));
+}
+
+LispObj *
+Lisp_Imagpart(LispBuiltin *builtin)
+/*
+ imagpart number
+ */
+{
+ LispObj *number;
+
+ number = ARGUMENT(0);
+
+ if (COMPLEXP(number))
+ return (OCXI(number));
+ else {
+ CHECK_REAL(number);
+ }
+
+ return (FIXNUM(0));
+}
+
+LispObj *
+Lisp_Incf(LispBuiltin *builtin)
+/*
+ incf place &optional delta
+ */
+{
+ n_number num;
+ LispObj *place, *delta, *number;
+
+ delta = ARGUMENT(1);
+ place = ARGUMENT(0);
+
+ if (SYMBOLP(place)) {
+ number = LispGetVar(place);
+ if (number == NULL)
+ LispDestroy("EVAL: the variable %s is unbound", STROBJ(place));
+ }
+ else
+ number = EVAL(place);
+
+ if (delta != UNSPEC) {
+ LispObj *operand;
+
+ operand = EVAL(delta);
+ set_number_object(&num, number);
+ add_number_object(&num, operand);
+ number = make_number_object(&num);
+ }
+ else {
+ num.complex = 0;
+ num.real.type = N_FIXNUM;
+ num.real.data.fixnum = 1;
+ add_number_object(&num, number);
+ number = make_number_object(&num);
+ }
+
+ if (SYMBOLP(place)) {
+ CHECK_CONSTANT(place);
+ LispSetVar(place, number);
+ }
+ else {
+ GC_ENTER();
+
+ GC_PROTECT(number);
+ (void)APPLY2(Osetf, place, number);
+ GC_LEAVE();
+ }
+
+ return (number);
+}
+
+LispObj *
+Lisp_Integerp(LispBuiltin *builtin)
+/*
+ integerp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (INTEGERP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Isqrt(LispBuiltin *builtin)
+/*
+ isqrt natural
+ */
+{
+ LispObj *natural, *result;
+
+ natural = ARGUMENT(0);
+
+ if (cmp_object_object(natural, obj_zero, 1) < 0)
+ goto not_a_natural_number;
+
+ switch (OBJECT_TYPE(natural)) {
+ case LispFixnum_t:
+ result = FIXNUM((long)floor(sqrt(FIXNUM_VALUE(natural))));
+ break;
+ case LispInteger_t:
+ result = INTEGER((long)floor(sqrt(INT_VALUE(natural))));
+ break;
+ case LispBignum_t: {
+ mpi *bigi;
+
+ bigi = XALLOC(mpi);
+ mpi_init(bigi);
+ mpi_sqrt(bigi, OBI(natural));
+ if (mpi_fiti(bigi)) {
+ result = INTEGER(mpi_geti(bigi));
+ mpi_clear(bigi);
+ XFREE(bigi);
+ }
+ else
+ result = BIGNUM(bigi);
+ } break;
+ default:
+ goto not_a_natural_number;
+ }
+
+ return (result);
+
+not_a_natural_number:
+ LispDestroy("%s: %s is not a natural number",
+ STRFUN(builtin), STROBJ(natural));
+ /*NOTREACHED*/
+ return (NIL);
+}
+
+LispObj *
+Lisp_Lcm(LispBuiltin *builtin)
+/*
+ lcm &rest integers
+ */
+{
+ n_real real, gcd;
+ LispObj *integers, *operand;
+
+ integers = ARGUMENT(0);
+
+ if (!CONSP(integers))
+ return (FIXNUM(1));
+
+ operand = CAR(integers);
+
+ CHECK_INTEGER(operand);
+ set_real_object(&real, operand);
+ integers = CDR(integers);
+
+ gcd.type = N_FIXNUM;
+ gcd.data.fixnum = 0;
+
+ for (; CONSP(integers); integers = CDR(integers)) {
+ operand = CAR(integers);
+
+ if (real.type == N_FIXNUM && real.data.fixnum == 0)
+ break;
+
+ /* calculate gcd before changing integer */
+ clear_real(&gcd);
+ set_real_real(&gcd, &real);
+ gcd_real_object(&gcd, operand);
+
+ /* calculate lcm */
+ mul_real_object(&real, operand);
+ div_real_real(&real, &gcd);
+ }
+ clear_real(&gcd);
+ abs_real(&real);
+
+ return (make_real_object(&real));
+}
+
+LispObj *
+Lisp_Logand(LispBuiltin *builtin)
+/*
+ logand &rest integers
+ */
+{
+ n_real real;
+
+ LispObj *integers;
+
+ integers = ARGUMENT(0);
+
+ real.type = N_FIXNUM;
+ real.data.fixnum = -1;
+
+ for (; CONSP(integers); integers = CDR(integers))
+ and_real_object(&real, CAR(integers));
+
+ return (make_real_object(&real));
+}
+
+LispObj *
+Lisp_Logeqv(LispBuiltin *builtin)
+/*
+ logeqv &rest integers
+ */
+{
+ n_real real;
+
+ LispObj *integers;
+
+ integers = ARGUMENT(0);
+
+ real.type = N_FIXNUM;
+ real.data.fixnum = -1;
+
+ for (; CONSP(integers); integers = CDR(integers))
+ eqv_real_object(&real, CAR(integers));
+
+ return (make_real_object(&real));
+}
+
+LispObj *
+Lisp_Logior(LispBuiltin *builtin)
+/*
+ logior &rest integers
+ */
+{
+ n_real real;
+
+ LispObj *integers;
+
+ integers = ARGUMENT(0);
+
+ real.type = N_FIXNUM;
+ real.data.fixnum = 0;
+
+ for (; CONSP(integers); integers = CDR(integers))
+ ior_real_object(&real, CAR(integers));
+
+ return (make_real_object(&real));
+}
+
+LispObj *
+Lisp_Lognot(LispBuiltin *builtin)
+/*
+ lognot integer
+ */
+{
+ n_real real;
+
+ LispObj *integer;
+
+ integer = ARGUMENT(0);
+
+ CHECK_INTEGER(integer);
+
+ set_real_object(&real, integer);
+ not_real(&real);
+
+ return (make_real_object(&real));
+}
+
+LispObj *
+Lisp_Logxor(LispBuiltin *builtin)
+/*
+ logxor &rest integers
+ */
+{
+ n_real real;
+
+ LispObj *integers;
+
+ integers = ARGUMENT(0);
+
+ real.type = N_FIXNUM;
+ real.data.fixnum = 0;
+
+ for (; CONSP(integers); integers = CDR(integers))
+ xor_real_object(&real, CAR(integers));
+
+ return (make_real_object(&real));
+}
+
+LispObj *
+Lisp_Minusp(LispBuiltin *builtin)
+/*
+ minusp number
+ */
+{
+ LispObj *number;
+
+ number = ARGUMENT(0);
+
+ CHECK_REAL(number);
+
+ return (cmp_real_object(&zero, number) > 0 ? T : NIL);
+}
+
+LispObj *
+Lisp_Mod(LispBuiltin *builtin)
+/*
+ mod number divisor
+ */
+{
+ LispObj *result;
+
+ LispObj *number, *divisor;
+
+ divisor = ARGUMENT(1);
+ number = ARGUMENT(0);
+
+ if (INTEGERP(number) && INTEGERP(divisor)) {
+ n_real real;
+
+ set_real_object(&real, number);
+ mod_real_object(&real, divisor);
+ result = make_real_object(&real);
+ }
+ else {
+ n_number num;
+
+ set_number_object(&num, number);
+ divide_number_object(&num, divisor, NDIVIDE_FLOOR, 0);
+ result = make_real_object(&(num.imag));
+ clear_real(&(num.real));
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Numberp(LispBuiltin *builtin)
+/*
+ numberp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (NUMBERP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Numerator(LispBuiltin *builtin)
+/*
+ numerator rational
+ */
+{
+ LispObj *result, *rational;
+
+ rational = ARGUMENT(0);
+
+ switch (OBJECT_TYPE(rational)) {
+ case LispFixnum_t:
+ case LispInteger_t:
+ case LispBignum_t:
+ result = rational;
+ break;
+ case LispRatio_t:
+ result = INTEGER(OFRN(rational));
+ break;
+ case LispBigratio_t:
+ if (mpi_fiti(OBRN(rational)))
+ result = INTEGER(mpi_geti(OBRN(rational)));
+ else {
+ mpi *num = XALLOC(mpi);
+
+ mpi_init(num);
+ mpi_set(num, OBRN(rational));
+ result = BIGNUM(num);
+ }
+ break;
+ default:
+ LispDestroy("%s: %s is not a rational number",
+ STRFUN(builtin), STROBJ(rational));
+ /*NOTREACHED*/
+ result = NIL;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Oddp(LispBuiltin *builtin)
+/*
+ oddp integer
+ */
+{
+ LispObj *result, *integer;
+
+ integer = ARGUMENT(0);
+
+ switch (OBJECT_TYPE(integer)) {
+ case LispFixnum_t:
+ result = FIXNUM_VALUE(integer) % 2 ? T : NIL;
+ break;
+ case LispInteger_t:
+ result = INT_VALUE(integer) % 2 ? T : NIL;
+ break;
+ case LispBignum_t:
+ result = mpi_remi(OBI(integer), 2) ? T : NIL;
+ break;
+ default:
+ fatal_builtin_object_error(builtin, integer, NOT_AN_INTEGER);
+ /*NOTREACHED*/
+ result = NIL;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Plusp(LispBuiltin *builtin)
+/*
+ plusp number
+ */
+{
+ LispObj *number;
+
+ number = ARGUMENT(0);
+
+ CHECK_REAL(number);
+
+ return (cmp_real_object(&zero, number) < 0 ? T : NIL);
+}
+
+LispObj *
+Lisp_Rational(LispBuiltin *builtin)
+/*
+ rational number
+ */
+{
+ LispObj *number;
+
+ number = ARGUMENT(0);
+
+ if (DFLOATP(number)) {
+ double numerator = ODF(number);
+
+ if ((long)numerator == numerator)
+ number = INTEGER(numerator);
+ else {
+ n_real real;
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ mpr_setd(bigr, numerator);
+ real.type = N_BIGRATIO;
+ real.data.bigratio = bigr;
+ rbr_canonicalize(&real);
+ number = make_real_object(&real);
+ }
+ }
+ else {
+ CHECK_REAL(number);
+ }
+
+ return (number);
+}
+
+LispObj *
+Lisp_Rationalp(LispBuiltin *builtin)
+/*
+ rationalp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (RATIONALP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_Realpart(LispBuiltin *builtin)
+/*
+ realpart number
+ */
+{
+ LispObj *number;
+
+ number = ARGUMENT(0);
+
+ if (COMPLEXP(number))
+ return (OCXR(number));
+ else {
+ CHECK_REAL(number);
+ }
+
+ return (number);
+}
+
+LispObj *
+Lisp_Rem(LispBuiltin *builtin)
+/*
+ rem number divisor
+ */
+{
+ LispObj *result;
+
+ LispObj *number, *divisor;
+
+ divisor = ARGUMENT(1);
+ number = ARGUMENT(0);
+
+ if (INTEGERP(number) && INTEGERP(divisor)) {
+ n_real real;
+
+ set_real_object(&real, number);
+ rem_real_object(&real, divisor);
+ result = make_real_object(&real);
+ }
+ else {
+ n_number num;
+
+ set_number_object(&num, number);
+ divide_number_object(&num, divisor, NDIVIDE_TRUNC, 0);
+ result = make_real_object(&(num.imag));
+ clear_real(&(num.real));
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Sqrt(LispBuiltin *builtin)
+/*
+ sqrt number
+ */
+{
+ n_number num;
+ LispObj *number;
+
+ number = ARGUMENT(0);
+
+ set_number_object(&num, number);
+ sqrt_number(&num);
+
+ return (make_number_object(&num));
+}
+
+LispObj *
+Lisp_Zerop(LispBuiltin *builtin)
+/*
+ zerop number
+ */
+{
+ LispObj *result, *number;
+
+ number = ARGUMENT(0);
+
+ switch (OBJECT_TYPE(number)) {
+ case LispFixnum_t:
+ case LispInteger_t:
+ case LispBignum_t:
+ case LispDFloat_t:
+ case LispRatio_t:
+ case LispBigratio_t:
+ result = cmp_real_object(&zero, number) == 0 ? T : NIL;
+ break;
+ case LispComplex_t:
+ result = cmp_real_object(&zero, OCXR(number)) == 0 &&
+ cmp_real_object(&zero, OCXI(number)) == 0 ? T : NIL;
+ break;
+ default:
+ fatal_builtin_object_error(builtin, number, NOT_A_NUMBER);
+ /*NOTREACHED*/
+ result = NIL;
+ }
+
+ return (result);
+}
+
+static LispObj *
+LispDivide(LispBuiltin *builtin, int fun, int flo)
+{
+ n_number num;
+ LispObj *number, *divisor;
+
+ divisor = ARGUMENT(1);
+ number = ARGUMENT(0);
+
+ RETURN_COUNT = 1;
+
+ if (cmp_real_object(&zero, number) == 0) {
+ if (divisor != NIL) {
+ CHECK_REAL(divisor);
+ }
+
+ return (RETURN(0) = obj_zero);
+ }
+
+ if (divisor == UNSPEC)
+ divisor = obj_one;
+
+ set_number_object(&num, number);
+ if (num.complex)
+ fatal_builtin_object_error(builtin, divisor, NOT_A_REAL_NUMBER);
+
+ divide_number_object(&num, divisor, fun, flo);
+ RETURN(0) = make_real_object(&(num.imag));
+
+ return (make_real_object(&(num.real)));
+}
+
+LispObj *
+Lisp_Ceiling(LispBuiltin *builtin)
+/*
+ ceiling number &optional divisor
+ */
+{
+ return (LispDivide(builtin, NDIVIDE_CEIL, 0));
+}
+
+LispObj *
+Lisp_Fceiling(LispBuiltin *builtin)
+/*
+ fceiling number &optional divisor
+ */
+{
+ return (LispDivide(builtin, NDIVIDE_CEIL, 1));
+}
+
+LispObj *
+Lisp_Floor(LispBuiltin *builtin)
+/*
+ floor number &optional divisor
+ */
+{
+ return (LispDivide(builtin, NDIVIDE_FLOOR, 0));
+}
+
+LispObj *
+Lisp_Ffloor(LispBuiltin *builtin)
+/*
+ ffloor number &optional divisor
+ */
+{
+ return (LispDivide(builtin, NDIVIDE_FLOOR, 1));
+}
+
+LispObj *
+Lisp_Round(LispBuiltin *builtin)
+/*
+ round number &optional divisor
+ */
+{
+ return (LispDivide(builtin, NDIVIDE_ROUND, 0));
+}
+
+LispObj *
+Lisp_Fround(LispBuiltin *builtin)
+/*
+ fround number &optional divisor
+ */
+{
+ return (LispDivide(builtin, NDIVIDE_ROUND, 1));
+}
+
+LispObj *
+Lisp_Truncate(LispBuiltin *builtin)
+/*
+ truncate number &optional divisor
+ */
+{
+ return (LispDivide(builtin, NDIVIDE_TRUNC, 0));
+}
+
+LispObj *
+Lisp_Ftruncate(LispBuiltin *builtin)
+/*
+ ftruncate number &optional divisor
+ */
+{
+ return (LispDivide(builtin, NDIVIDE_TRUNC, 1));
+}
diff --git a/lisp/math.h b/lisp/math.h
new file mode 100644
index 0000000..8297d43
--- /dev/null
+++ b/lisp/math.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/math.h,v 1.6 2002/11/08 08:00:57 paulo Exp $ */
+
+#ifndef Lisp_math_h
+#define Lisp_math_h
+
+#include "internal.h"
+#include "mp.h"
+
+void LispMathInit(void);
+LispObj *LispFloatCoerce(LispBuiltin*, LispObj*);
+
+LispObj *Lisp_Mul(LispBuiltin*);
+LispObj *Lisp_Plus(LispBuiltin*);
+LispObj *Lisp_Minus(LispBuiltin*);
+LispObj *Lisp_Div(LispBuiltin*);
+LispObj *Lisp_OnePlus(LispBuiltin*);
+LispObj *Lisp_OneMinus(LispBuiltin*);
+LispObj *Lisp_Less(LispBuiltin*);
+LispObj *Lisp_LessEqual(LispBuiltin*);
+LispObj *Lisp_Equal_(LispBuiltin*);
+LispObj *Lisp_Greater(LispBuiltin*);
+LispObj *Lisp_GreaterEqual(LispBuiltin*);
+LispObj *Lisp_NotEqual(LispBuiltin*);
+LispObj *Lisp_Max(LispBuiltin*);
+LispObj *Lisp_Min(LispBuiltin*);
+LispObj *Lisp_Mod(LispBuiltin*);
+LispObj *Lisp_Abs(LispBuiltin*);
+LispObj *Lisp_Complex(LispBuiltin*);
+LispObj *Lisp_Complexp(LispBuiltin*);
+LispObj *Lisp_Conjugate(LispBuiltin*);
+LispObj *Lisp_Decf(LispBuiltin*);
+LispObj *Lisp_Denominator(LispBuiltin*);
+LispObj *Lisp_Evenp(LispBuiltin*);
+LispObj *Lisp_Float(LispBuiltin*);
+LispObj *Lisp_Floatp(LispBuiltin*);
+LispObj *Lisp_Gcd(LispBuiltin*);
+LispObj *Lisp_Imagpart(LispBuiltin*);
+LispObj *Lisp_Incf(LispBuiltin*);
+LispObj *Lisp_Integerp(LispBuiltin*);
+LispObj *Lisp_Isqrt(LispBuiltin*);
+LispObj *Lisp_Lcm(LispBuiltin*);
+LispObj *Lisp_Logand(LispBuiltin*);
+LispObj *Lisp_Logeqv(LispBuiltin*);
+LispObj *Lisp_Logior(LispBuiltin*);
+LispObj *Lisp_Lognot(LispBuiltin*);
+LispObj *Lisp_Logxor(LispBuiltin*);
+LispObj *Lisp_Minusp(LispBuiltin*);
+LispObj *Lisp_Numberp(LispBuiltin*);
+LispObj *Lisp_Numerator(LispBuiltin*);
+LispObj *Lisp_Oddp(LispBuiltin*);
+LispObj *Lisp_Plusp(LispBuiltin*);
+LispObj *Lisp_Rational(LispBuiltin*);
+#if 0
+LispObj *Lisp_Rationalize(LispBuiltin*);
+#endif
+LispObj *Lisp_Rationalp(LispBuiltin*);
+LispObj *Lisp_Realpart(LispBuiltin*);
+LispObj *Lisp_Rem(LispBuiltin*);
+LispObj *Lisp_Sqrt(LispBuiltin*);
+LispObj *Lisp_Zerop(LispBuiltin*);
+LispObj *Lisp_Ceiling(LispBuiltin*);
+LispObj *Lisp_Fceiling(LispBuiltin*);
+LispObj *Lisp_Floor(LispBuiltin*);
+LispObj *Lisp_Ffloor(LispBuiltin*);
+LispObj *Lisp_Round(LispBuiltin*);
+LispObj *Lisp_Fround(LispBuiltin*);
+LispObj *Lisp_Truncate(LispBuiltin*);
+LispObj *Lisp_Ftruncate(LispBuiltin*);
+
+
+#endif /* Lisp_math_h */
diff --git a/lisp/mathimp.c b/lisp/mathimp.c
new file mode 100644
index 0000000..ccda576
--- /dev/null
+++ b/lisp/mathimp.c
@@ -0,0 +1,5225 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/mathimp.c,v 1.14 2003/01/30 02:46:25 paulo Exp $ */
+
+
+/*
+ * Defines
+ */
+#ifdef __GNUC__
+#define CONST __attribute__ ((__const__))
+#else
+#define CONST /**/
+#endif
+
+/* mask for checking overflow on long operations */
+#ifdef LONG64
+#define FI_MASK 0x4000000000000000L
+#define LONGSBITS 63
+#else
+#define FI_MASK 0x40000000L
+#define LONGSBITS 31
+#endif
+
+#define N_FIXNUM 1
+#define N_BIGNUM 2
+#define N_FLONUM 3
+#define N_FIXRATIO 4
+#define N_BIGRATIO 5
+
+#define NOP_ADD 1
+#define NOP_SUB 2
+#define NOP_MUL 3
+#define NOP_DIV 4
+
+#define NDIVIDE_CEIL 1
+#define NDIVIDE_FLOOR 2
+#define NDIVIDE_ROUND 3
+#define NDIVIDE_TRUNC 4
+
+/* real part from number */
+#define NREAL(num) &((num)->real)
+#define NRTYPE(num) (num)->real.type
+#define NRFI(num) (num)->real.data.fixnum
+#define NRBI(num) (num)->real.data.bignum
+#define NRFF(num) (num)->real.data.flonum
+#define NRFRN(Num) (Num)->real.data.fixratio.num
+#define NRFRD(num) (num)->real.data.fixratio.den
+#define NRBR(num) (num)->real.data.bigratio
+#define NRBRN(num) mpr_num(NRBR(num))
+#define NRBRD(num) mpr_den(NRBR(num))
+
+#define NRCLEAR_BI(num) mpi_clear(NRBI(num)); XFREE(NRBI(num))
+#define NRCLEAR_BR(num) mpr_clear(NRBR(num)); XFREE(NRBR(num))
+
+/* imag part from number */
+#define NIMAG(num) &((num)->imag)
+#define NITYPE(num) (num)->imag.type
+#define NIFI(num) (num)->imag.data.fixnum
+#define NIBI(num) (num)->imag.data.bignum
+#define NIFF(num) (num)->imag.data.flonum
+#define NIFRN(Num) (Num)->imag.data.fixratio.num
+#define NIFRD(num) (num)->imag.data.fixratio.den
+#define NIBR(num) (num)->imag.data.bigratio
+#define NIBRN(obj) mpr_num(NIBR(obj))
+#define NIBRD(obj) mpr_den(NIBR(obj))
+
+/* real number fields */
+#define RTYPE(real) (real)->type
+#define RFI(real) (real)->data.fixnum
+#define RBI(real) (real)->data.bignum
+#define RFF(real) (real)->data.flonum
+#define RFRN(real) (real)->data.fixratio.num
+#define RFRD(real) (real)->data.fixratio.den
+#define RBR(real) (real)->data.bigratio
+#define RBRN(real) mpr_num(RBR(real))
+#define RBRD(real) mpr_den(RBR(real))
+
+#define RINTEGERP(real) \
+ (RTYPE(real) == N_FIXNUM || RTYPE(real) == N_BIGNUM)
+
+#define RCLEAR_BI(real) mpi_clear(RBI(real)); XFREE(RBI(real))
+#define RCLEAR_BR(real) mpr_clear(RBR(real)); XFREE(RBR(real))
+
+/* numeric value from lisp object */
+#define OFI(object) FIXNUM_VALUE(object)
+#define OII(object) INT_VALUE(object)
+#define OBI(object) (object)->data.mp.integer
+#define ODF(object) DFLOAT_VALUE(object)
+#define OFRN(object) (object)->data.ratio.numerator
+#define OFRD(object) (object)->data.ratio.denominator
+#define OBR(object) (object)->data.mp.ratio
+#define OBRN(object) mpr_num(OBR(object))
+#define OBRD(object) mpr_den(OBR(object))
+#define OCXR(object) (object)->data.complex.real
+#define OCXI(object) (object)->data.complex.imag
+
+#define XALLOC(type) LispMalloc(sizeof(type))
+#define XFREE(ptr) LispFree(ptr)
+
+
+/*
+ * Types
+ */
+typedef struct _n_real {
+ char type;
+ union {
+ long fixnum;
+ mpi *bignum;
+ double flonum;
+ struct {
+ long num;
+ long den;
+ } fixratio;
+ mpr *bigratio;
+ } data;
+} n_real;
+
+typedef struct _n_number {
+ char complex;
+ n_real real;
+ n_real imag;
+} n_number;
+
+
+/*
+ * Prototypes
+ */
+static void number_init(void);
+static LispObj *number_pi(void);
+
+static void set_real_real(n_real*, n_real*);
+static void set_real_object(n_real*, LispObj*);
+static void set_number_object(n_number*, LispObj*);
+static void clear_real(n_real*);
+static void clear_number(n_number*);
+
+static LispObj *make_real_object(n_real*);
+static LispObj *make_number_object(n_number*);
+
+static void fatal_error(int);
+static void fatal_object_error(LispObj*, int);
+static void fatal_builtin_object_error(LispBuiltin*, LispObj*, int);
+
+static double bi_getd(mpi*);
+static double br_getd(mpr*);
+
+/* add */
+static void add_real_object(n_real*, LispObj*);
+static void add_number_object(n_number*, LispObj*);
+
+/* sub */
+static void sub_real_object(n_real*, LispObj*);
+static void sub_number_object(n_number*, LispObj*);
+
+/* mul */
+static void mul_real_object(n_real*, LispObj*);
+static void mul_number_object(n_number*, LispObj*);
+
+/* div */
+static void div_real_object(n_real*, LispObj*);
+static void div_number_object(n_number*, LispObj*);
+
+/* compare */
+static int cmp_real_real(n_real*, n_real*);
+static int cmp_real_object(n_real*, LispObj*);
+#if 0 /* not used */
+static int cmp_number_object(n_number*, LispObj*);
+#endif
+static int cmp_object_object(LispObj*, LispObj*, int);
+
+/* fixnum */
+static INLINE int fi_fi_add_overflow(long, long) CONST;
+static INLINE int fi_fi_sub_overflow(long, long) CONST;
+static INLINE int fi_fi_mul_overflow(long, long) CONST;
+
+/* bignum */
+static void rbi_canonicalize(n_real*);
+
+/* ratio */
+static void rfr_canonicalize(n_real*);
+static void rbr_canonicalize(n_real*);
+
+/* complex */
+static void ncx_canonicalize(n_number*);
+
+/* abs */
+static void abs_real(n_real*);
+static void abs_number(n_number*);
+static void nabs_cx(n_number*);
+static INLINE void rabs_fi(n_real*);
+static INLINE void rabs_bi(n_real*);
+static INLINE void rabs_ff(n_real*);
+static INLINE void rabs_fr(n_real*);
+static INLINE void rabs_br(n_real*);
+
+/* neg */
+static void neg_real(n_real*);
+static void neg_number(n_number*);
+static void rneg_fi(n_real*);
+static INLINE void rneg_bi(n_real*);
+static INLINE void rneg_ff(n_real*);
+static INLINE void rneg_fr(n_real*);
+static INLINE void rneg_br(n_real*);
+
+/* sqrt */
+static void sqrt_real(n_real*);
+static void sqrt_number(n_number*);
+static void rsqrt_xi(n_real*);
+static void rsqrt_xr(n_real*);
+static void rsqrt_ff(n_real*);
+static void nsqrt_cx(n_number*);
+static void nsqrt_xi(n_number*);
+static void nsqrt_ff(n_number*);
+static void nsqrt_xr(n_number*);
+
+/* mod */
+static void mod_real_real(n_real*, n_real*);
+static void mod_real_object(n_real*, LispObj*);
+static void rmod_fi_fi(n_real*, long);
+static void rmod_fi_bi(n_real*, mpi*);
+static void rmod_bi_fi(n_real*, long);
+static void rmod_bi_bi(n_real*, mpi*);
+
+/* rem */
+static void rem_real_object(n_real*, LispObj*);
+static void rrem_fi_fi(n_real*, long);
+static void rrem_fi_bi(n_real*, mpi*);
+static void rrem_bi_fi(n_real*, long);
+static void rrem_bi_bi(n_real*, mpi*);
+
+/* gcd */
+static void gcd_real_object(n_real*, LispObj*);
+
+/* and */
+static void and_real_object(n_real*, LispObj*);
+
+/* eqv */
+static void eqv_real_object(n_real*, LispObj*);
+
+/* ior */
+static void ior_real_object(n_real*, LispObj*);
+
+/* not */
+static void not_real(n_real*);
+
+/* xor */
+static void xor_real_object(n_real*, LispObj*);
+
+/* divide */
+static void divide_number_object(n_number*, LispObj*, int, int);
+static void ndivide_xi_xi(n_number*, LispObj*, int, int);
+static void ndivide_flonum(n_number*, double, double, int, int);
+static void ndivide_xi_xr(n_number*, LispObj*, int, int);
+static void ndivide_xr_xi(n_number*, LispObj*, int, int);
+static void ndivide_xr_xr(n_number*, LispObj*, int, int);
+
+/* real complex */
+static void nadd_re_cx(n_number*, LispObj*);
+static void nsub_re_cx(n_number*, LispObj*);
+static void nmul_re_cx(n_number*, LispObj*);
+static void ndiv_re_cx(n_number*, LispObj*);
+
+/* complex real */
+static void nadd_cx_re(n_number*, LispObj*);
+static void nsub_cx_re(n_number*, LispObj*);
+static void nmul_cx_re(n_number*, LispObj*);
+static void ndiv_cx_re(n_number*, LispObj*);
+
+/* complex complex */
+static void nadd_cx_cx(n_number*, LispObj*);
+static void nsub_cx_cx(n_number*, LispObj*);
+static void nmul_cx_cx(n_number*, LispObj*);
+static void ndiv_cx_cx(n_number*, LispObj*);
+static int cmp_cx_cx(LispObj*, LispObj*);
+
+/* flonum flonum */
+static void radd_flonum(n_real*, double, double);
+static void rsub_flonum(n_real*, double, double);
+static void rmul_flonum(n_real*, double, double);
+static void rdiv_flonum(n_real*, double, double);
+static int cmp_flonum(double, double);
+
+/* fixnum fixnum */
+static void rop_fi_fi_bi(n_real*, long, int);
+static INLINE void radd_fi_fi(n_real*, long);
+static INLINE void rsub_fi_fi(n_real*, long);
+static INLINE void rmul_fi_fi(n_real*, long);
+static INLINE void rdiv_fi_fi(n_real*, long);
+static INLINE int cmp_fi_fi(long, long);
+static void ndivide_fi_fi(n_number*, long, int, int);
+
+/* fixnum bignum */
+static void rop_fi_bi_xi(n_real*, mpi*, int);
+static INLINE void radd_fi_bi(n_real*, mpi*);
+static INLINE void rsub_fi_bi(n_real*, mpi*);
+static INLINE void rmul_fi_bi(n_real*, mpi*);
+static void rdiv_fi_bi(n_real*, mpi*);
+static INLINE int cmp_fi_bi(long, mpi*);
+
+/* fixnum fixratio */
+static void rop_fi_fr_as_xr(n_real*, long, long, int);
+static void rop_fi_fr_md_xr(n_real*, long, long, int);
+static INLINE void radd_fi_fr(n_real*, long, long);
+static INLINE void rsub_fi_fr(n_real*, long, long);
+static INLINE void rmul_fi_fr(n_real*, long, long);
+static INLINE void rdiv_fi_fr(n_real*, long, long);
+static INLINE int cmp_fi_fr(long, long, long);
+
+/* fixnum bigratio */
+static void rop_fi_br_as_xr(n_real*, mpr*, int);
+static void rop_fi_br_md_xr(n_real*, mpr*, int);
+static INLINE void radd_fi_br(n_real*, mpr*);
+static INLINE void rsub_fi_br(n_real*, mpr*);
+static INLINE void rmul_fi_br(n_real*, mpr*);
+static INLINE void rdiv_fi_br(n_real*, mpr*);
+static INLINE int cmp_fi_br(long, mpr*);
+
+/* bignum fixnum */
+static INLINE void radd_bi_fi(n_real*, long);
+static INLINE void rsub_bi_fi(n_real*, long);
+static INLINE void rmul_bi_fi(n_real*, long);
+static void rdiv_bi_fi(n_real*, long);
+static INLINE int cmp_bi_fi(mpi*, long);
+
+/* bignum bignum */
+static INLINE void radd_bi_bi(n_real*, mpi*);
+static INLINE void rsub_bi_bi(n_real*, mpi*);
+static INLINE void rmul_bi_bi(n_real*, mpi*);
+static void rdiv_bi_bi(n_real*, mpi*);
+static INLINE int cmp_bi_bi(mpi*, mpi*);
+
+/* bignum fixratio */
+static void rop_bi_fr_as_xr(n_real*, long, long, int);
+static void rop_bi_fr_md_xr(n_real*, long, long, int);
+static INLINE void radd_bi_fr(n_real*, long, long);
+static INLINE void rsub_bi_fr(n_real*, long, long);
+static INLINE void rmul_bi_fr(n_real*, long, long);
+static INLINE void rdiv_bi_fr(n_real*, long, long);
+static int cmp_bi_fr(mpi*, long, long);
+
+/* bignum bigratio */
+static void rop_bi_br_as_xr(n_real*, mpr*, int);
+static void rop_bi_br_md_xr(n_real*, mpr*, int);
+static INLINE void radd_bi_br(n_real*, mpr*);
+static INLINE void rsub_bi_br(n_real*, mpr*);
+static INLINE void rmul_bi_br(n_real*, mpr*);
+static INLINE void rdiv_bi_br(n_real*, mpr*);
+static int cmp_bi_br(mpi*, mpr*);
+
+/* fixratio fixnum */
+static void rop_fr_fi_as_xr(n_real*, long, int);
+static void rop_fr_fi_md_xr(n_real*, long, int);
+static INLINE void radd_fr_fi(n_real*, long);
+static INLINE void rsub_fr_fi(n_real*, long);
+static INLINE void rmul_fr_fi(n_real*, long);
+static INLINE void rdiv_fr_fi(n_real*, long);
+static INLINE int cmp_fr_fi(long, long, long);
+
+/* fixratio bignum */
+static void rop_fr_bi_as_xr(n_real*, mpi*, int);
+static void rop_fr_bi_md_xr(n_real*, mpi*, int);
+static INLINE void radd_fr_bi(n_real*, mpi*);
+static INLINE void rsub_fr_bi(n_real*, mpi*);
+static INLINE void rmul_fr_bi(n_real*, mpi*);
+static INLINE void rdiv_fr_bi(n_real*, mpi*);
+static int cmp_fr_bi(long, long, mpi*);
+
+/* fixratio fixratio */
+static void rop_fr_fr_as_xr(n_real*, long, long, int);
+static void rop_fr_fr_md_xr(n_real*, long, long, int);
+static INLINE void radd_fr_fr(n_real*, long, long);
+static INLINE void rsub_fr_fr(n_real*, long, long);
+static INLINE void rmul_fr_fr(n_real*, long, long);
+static INLINE void rdiv_fr_fr(n_real*, long, long);
+static INLINE int cmp_fr_fr(long, long, long, long);
+
+/* fixratio bigratio */
+static void rop_fr_br_asmd_xr(n_real*, mpr*, int);
+static INLINE void radd_fr_br(n_real*, mpr*);
+static INLINE void rsub_fr_br(n_real*, mpr*);
+static INLINE void rmul_fr_br(n_real*, mpr*);
+static INLINE void rdiv_fr_br(n_real*, mpr*);
+static int cmp_fr_br(long, long, mpr*);
+
+/* bigratio fixnum */
+static void rop_br_fi_asmd_xr(n_real*, long, int);
+static INLINE void radd_br_fi(n_real*, long);
+static INLINE void rsub_br_fi(n_real*, long);
+static INLINE void rmul_br_fi(n_real*, long);
+static INLINE void rdiv_br_fi(n_real*, long);
+static int cmp_br_fi(mpr*, long);
+
+/* bigratio bignum */
+static void rop_br_bi_as_xr(n_real*, mpi*, int);
+static INLINE void radd_br_bi(n_real*, mpi*);
+static INLINE void rsub_br_bi(n_real*, mpi*);
+static INLINE void rmul_br_bi(n_real*, mpi*);
+static INLINE void rdiv_br_bi(n_real*, mpi*);
+static int cmp_br_bi(mpr*, mpi*);
+
+/* bigratio fixratio */
+static void rop_br_fr_asmd_xr(n_real*, long, long, int);
+static INLINE void radd_br_fr(n_real*, long, long);
+static INLINE void rsub_br_fr(n_real*, long, long);
+static INLINE void rmul_br_fr(n_real*, long, long);
+static INLINE void rdiv_br_fr(n_real*, long, long);
+static int cmp_br_fr(mpr*, long, long);
+
+/* bigratio bigratio */
+static INLINE void radd_br_br(n_real*, mpr*);
+static INLINE void rsub_br_br(n_real*, mpr*);
+static INLINE void rmul_br_br(n_real*, mpr*);
+static INLINE void rdiv_br_br(n_real*, mpr*);
+static INLINE int cmp_br_br(mpr*, mpr*);
+
+/*
+ * Initialization
+ */
+static n_real zero, one, two;
+
+static char *fatal_error_strings[] = {
+#define DIVIDE_BY_ZERO 0
+ "divide by zero",
+#define FLOATING_POINT_OVERFLOW 1
+ "floating point overflow",
+#define FLOATING_POINT_EXCEPTION 2
+ "floating point exception"
+};
+
+static char *fatal_object_error_strings[] = {
+#define NOT_A_NUMBER 0
+ "is not a number",
+#define NOT_A_REAL_NUMBER 1
+ "is not a real number",
+#define NOT_AN_INTEGER 2
+ "is not an integer"
+};
+
+/*
+ * Implementation
+ */
+static void
+fatal_error(int num)
+{
+ LispDestroy(fatal_error_strings[num]);
+}
+
+static void
+fatal_object_error(LispObj *obj, int num)
+{
+ LispDestroy("%s %s", STROBJ(obj), fatal_object_error_strings[num]);
+}
+
+static void
+fatal_builtin_object_error(LispBuiltin *builtin, LispObj *obj, int num)
+{
+ LispDestroy("%s: %s %s", STRFUN(builtin), STROBJ(obj),
+ fatal_object_error_strings[num]);
+}
+
+static void
+number_init(void)
+{
+ zero.type = one.type = two.type = N_FIXNUM;
+ zero.data.fixnum = 0;
+ one.data.fixnum = 1;
+ two.data.fixnum = 2;
+}
+
+static double
+bi_getd(mpi *bignum)
+{
+ double value = mpi_getd(bignum);
+
+ if (!finite(value))
+ fatal_error(FLOATING_POINT_EXCEPTION);
+
+ return (value);
+}
+
+static double
+br_getd(mpr *bigratio)
+{
+ double value = mpr_getd(bigratio);
+
+ if (!finite(value))
+ fatal_error(FLOATING_POINT_EXCEPTION);
+
+ return (value);
+}
+
+static LispObj *
+number_pi(void)
+{
+ LispObj *result;
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+ result = DFLOAT(M_PI);
+
+ return (result);
+}
+
+static void
+set_real_real(n_real *real, n_real *val)
+{
+ switch (RTYPE(real) = RTYPE(val)) {
+ case N_FIXNUM:
+ RFI(real) = RFI(val);
+ break;
+ case N_BIGNUM:
+ RBI(real) = XALLOC(mpi);
+ mpi_init(RBI(real));
+ mpi_set(RBI(real), RBI(val));
+ break;
+ case N_FLONUM:
+ RFF(real) = RFF(val);
+ break;
+ case N_FIXRATIO:
+ RFRN(real) = RFRN(val);
+ RFRD(real) = RFRD(val);
+ break;
+ case N_BIGRATIO:
+ RBR(real) = XALLOC(mpr);
+ mpr_init(RBR(real));
+ mpr_set(RBR(real), RBR(val));
+ break;
+ }
+}
+
+static void
+set_real_object(n_real *real, LispObj *obj)
+{
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ RTYPE(real) = N_FIXNUM;
+ RFI(real) = OFI(obj);
+ break;
+ case LispInteger_t:
+ RTYPE(real) = N_FIXNUM;
+ RFI(real) = OII(obj);
+ break;
+ case LispBignum_t:
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = XALLOC(mpi);
+ mpi_init(RBI(real));
+ mpi_set(RBI(real), OBI(obj));
+ break;
+ case LispDFloat_t:
+ RTYPE(real) = N_FLONUM;
+ RFF(real) = ODF(obj);
+ break;
+ case LispRatio_t:
+ RTYPE(real) = N_FIXRATIO;
+ RFRN(real) = OFRN(obj);
+ RFRD(real) = OFRD(obj);
+ break;
+ case LispBigratio_t:
+ RTYPE(real) = N_BIGRATIO;
+ RBR(real) = XALLOC(mpr);
+ mpr_init(RBR(real));
+ mpr_set(RBR(real), OBR(obj));
+ break;
+ default:
+ fatal_object_error(obj, NOT_A_REAL_NUMBER);
+ break;
+ }
+}
+
+static void
+set_number_object(n_number *num, LispObj *obj)
+{
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ num->complex = 0;
+ NRTYPE(num) = N_FIXNUM;
+ NRFI(num) = OFI(obj);
+ break;
+ case LispInteger_t:
+ num->complex = 0;
+ NRTYPE(num) = N_FIXNUM;
+ NRFI(num) = OII(obj);
+ break;
+ case LispBignum_t:
+ num->complex = 0;
+ NRTYPE(num) = N_BIGNUM;
+ NRBI(num) = XALLOC(mpi);
+ mpi_init(NRBI(num));
+ mpi_set(NRBI(num), OBI(obj));
+ break;
+ case LispDFloat_t:
+ num->complex = 0;
+ NRTYPE(num) = N_FLONUM;
+ NRFF(num) = ODF(obj);
+ break;
+ case LispRatio_t:
+ num->complex = 0;
+ NRTYPE(num) = N_FIXRATIO;
+ NRFRN(num) = OFRN(obj);
+ NRFRD(num) = OFRD(obj);
+ break;
+ case LispBigratio_t:
+ num->complex = 0;
+ NRTYPE(num) = N_BIGRATIO;
+ NRBR(num) = XALLOC(mpr);
+ mpr_init(NRBR(num));
+ mpr_set(NRBR(num), OBR(obj));
+ break;
+ case LispComplex_t:
+ num->complex = 1;
+ set_real_object(NREAL(num), OCXR(obj));
+ set_real_object(NIMAG(num), OCXI(obj));
+ break;
+ default:
+ fatal_object_error(obj, NOT_A_NUMBER);
+ break;
+ }
+}
+
+static void
+clear_real(n_real *real)
+{
+ if (RTYPE(real) == N_BIGNUM) {
+ mpi_clear(RBI(real));
+ XFREE(RBI(real));
+ }
+ else if (RTYPE(real) == N_BIGRATIO) {
+ mpr_clear(RBR(real));
+ XFREE(RBR(real));
+ }
+}
+
+static void
+clear_number(n_number *num)
+{
+ clear_real(NREAL(num));
+ if (num->complex)
+ clear_real(NIMAG(num));
+}
+
+static LispObj *
+make_real_object(n_real *real)
+{
+ LispObj *obj;
+
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ if (RFI(real) > MOST_POSITIVE_FIXNUM ||
+ RFI(real) < MOST_NEGATIVE_FIXNUM) {
+ obj = LispNew(NIL, NIL);
+ obj->type = LispInteger_t;
+ OII(obj) = RFI(real);
+ }
+ else
+ obj = FIXNUM(RFI(real));
+ break;
+ case N_BIGNUM:
+ obj = BIGNUM(RBI(real));
+ break;
+ case N_FLONUM:
+ obj = DFLOAT(RFF(real));
+ break;
+ case N_FIXRATIO:
+ obj = LispNew(NIL, NIL);
+ obj->type = LispRatio_t;
+ OFRN(obj) = RFRN(real);
+ OFRD(obj) = RFRD(real);
+ break;
+ case N_BIGRATIO:
+ obj = BIGRATIO(RBR(real));
+ break;
+ default:
+ obj = NIL;
+ break;
+ }
+
+ return (obj);
+}
+
+static LispObj *
+make_number_object(n_number *num)
+{
+ LispObj *obj;
+
+ if (num->complex) {
+ GC_ENTER();
+
+ obj = LispNew(NIL, NIL);
+ GC_PROTECT(obj);
+ OCXI(obj) = NIL;
+ obj->type = LispComplex_t;
+ OCXR(obj) = make_real_object(NREAL(num));
+ OCXI(obj) = make_real_object(NIMAG(num));
+ GC_LEAVE();
+ }
+ else {
+ switch (NRTYPE(num)) {
+ case N_FIXNUM:
+ if (NRFI(num) > MOST_POSITIVE_FIXNUM ||
+ NRFI(num) < MOST_NEGATIVE_FIXNUM) {
+ obj = LispNew(NIL, NIL);
+ obj->type = LispInteger_t;
+ OII(obj) = NRFI(num);
+ }
+ else
+ obj = FIXNUM(NRFI(num));
+ break;
+ case N_BIGNUM:
+ obj = BIGNUM(NRBI(num));
+ break;
+ case N_FLONUM:
+ obj = DFLOAT(NRFF(num));
+ break;
+ case N_FIXRATIO:
+ obj = LispNew(NIL, NIL);
+ obj->type = LispRatio_t;
+ OFRN(obj) = NRFRN(num);
+ OFRD(obj) = NRFRD(num);
+ break;
+ case N_BIGRATIO:
+ obj = BIGRATIO(NRBR(num));
+ break;
+ default:
+ obj = NIL;
+ break;
+ }
+ }
+
+ return (obj);
+}
+
+#define DEFOP_REAL_REAL(OP) \
+OP##_real_real(n_real *real, n_real *val) \
+{ \
+ switch (RTYPE(real)) { \
+ case N_FIXNUM: \
+ switch (RTYPE(val)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_fi(real, RFI(val)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_fi_bi(real, RBI(val)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, (double)RFI(real), RFF(val)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fi_fr(real, RFRN(val), RFRD(val)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_fi_br(real, RBR(val)); \
+ break; \
+ } \
+ break; \
+ case N_BIGNUM: \
+ switch (RTYPE(val)) { \
+ case N_FIXNUM: \
+ r##OP##_bi_fi(real, RFI(val)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_bi(real, RBI(val)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, bi_getd(RBI(real)), RFF(val)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_bi_fr(real, RFRN(val), RFRD(val)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_bi_br(real, RBR(val)); \
+ break; \
+ } \
+ break; \
+ case N_FLONUM: \
+ switch (RTYPE(val)) { \
+ case N_FIXNUM: \
+ r##OP##_flonum(real, RFF(real), (double)RFI(val)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_flonum(real, RFF(real), bi_getd(RBI(val))); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, RFF(real), RFF(val)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_flonum(real, RFF(real), \
+ (double)RFRN(val) / (double)RFRD(val));\
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_flonum(real, RFF(real), br_getd(RBR(val))); \
+ break; \
+ } \
+ break; \
+ case N_FIXRATIO: \
+ switch (RTYPE(val)) { \
+ case N_FIXNUM: \
+ r##OP##_fr_fi(real, RFI(val)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_fr_bi(real, RBI(val)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, \
+ (double)RFRN(real) / (double)RFRD(real),\
+ RFF(val)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_fr(real, RFRN(val), RFRD(val)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_fr_br(real, RBR(val)); \
+ break; \
+ } \
+ break; \
+ case N_BIGRATIO: \
+ switch (RTYPE(val)) { \
+ case N_FIXNUM: \
+ r##OP##_br_fi(real, RFI(val)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_br_bi(real, RBI(val)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, br_getd(RBR(real)), RFF(val)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_br_fr(real, RFRN(val), RFRD(val)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_br(real, RBR(val)); \
+ break; \
+ } \
+ break; \
+ } \
+}
+
+static void
+DEFOP_REAL_REAL(add)
+
+static void
+DEFOP_REAL_REAL(sub)
+
+static void
+DEFOP_REAL_REAL(div)
+
+static void
+DEFOP_REAL_REAL(mul)
+
+
+#define DEFOP_REAL_OBJECT(OP) \
+OP##_real_object(n_real *real, LispObj *obj) \
+{ \
+ switch (OBJECT_TYPE(obj)) { \
+ case LispFixnum_t: \
+ switch (RTYPE(real)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_fi(real, OFI(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_fi(real, OFI(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, RFF(real), (double)OFI(obj)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_fi(real, OFI(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_fi(real, OFI(obj)); \
+ break; \
+ } \
+ break; \
+ case LispInteger_t: \
+ switch (RTYPE(real)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_fi(real, OII(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_fi(real, OII(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, RFF(real), (double)OII(obj)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_fi(real, OII(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_fi(real, OII(obj)); \
+ break; \
+ } \
+ break; \
+ case LispBignum_t: \
+ switch (RTYPE(real)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_bi(real, OBI(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_bi(real, OBI(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, RFF(real), bi_getd(OBI(obj))); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_bi(real, OBI(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_bi(real, OBI(obj)); \
+ break; \
+ } \
+ break; \
+ case LispDFloat_t: \
+ switch (RTYPE(real)) { \
+ case N_FIXNUM: \
+ r##OP##_flonum(real, (double)RFI(real), ODF(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_flonum(real, bi_getd(RBI(real)), ODF(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, RFF(real), ODF(obj)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_flonum(real, \
+ (double)RFRN(real) / (double)RFRD(real),\
+ ODF(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_flonum(real, br_getd(RBR(real)), ODF(obj)); \
+ break; \
+ } \
+ break; \
+ case LispRatio_t: \
+ switch (RTYPE(real)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_fr(real, OFRN(obj), OFRD(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_fr(real, OFRN(obj), OFRD(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, RFF(real), \
+ (double)OFRN(obj) / (double)OFRD(obj)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_fr(real, OFRN(obj), OFRD(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_fr(real, OFRN(obj), OFRD(obj)); \
+ break; \
+ } \
+ break; \
+ case LispBigratio_t: \
+ switch (RTYPE(real)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_br(real, OBR(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_br(real, OBR(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(real, RFF(real), br_getd(OBR(obj))); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_br(real, OBR(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_br(real, OBR(obj)); \
+ break; \
+ } \
+ break; \
+ default: \
+ fatal_object_error(obj, NOT_A_REAL_NUMBER); \
+ break; \
+ } \
+}
+
+static void
+DEFOP_REAL_OBJECT(add)
+
+static void
+DEFOP_REAL_OBJECT(sub)
+
+static void
+DEFOP_REAL_OBJECT(div)
+
+static void
+DEFOP_REAL_OBJECT(mul)
+
+
+#define DEFOP_NUMBER_OBJECT(OP) \
+OP##_number_object(n_number *num, LispObj *obj) \
+{ \
+ if (num->complex) { \
+ switch (OBJECT_TYPE(obj)) { \
+ case LispFixnum_t: \
+ case LispInteger_t: \
+ case LispBignum_t: \
+ case LispDFloat_t: \
+ case LispRatio_t: \
+ case LispBigratio_t: \
+ n##OP##_cx_re(num, obj); \
+ break; \
+ case LispComplex_t: \
+ n##OP##_cx_cx(num, obj); \
+ break; \
+ default: \
+ fatal_object_error(obj, NOT_A_NUMBER); \
+ break; \
+ } \
+ } \
+ else { \
+ switch (OBJECT_TYPE(obj)) { \
+ case LispFixnum_t: \
+ switch (NRTYPE(num)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_fi(NREAL(num), OFI(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_fi(NREAL(num), OFI(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(NREAL(num), NRFF(num), \
+ (double)OFI(obj)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_fi(NREAL(num), OFI(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_fi(NREAL(num), OFI(obj)); \
+ break; \
+ } \
+ break; \
+ case LispInteger_t: \
+ switch (NRTYPE(num)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_fi(NREAL(num), OII(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_fi(NREAL(num), OII(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(NREAL(num), NRFF(num), \
+ (double)OII(obj)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_fi(NREAL(num), OII(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_fi(NREAL(num), OII(obj)); \
+ break; \
+ } \
+ break; \
+ case LispBignum_t: \
+ switch (NRTYPE(num)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_bi(NREAL(num), OBI(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_bi(NREAL(num), OBI(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(NREAL(num), NRFF(num), \
+ bi_getd(OBI(obj))); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_bi(NREAL(num), OBI(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_bi(NREAL(num), OBI(obj)); \
+ break; \
+ } \
+ break; \
+ case LispDFloat_t: \
+ switch (NRTYPE(num)) { \
+ case N_FIXNUM: \
+ r##OP##_flonum(NREAL(num), (double)NRFI(num), \
+ ODF(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_flonum(NREAL(num), bi_getd(NRBI(num)), \
+ ODF(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(NREAL(num), NRFF(num), ODF(obj));\
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_flonum(NREAL(num), \
+ (double)NRFRN(num) / \
+ (double)NRFRD(num), \
+ ODF(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_flonum(NREAL(num), br_getd(NRBR(num)), \
+ ODF(obj)); \
+ break; \
+ } \
+ break; \
+ case LispRatio_t: \
+ switch (NRTYPE(num)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_fr(NREAL(num), OFRN(obj), OFRD(obj));\
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_fr(NREAL(num), OFRN(obj), OFRD(obj));\
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(NREAL(num), NRFF(num), \
+ (double)OFRN(obj) / \
+ (double)OFRD(obj)); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_fr(NREAL(num), OFRN(obj), OFRD(obj));\
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_fr(NREAL(num), OFRN(obj), OFRD(obj));\
+ break; \
+ } \
+ break; \
+ case LispBigratio_t: \
+ switch (NRTYPE(num)) { \
+ case N_FIXNUM: \
+ r##OP##_fi_br(NREAL(num), OBR(obj)); \
+ break; \
+ case N_BIGNUM: \
+ r##OP##_bi_br(NREAL(num), OBR(obj)); \
+ break; \
+ case N_FLONUM: \
+ r##OP##_flonum(NREAL(num), NRFF(num), \
+ br_getd(OBR(obj))); \
+ break; \
+ case N_FIXRATIO: \
+ r##OP##_fr_br(NREAL(num), OBR(obj)); \
+ break; \
+ case N_BIGRATIO: \
+ r##OP##_br_br(NREAL(num), OBR(obj)); \
+ break; \
+ } \
+ break; \
+ case LispComplex_t: \
+ n##OP##_re_cx(num, obj); \
+ break; \
+ default: \
+ fatal_object_error(obj, NOT_A_NUMBER); \
+ break; \
+ } \
+ } \
+}
+
+static void
+DEFOP_NUMBER_OBJECT(add)
+
+static void
+DEFOP_NUMBER_OBJECT(sub)
+
+static void
+DEFOP_NUMBER_OBJECT(div)
+
+static void
+DEFOP_NUMBER_OBJECT(mul)
+
+
+/************************************************************************
+ * ABS
+ ************************************************************************/
+static void
+abs_real(n_real *real)
+{
+ switch (RTYPE(real)) {
+ case N_FIXNUM: rabs_fi(real); break;
+ case N_BIGNUM: rabs_bi(real); break;
+ case N_FLONUM: rabs_ff(real); break;
+ case N_FIXRATIO: rabs_fr(real); break;
+ case N_BIGRATIO: rabs_br(real); break;
+ }
+}
+
+static void
+abs_number(n_number *num)
+{
+ if (num->complex)
+ nabs_cx(num);
+ else {
+ switch (NRTYPE(num)) {
+ case N_FIXNUM: rabs_fi(NREAL(num)); break;
+ case N_BIGNUM: rabs_bi(NREAL(num)); break;
+ case N_FLONUM: rabs_ff(NREAL(num)); break;
+ case N_FIXRATIO: rabs_fr(NREAL(num)); break;
+ case N_BIGRATIO: rabs_br(NREAL(num)); break;
+ }
+ }
+}
+
+static void
+nabs_cx(n_number *num)
+{
+ n_real temp;
+
+ abs_real(NREAL(num));
+ abs_real(NIMAG(num));
+
+ if (cmp_real_real(NREAL(num), NIMAG(num)) < 0) {
+ memcpy(&temp, NIMAG(num), sizeof(n_real));
+ memcpy(NIMAG(num), NREAL(num), sizeof(n_real));
+ memcpy(NREAL(num), &temp, sizeof(n_real));
+ }
+
+ if (cmp_real_real(NIMAG(num), &zero) == 0) {
+ num->complex = 0;
+ if (NITYPE(num) == N_FLONUM) {
+ /* change number type */
+ temp.type = N_FLONUM;
+ temp.data.flonum = 1.0;
+ mul_real_real(NREAL(num), &temp);
+ }
+ else
+ clear_real(NIMAG(num));
+ }
+ else {
+ div_real_real(NIMAG(num), NREAL(num));
+ set_real_real(&temp, NIMAG(num));
+ mul_real_real(NIMAG(num), &temp);
+ clear_real(&temp);
+
+ add_real_real(NIMAG(num), &one);
+ sqrt_real(NIMAG(num));
+
+ mul_real_real(NIMAG(num), NREAL(num));
+ clear_real(NREAL(num));
+ memcpy(NREAL(num), NIMAG(num), sizeof(n_real));
+ num->complex = 0;
+ }
+}
+
+static INLINE void
+rabs_fi(n_real *real)
+{
+ if (RFI(real) < 0)
+ rneg_fi(real);
+}
+
+static INLINE void
+rabs_bi(n_real *real)
+{
+ if (mpi_cmpi(RBI(real), 0) < 0)
+ mpi_neg(RBI(real), RBI(real));
+}
+
+static INLINE void
+rabs_ff(n_real *real)
+{
+ if (RFF(real) < 0.0)
+ RFF(real) = -RFF(real);
+}
+
+static INLINE void
+rabs_fr(n_real *real)
+{
+ if (RFRN(real) < 0)
+ rneg_fr(real);
+}
+
+static INLINE void
+rabs_br(n_real *real)
+{
+ if (mpi_cmpi(RBRN(real), 0) < 0)
+ mpi_neg(RBRN(real), RBRN(real));
+}
+
+
+/************************************************************************
+ * NEG
+ ************************************************************************/
+static void
+neg_real(n_real *real)
+{
+ switch (RTYPE(real)) {
+ case N_FIXNUM: rneg_fi(real); break;
+ case N_BIGNUM: rneg_bi(real); break;
+ case N_FLONUM: rneg_ff(real); break;
+ case N_FIXRATIO: rneg_fr(real); break;
+ case N_BIGRATIO: rneg_br(real); break;
+ }
+}
+
+static void
+neg_number(n_number *num)
+{
+ if (num->complex) {
+ neg_real(NREAL(num));
+ neg_real(NIMAG(num));
+ }
+ else {
+ switch (NRTYPE(num)) {
+ case N_FIXNUM: rneg_fi(NREAL(num)); break;
+ case N_BIGNUM: rneg_bi(NREAL(num)); break;
+ case N_FLONUM: rneg_ff(NREAL(num)); break;
+ case N_FIXRATIO: rneg_fr(NREAL(num)); break;
+ case N_BIGRATIO: rneg_br(NREAL(num)); break;
+ }
+ }
+}
+
+static void
+rneg_fi(n_real *real)
+{
+ if (RFI(real) == MINSLONG) {
+ mpi *bigi = XALLOC(mpi);
+
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ mpi_neg(bigi, bigi);
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = bigi;
+ }
+ else
+ RFI(real) = -RFI(real);
+}
+
+static INLINE void
+rneg_bi(n_real *real)
+{
+ mpi_neg(RBI(real), RBI(real));
+}
+
+static INLINE void
+rneg_ff(n_real *real)
+{
+ RFF(real) = -RFF(real);
+}
+
+static void
+rneg_fr(n_real *real)
+{
+ if (RFRN(real) == MINSLONG) {
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, RFRN(real), RFRD(real));
+ mpi_neg(mpr_num(bigr), mpr_num(bigr));
+ RTYPE(real) = N_BIGRATIO;
+ RBR(real) = bigr;
+ }
+ else
+ RFRN(real) = -RFRN(real);
+}
+
+static INLINE void
+rneg_br(n_real *real)
+{
+ mpi_neg(RBRN(real), RBRN(real));
+}
+
+
+/************************************************************************
+ * SQRT
+ ************************************************************************/
+static void
+sqrt_real(n_real *real)
+{
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ case N_BIGNUM:
+ rsqrt_xi(real);
+ break;
+ case N_FLONUM:
+ rsqrt_ff(real);
+ break;
+ case N_FIXRATIO:
+ case N_BIGRATIO:
+ rsqrt_xr(real);
+ break;
+ }
+}
+
+static void
+sqrt_number(n_number *num)
+{
+ if (num->complex)
+ nsqrt_cx(num);
+ else {
+ switch (NRTYPE(num)) {
+ case N_FIXNUM:
+ case N_BIGNUM:
+ nsqrt_xi(num);
+ break;
+ case N_FLONUM:
+ nsqrt_ff(num);
+ break;
+ case N_FIXRATIO:
+ case N_BIGRATIO:
+ nsqrt_xr(num);
+ break;
+ }
+ }
+}
+
+static void
+rsqrt_xi(n_real *real)
+{
+ int exact;
+ mpi bignum;
+
+ if (cmp_real_real(real, &zero) < 0)
+ fatal_error(FLOATING_POINT_EXCEPTION);
+
+ mpi_init(&bignum);
+ if (RTYPE(real) == N_BIGNUM)
+ exact = mpi_sqrt(&bignum, RBI(real));
+ else {
+ mpi tmp;
+
+ mpi_init(&tmp);
+ mpi_seti(&tmp, RFI(real));
+ exact = mpi_sqrt(&bignum, &tmp);
+ mpi_clear(&tmp);
+ }
+ if (exact) {
+ if (RTYPE(real) == N_BIGNUM) {
+ mpi_set(RBI(real), &bignum);
+ rbi_canonicalize(real);
+ }
+ else
+ RFI(real) = mpi_geti(&bignum);
+ }
+ else {
+ double value;
+
+ if (RTYPE(real) == N_BIGNUM) {
+ value = bi_getd(RBI(real));
+ RCLEAR_BI(real);
+ }
+ else
+ value = (double)RFI(real);
+
+ value = sqrt(value);
+ RTYPE(real) = N_FLONUM;
+ RFF(real) = value;
+ }
+ mpi_clear(&bignum);
+}
+
+static void
+rsqrt_xr(n_real *real)
+{
+ n_real num, den;
+
+ if (cmp_real_real(real, &zero) < 0)
+ fatal_error(FLOATING_POINT_EXCEPTION);
+
+ if (RTYPE(real) == N_FIXRATIO) {
+ num.type = den.type = N_FIXNUM;
+ num.data.fixnum = RFRN(real);
+ den.data.fixnum = RFRD(real);
+ }
+ else {
+ mpi *bignum;
+
+ if (mpi_fiti(RBRN(real))) {
+ num.type = N_FIXNUM;
+ num.data.fixnum = mpi_geti(RBRN(real));
+ }
+ else {
+ bignum = XALLOC(mpi);
+ mpi_init(bignum);
+ mpi_set(bignum, RBRN(real));
+ num.type = N_BIGNUM;
+ num.data.bignum = bignum;
+ }
+
+ if (mpi_fiti(RBRD(real))) {
+ den.type = N_FIXNUM;
+ den.data.fixnum = mpi_geti(RBRD(real));
+ }
+ else {
+ bignum = XALLOC(mpi);
+ mpi_init(bignum);
+ mpi_set(bignum, RBRD(real));
+ den.type = N_BIGNUM;
+ den.data.bignum = bignum;
+ }
+ }
+
+ rsqrt_xi(&num);
+ rsqrt_xi(&den);
+
+ clear_real(real);
+ memcpy(real, &num, sizeof(n_real));
+ div_real_real(real, &den);
+ clear_real(&den);
+}
+
+static void
+rsqrt_ff(n_real *real)
+{
+ if (RFF(real) < 0.0)
+ fatal_error(FLOATING_POINT_EXCEPTION);
+ RFF(real) = sqrt(RFF(real));
+}
+
+
+static void
+nsqrt_cx(n_number *num)
+{
+ n_number mag;
+ n_real *real, *imag;
+
+ real = &(mag.real);
+ imag = &(mag.imag);
+ set_real_real(real, NREAL(num));
+ set_real_real(imag, NIMAG(num));
+ mag.complex = 1;
+
+ nabs_cx(&mag); /* this will free the imag part data */
+ if (cmp_real_real(real, &zero) == 0) {
+ clear_number(num);
+ memcpy(NREAL(num), real, sizeof(n_real));
+ clear_real(real);
+ num->complex = 0;
+ return;
+ }
+ else if (cmp_real_real(NREAL(num), &zero) > 0) {
+ /* R = sqrt((mag + Ra) / 2) */
+ add_real_real(NREAL(num), real);
+ clear_real(real);
+ div_real_real(NREAL(num), &two);
+ sqrt_real(NREAL(num));
+
+ /* I = Ia / R / 2 */
+ div_real_real(NIMAG(num), NREAL(num));
+ div_real_real(NIMAG(num), &two);
+ }
+ else {
+ /* remember old imag part */
+ memcpy(imag, NIMAG(num), sizeof(n_real));
+
+ /* I = sqrt((mag - Ra) / 2) */
+ memcpy(NIMAG(num), real, sizeof(n_real));
+ sub_real_real(NIMAG(num), NREAL(num));
+ div_real_real(NIMAG(num), &two);
+ sqrt_real(NIMAG(num));
+ if (cmp_real_real(imag, &zero) < 0)
+ neg_real(NIMAG(num));
+
+ /* R = Ia / I / 2 */
+ clear_real(NREAL(num));
+ /* start with old imag part */
+ memcpy(NREAL(num), imag, sizeof(n_real));
+ div_real_real(NREAL(num), NIMAG(num));
+ div_real_real(NREAL(num), &two);
+ }
+
+ ncx_canonicalize(num);
+}
+
+static void
+nsqrt_xi(n_number *num)
+{
+ if (cmp_real_real(NREAL(num), &zero) < 0) {
+ memcpy(NIMAG(num), NREAL(num), sizeof(n_real));
+ neg_real(NIMAG(num));
+ rsqrt_xi(NIMAG(num));
+ NRTYPE(num) = N_FIXNUM;
+ NRFI(num) = 0;
+ num->complex = 1;
+ }
+ else
+ rsqrt_xi(NREAL(num));
+}
+
+static void
+nsqrt_ff(n_number *num)
+{
+ double value;
+
+ if (NRFF(num) < 0.0) {
+ value = sqrt(-NRFF(num));
+
+ NITYPE(num) = N_FLONUM;
+ NIFF(num) = value;
+ NRTYPE(num) = N_FIXNUM;
+ NRFI(num) = 0;
+ num->complex = 1;
+ }
+ else {
+ value = sqrt(NRFF(num));
+ NRFF(num) = value;
+ }
+}
+
+static void
+nsqrt_xr(n_number *num)
+{
+ if (cmp_real_real(NREAL(num), &zero) < 0) {
+ memcpy(NIMAG(num), NREAL(num), sizeof(n_real));
+ neg_real(NIMAG(num));
+ rsqrt_xr(NIMAG(num));
+ NRTYPE(num) = N_FIXNUM;
+ NRFI(num) = 0;
+ num->complex = 1;
+ }
+ else
+ rsqrt_xr(NREAL(num));
+}
+
+
+/************************************************************************
+ * MOD
+ ************************************************************************/
+static void
+mod_real_real(n_real *real, n_real *val)
+{
+ /* Assume both operands are integers */
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ switch (RTYPE(val)) {
+ case N_FIXNUM:
+ rmod_fi_fi(real, RFI(val));
+ break;
+ case N_BIGNUM:
+ rmod_fi_bi(real, RBI(val));
+ break;
+ }
+ break;
+ case N_BIGNUM:
+ switch (RTYPE(val)) {
+ case N_FIXNUM:
+ rmod_bi_fi(real, RFI(val));
+ break;
+ case N_BIGNUM:
+ rmod_bi_bi(real, RBI(val));
+ break;
+ }
+ break;
+ }
+}
+
+static void
+mod_real_object(n_real *real, LispObj *obj)
+{
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ rmod_fi_fi(real, OFI(obj));
+ return;
+ case LispInteger_t:
+ rmod_fi_fi(real, OII(obj));
+ return;
+ case LispBignum_t:
+ rmod_fi_bi(real, OBI(obj));
+ return;
+ default:
+ break;
+ }
+ break;
+ case N_BIGNUM:
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ rmod_bi_fi(real, OFI(obj));
+ return;
+ case LispInteger_t:
+ rmod_bi_fi(real, OII(obj));
+ return;
+ case LispBignum_t:
+ rmod_bi_bi(real, OBI(obj));
+ return;
+ default:
+ break;
+ }
+ break;
+ /* Assume the n_real object is an integer */
+ }
+ fatal_object_error(obj, NOT_AN_INTEGER);
+}
+
+static void
+rmod_fi_fi(n_real *real, long fi)
+{
+ if (fi == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ if ((RFI(real) < 0) ^ (fi < 0))
+ RFI(real) = (RFI(real) % fi) + fi;
+ else if (RFI(real) == MINSLONG || fi == MINSLONG) {
+ mpi bignum;
+
+ mpi_init(&bignum);
+ mpi_seti(&bignum, RFI(real));
+ RFI(real) = mpi_modi(&bignum, fi);
+ mpi_clear(&bignum);
+ }
+ else
+ RFI(real) = RFI(real) % fi;
+}
+
+static void
+rmod_fi_bi(n_real *real, mpi *bignum)
+{
+ mpi *bigi;
+
+ if (mpi_cmpi(bignum, 0) == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ bigi = XALLOC(mpi);
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ mpi_mod(bigi, bigi, bignum);
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = bigi;
+ rbi_canonicalize(real);
+}
+
+static void
+rmod_bi_fi(n_real *real, long fi)
+{
+ mpi iop;
+
+ if (fi == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ mpi_init(&iop);
+ mpi_seti(&iop, fi);
+ mpi_mod(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+}
+
+static void
+rmod_bi_bi(n_real *real, mpi *bignum)
+{
+ if (mpi_cmpi(bignum, 0) == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ mpi_mod(RBI(real), RBI(real), bignum);
+ rbi_canonicalize(real);
+}
+
+/************************************************************************
+ * REM
+ ************************************************************************/
+static void
+rem_real_object(n_real *real, LispObj *obj)
+{
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ rrem_fi_fi(real, OFI(obj));
+ return;
+ case LispInteger_t:
+ rrem_fi_fi(real, OII(obj));
+ return;
+ case LispBignum_t:
+ rrem_fi_bi(real, OBI(obj));
+ return;
+ default:
+ break;
+ }
+ break;
+ case N_BIGNUM:
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ rrem_bi_fi(real, OFI(obj));
+ return;
+ case LispInteger_t:
+ rrem_bi_fi(real, OII(obj));
+ return;
+ case LispBignum_t:
+ rrem_bi_bi(real, OBI(obj));
+ return;
+ default:
+ break;
+ }
+ break;
+ /* Assume the n_real object is an integer */
+ }
+ fatal_object_error(obj, NOT_AN_INTEGER);
+}
+
+static void
+rrem_fi_fi(n_real *real, long fi)
+{
+ if (fi == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ if (RFI(real) == MINSLONG || fi == MINSLONG) {
+ mpi bignum;
+
+ mpi_init(&bignum);
+ mpi_seti(&bignum, RFI(real));
+ RFI(real) = mpi_remi(&bignum, fi);
+ mpi_clear(&bignum);
+ }
+ else
+ RFI(real) = RFI(real) % fi;
+}
+
+static void
+rrem_fi_bi(n_real *real, mpi *bignum)
+{
+ mpi *bigi;
+
+ if (mpi_cmpi(bignum, 0) == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ bigi = XALLOC(mpi);
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ mpi_rem(bigi, bigi, bignum);
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = bigi;
+ rbi_canonicalize(real);
+}
+
+static void
+rrem_bi_fi(n_real *real, long fi)
+{
+ mpi iop;
+
+ if (fi == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ mpi_init(&iop);
+ mpi_seti(&iop, fi);
+ mpi_rem(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+}
+
+static void
+rrem_bi_bi(n_real *real, mpi *bignum)
+{
+ if (mpi_cmpi(bignum, 0) == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ mpi_rem(RBI(real), RBI(real), bignum);
+ rbi_canonicalize(real);
+}
+
+
+/************************************************************************
+ * GCD
+ ************************************************************************/
+static void
+gcd_real_object(n_real *real, LispObj *obj)
+{
+ if (!INTEGERP(obj))
+ fatal_object_error(obj, NOT_AN_INTEGER);
+
+ /* check for zero operand */
+ if (cmp_real_real(real, &zero) == 0)
+ set_real_object(real, obj);
+ else if (cmp_real_object(&zero, obj) != 0) {
+ n_real rest, temp;
+
+ set_real_object(&rest, obj);
+ for (;;) {
+ mod_real_real(&rest, real);
+ if (cmp_real_real(&rest, &zero) == 0)
+ break;
+ memcpy(&temp, real, sizeof(n_real));
+ memcpy(real, &rest, sizeof(n_real));
+ memcpy(&rest, &temp, sizeof(n_real));
+ }
+ clear_real(&rest);
+ }
+}
+
+/************************************************************************
+ * AND
+ ************************************************************************/
+static void
+and_real_object(n_real *real, LispObj *obj)
+{
+ mpi *bigi, iop;
+
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ RFI(real) &= OFI(obj);
+ break;
+ case N_BIGNUM:
+ mpi_init(&iop);
+ mpi_seti(&iop, OFI(obj));
+ mpi_and(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ case LispInteger_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ RFI(real) &= OII(obj);
+ break;
+ case N_BIGNUM:
+ mpi_init(&iop);
+ mpi_seti(&iop, OII(obj));
+ mpi_and(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ case LispBignum_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ bigi = XALLOC(mpi);
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ mpi_and(bigi, bigi, OBI(obj));
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = bigi;
+ rbi_canonicalize(real);
+ break;
+ case N_BIGNUM:
+ mpi_and(RBI(real), RBI(real), OBI(obj));
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ default:
+ fatal_object_error(obj, NOT_AN_INTEGER);
+ break;
+ }
+}
+
+
+/************************************************************************
+ * EQV
+ ************************************************************************/
+static void
+eqv_real_object(n_real *real, LispObj *obj)
+{
+ mpi *bigi, iop;
+
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ RFI(real) ^= ~OFI(obj);
+ break;
+ case N_BIGNUM:
+ mpi_init(&iop);
+ mpi_seti(&iop, OFI(obj));
+ mpi_com(&iop, &iop);
+ mpi_xor(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ case LispInteger_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ RFI(real) ^= ~OII(obj);
+ break;
+ case N_BIGNUM:
+ mpi_init(&iop);
+ mpi_seti(&iop, OII(obj));
+ mpi_com(&iop, &iop);
+ mpi_xor(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ case LispBignum_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ bigi = XALLOC(mpi);
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ mpi_com(bigi, bigi);
+ mpi_xor(bigi, bigi, OBI(obj));
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = bigi;
+ rbi_canonicalize(real);
+ break;
+ case N_BIGNUM:
+ mpi_com(RBI(real), RBI(real));
+ mpi_xor(RBI(real), RBI(real), OBI(obj));
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ default:
+ fatal_object_error(obj, NOT_AN_INTEGER);
+ break;
+ }
+}
+
+
+/************************************************************************
+ * IOR
+ ************************************************************************/
+static void
+ior_real_object(n_real *real, LispObj *obj)
+{
+ mpi *bigi, iop;
+
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ RFI(real) |= OFI(obj);
+ break;
+ case N_BIGNUM:
+ mpi_init(&iop);
+ mpi_seti(&iop, OFI(obj));
+ mpi_ior(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ case LispInteger_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ RFI(real) |= OII(obj);
+ break;
+ case N_BIGNUM:
+ mpi_init(&iop);
+ mpi_seti(&iop, OII(obj));
+ mpi_ior(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ case LispBignum_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ bigi = XALLOC(mpi);
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ mpi_ior(bigi, bigi, OBI(obj));
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = bigi;
+ rbi_canonicalize(real);
+ break;
+ case N_BIGNUM:
+ mpi_ior(RBI(real), RBI(real), OBI(obj));
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ default:
+ fatal_object_error(obj, NOT_AN_INTEGER);
+ break;
+ }
+}
+
+
+/************************************************************************
+ * NOT
+ ************************************************************************/
+static void
+not_real(n_real *real)
+{
+ if (RTYPE(real) == N_FIXNUM)
+ RFI(real) = ~RFI(real);
+ else {
+ mpi_com(RBI(real), RBI(real));
+ rbi_canonicalize(real);
+ }
+}
+
+/************************************************************************
+ * XOR
+ ************************************************************************/
+static void
+xor_real_object(n_real *real, LispObj *obj)
+{
+ mpi *bigi, iop;
+
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ RFI(real) ^= OFI(obj);
+ break;
+ case N_BIGNUM:
+ mpi_init(&iop);
+ mpi_seti(&iop, OFI(obj));
+ mpi_xor(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ case LispInteger_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ RFI(real) ^= OII(obj);
+ break;
+ case N_BIGNUM:
+ mpi_init(&iop);
+ mpi_seti(&iop, OII(obj));
+ mpi_xor(RBI(real), RBI(real), &iop);
+ mpi_clear(&iop);
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ case LispBignum_t:
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ bigi = XALLOC(mpi);
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ mpi_xor(bigi, bigi, OBI(obj));
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = bigi;
+ rbi_canonicalize(real);
+ break;
+ case N_BIGNUM:
+ mpi_xor(RBI(real), RBI(real), OBI(obj));
+ rbi_canonicalize(real);
+ break;
+ }
+ break;
+ default:
+ fatal_object_error(obj, NOT_AN_INTEGER);
+ break;
+ }
+}
+
+
+/************************************************************************
+ * DIVIDE
+ ************************************************************************/
+static void
+divide_number_object(n_number *num, LispObj *obj, int fun, int flo)
+{
+ switch (OBJECT_TYPE(obj)) {
+ case LispFixnum_t:
+ switch (NRTYPE(num)) {
+ case N_FIXNUM:
+ ndivide_fi_fi(num, OFI(obj), fun, flo);
+ break;
+ case N_BIGNUM:
+ ndivide_xi_xi(num, obj, fun, flo);
+ break;
+ case N_FLONUM:
+ ndivide_flonum(num, NRFF(num), (double)OFI(obj), fun, flo);
+ break;
+ case N_FIXRATIO:
+ case N_BIGRATIO:
+ ndivide_xr_xi(num, obj, fun, flo);
+ break;
+ }
+ break;
+ case LispInteger_t:
+ switch (NRTYPE(num)) {
+ case N_FIXNUM:
+ ndivide_fi_fi(num, OII(obj), fun, flo);
+ break;
+ case N_BIGNUM:
+ ndivide_xi_xi(num, obj, fun, flo);
+ break;
+ case N_FLONUM:
+ ndivide_flonum(num, NRFF(num), (double)OII(obj), fun, flo);
+ break;
+ case N_FIXRATIO:
+ case N_BIGRATIO:
+ ndivide_xr_xi(num, obj, fun, flo);
+ break;
+ }
+ break;
+ case LispBignum_t:
+ switch (NRTYPE(num)) {
+ case N_FIXNUM:
+ case N_BIGNUM:
+ ndivide_xi_xi(num, obj, fun, flo);
+ break;
+ case N_FLONUM:
+ ndivide_flonum(num, NRFF(num), bi_getd(OBI(obj)),
+ fun, flo);
+ break;
+ case N_FIXRATIO:
+ case N_BIGRATIO:
+ ndivide_xr_xi(num, obj, fun, flo);
+ break;
+ }
+ break;
+ case LispDFloat_t:
+ switch (NRTYPE(num)) {
+ case N_FIXNUM:
+ ndivide_flonum(num, (double)NRFI(num), ODF(obj),
+ fun, flo);
+ break;
+ case N_BIGNUM:
+ ndivide_flonum(num, bi_getd(NRBI(num)), ODF(obj),
+ fun, flo);
+ break;
+ case N_FLONUM:
+ ndivide_flonum(num, NRFF(num), ODF(obj), fun, flo);
+ break;
+ case N_FIXRATIO:
+ ndivide_flonum(num,
+ (double)NRFRN(num) / (double)NRFRD(num),
+ ODF(obj), fun, flo);
+ break;
+ case N_BIGRATIO:
+ ndivide_flonum(num, br_getd(NRBR(num)), ODF(obj),
+ fun, flo);
+ break;
+ }
+ break;
+ case LispRatio_t:
+ switch (NRTYPE(num)) {
+ case N_FIXNUM:
+ case N_BIGNUM:
+ ndivide_xi_xr(num, obj, fun, flo);
+ break;
+ case N_FLONUM:
+ ndivide_flonum(num, NRFF(num),
+ (double)OFRN(obj) / (double)OFRD(obj),
+ fun, flo);
+ break;
+ case N_FIXRATIO:
+ case N_BIGRATIO:
+ ndivide_xr_xr(num, obj, fun, flo);
+ break;
+ }
+ break;
+ case LispBigratio_t:
+ switch (NRTYPE(num)) {
+ case N_FIXNUM:
+ case N_BIGNUM:
+ ndivide_xi_xr(num, obj, fun, flo);
+ break;
+ case N_FLONUM:
+ ndivide_flonum(num, NRFF(num), br_getd(OBR(obj)),
+ fun, flo);
+ break;
+ case N_FIXRATIO:
+ case N_BIGRATIO:
+ ndivide_xr_xr(num, obj, fun, flo);
+ break;
+ }
+ break;
+ default:
+ fatal_object_error(obj, NOT_A_REAL_NUMBER);
+ break;
+ }
+}
+
+
+/************************************************************************
+ * COMPARE
+ ************************************************************************/
+static int
+cmp_real_real(n_real *op1, n_real *op2)
+{
+ switch (RTYPE(op1)) {
+ case N_FIXNUM:
+ switch (RTYPE(op2)) {
+ case N_FIXNUM:
+ return (cmp_fi_fi(RFI(op1), RFI(op2)));
+ case N_BIGNUM:
+ return (cmp_fi_bi(RFI(op1), RBI(op2)));
+ case N_FLONUM:
+ return (cmp_flonum((double)RFI(op1), RFF(op2)));
+ case N_FIXRATIO:
+ return (cmp_fi_fr(RFI(op1), RFRN(op2), RFRD(op2)));
+ case N_BIGRATIO:
+ return (cmp_fi_br(RFI(op1), RBR(op2)));
+ }
+ break;
+ case N_BIGNUM:
+ switch (RTYPE(op2)) {
+ case N_FIXNUM:
+ return (cmp_bi_fi(RBI(op1), RFI(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_bi(RBI(op1), RBI(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(bi_getd(RBI(op1)), RFF(op2)));
+ case N_FIXRATIO:
+ return (cmp_bi_fr(RBI(op1), RFRN(op2), RFRD(op2)));
+ case N_BIGRATIO:
+ return (cmp_bi_br(RBI(op1), RBR(op2)));
+ }
+ break;
+ case N_FLONUM:
+ switch (RTYPE(op2)) {
+ case N_FIXNUM:
+ return (cmp_flonum(RFF(op1), (double)RFI(op2)));
+ case N_BIGNUM:
+ return (cmp_flonum(RFF(op1), bi_getd(RBI(op2))));
+ case N_FLONUM:
+ return (cmp_flonum(RFF(op1), RFF(op2)));
+ case N_FIXRATIO:
+ return (cmp_flonum(RFF(op1),
+ (double)RFRN(op2) / (double)RFRD(op2)));
+ case N_BIGRATIO:
+ return (cmp_flonum(RFF(op1), br_getd(RBR(op2))));
+ }
+ break;
+ case N_FIXRATIO:
+ switch (RTYPE(op2)) {
+ case N_FIXNUM:
+ return (cmp_fr_fi(RFRN(op1), RFRD(op1), RFI(op2)));
+ case N_BIGNUM:
+ return (cmp_fr_bi(RFRN(op1), RFRD(op1), RBI(op2)));
+ case N_FLONUM:
+ return (cmp_flonum((double)RFRN(op1) / (double)RFRD(op1),
+ RFF(op2)));
+ case N_FIXRATIO:
+ return (cmp_fr_fr(RFRN(op1), RFRD(op1),
+ RFRN(op2), RFRD(op2)));
+ case N_BIGRATIO:
+ return (cmp_fr_br(RFRN(op1), RFRD(op1), RBR(op2)));
+ }
+ break;
+ case N_BIGRATIO:
+ switch (RTYPE(op2)) {
+ case N_FIXNUM:
+ return (cmp_br_fi(RBR(op1), RFI(op2)));
+ case N_BIGNUM:
+ return (cmp_br_bi(RBR(op1), RBI(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(br_getd(RBR(op1)), RFF(op2)));
+ case N_FIXRATIO:
+ return (cmp_br_fr(RBR(op1), RFRN(op2), RFRD(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_br(RBR(op1), RBR(op2)));
+ }
+ }
+
+ return (0);
+}
+
+static int
+cmp_real_object(n_real *op1, LispObj *op2)
+{
+ switch (OBJECT_TYPE(op2)) {
+ case LispFixnum_t:
+ switch (RTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_fi(RFI(op1), OFI(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_fi(RBI(op1), OFI(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(RFF(op1), (double)OFI(op2)));
+ case N_FIXRATIO:
+ return (cmp_fr_fi(RFRD(op1), RFRN(op1), OFI(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_fi(RBR(op1), OFI(op2)));
+ }
+ break;
+ case LispInteger_t:
+ switch (RTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_fi(RFI(op1), OII(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_fi(RBI(op1), OII(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(RFF(op1), (double)OII(op2)));
+ case N_FIXRATIO:
+ return (cmp_fr_fi(RFRD(op1), RFRN(op1), OII(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_fi(RBR(op1), OII(op2)));
+ }
+ break;
+ case LispBignum_t:
+ switch (RTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_bi(RFI(op1), OBI(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_bi(RBI(op1), OBI(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(RFF(op1), bi_getd(OBI(op2))));
+ case N_FIXRATIO:
+ return (cmp_fr_bi(RFRD(op1), RFRN(op1), OBI(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_bi(RBR(op1), OBI(op2)));
+ }
+ break;
+ case LispDFloat_t:
+ switch (RTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_flonum((double)RFI(op1), ODF(op2)));
+ case N_BIGNUM:
+ return (cmp_flonum(bi_getd(RBI(op1)), ODF(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(RFF(op1), ODF(op2)));
+ case N_FIXRATIO:
+ return (cmp_flonum((double)RFRN(op1) / (double)RFRD(op1),
+ ODF(op2)));
+ case N_BIGRATIO:
+ return (cmp_flonum(br_getd(RBR(op1)), ODF(op2)));
+ }
+ break;
+ case LispRatio_t:
+ switch (RTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_fr(RFI(op1), OFRN(op2), OFRD(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_fr(RBI(op1), OFRN(op2), OFRD(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(RFF(op1),
+ (double)OFRN(op2) / (double)OFRD(op2)));
+ case N_FIXRATIO:
+ return (cmp_fr_fr(RFRN(op1), RFRD(op1),
+ OFRN(op2), OFRD(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_fr(RBR(op1), OFRN(op2), OFRD(op2)));
+ }
+ break;
+ case LispBigratio_t:
+ switch (RTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_br(RFI(op1), OBR(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_br(RBI(op1), OBR(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(RFF(op1), br_getd(OBR(op2))));
+ case N_FIXRATIO:
+ return (cmp_fr_br(RFRN(op1), RFRD(op1), OBR(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_br(RBR(op1), OBR(op2)));
+ }
+ break;
+ default:
+ fatal_object_error(op2, NOT_A_REAL_NUMBER);
+ break;
+ }
+
+ return (0);
+}
+
+#if 0 /* not used */
+static int
+cmp_number_object(n_number *op1, LispObj *op2)
+{
+ if (op1->complex) {
+ if (OBJECT_TYPE(op2) == LispComplex_t) {
+ if (cmp_real_object(NREAL(op1), OCXR(op2)) == 0)
+ return (cmp_real_object(NIMAG(op1), OCXI(op2)));
+ return (1);
+ }
+ else if (cmp_real_real(NIMAG(op1), &zero) == 0)
+ return (cmp_real_object(NREAL(op1), op2));
+ else
+ return (1);
+ }
+ else {
+ switch (OBJECT_TYPE(op2)) {
+ case LispFixnum_t:
+ switch (NRTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_fi(NRFI(op1), OFI(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_fi(NRBI(op1), OFI(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(NRFF(op1), (double)OFI(op2)));
+ case N_FIXRATIO:
+ return (cmp_fr_fi(NRFRD(op1), NRFRN(op1), OFI(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_fi(NRBR(op1), OFI(op2)));
+ }
+ break;
+ case LispInteger_t:
+ switch (NRTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_fi(NRFI(op1), OII(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_fi(NRBI(op1), OII(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(NRFF(op1), (double)OII(op2)));
+ case N_FIXRATIO:
+ return (cmp_fr_fi(NRFRD(op1), NRFRN(op1), OII(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_fi(NRBR(op1), OII(op2)));
+ }
+ break;
+ case LispBignum_t:
+ switch (NRTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_bi(NRFI(op1), OBI(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_bi(NRBI(op1), OBI(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(NRFF(op1), bi_getd(OBI(op2))));
+ case N_FIXRATIO:
+ return (cmp_fr_bi(NRFRD(op1), NRFRN(op1), OBI(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_bi(NRBR(op1), OBI(op2)));
+ }
+ break;
+ case LispDFloat_t:
+ switch (NRTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_flonum((double)NRFI(op1), ODF(op2)));
+ case N_BIGNUM:
+ return (cmp_flonum(bi_getd(NRBI(op1)), ODF(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(NRFF(op1), ODF(op2)));
+ case N_FIXRATIO:
+ return (cmp_flonum((double)NRFRN(op1) /
+ (double)NRFRD(op1),
+ ODF(op2)));
+ case N_BIGRATIO:
+ return (cmp_flonum(br_getd(NRBR(op1)), ODF(op2)));
+ }
+ break;
+ case LispRatio_t:
+ switch (NRTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_fr(NRFI(op1), OFRN(op2), OFRD(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_fr(NRBI(op1), OFRN(op2), OFRD(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(NRFF(op1),
+ (double)OFRN(op2) / (double)OFRD(op2)));
+ case N_FIXRATIO:
+ return (cmp_fr_fr(NRFRN(op1), NRFRD(op1),
+ OFRN(op2), OFRD(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_fr(NRBR(op1), OFRN(op2), OFRD(op2)));
+ }
+ break;
+ case LispBigratio_t:
+ switch (NRTYPE(op1)) {
+ case N_FIXNUM:
+ return (cmp_fi_br(NRFI(op1), OBR(op2)));
+ case N_BIGNUM:
+ return (cmp_bi_br(NRBI(op1), OBR(op2)));
+ case N_FLONUM:
+ return (cmp_flonum(NRFF(op1), br_getd(OBR(op2))));
+ case N_FIXRATIO:
+ return (cmp_fr_br(NRFRN(op1), NRFRD(op1), OBR(op2)));
+ case N_BIGRATIO:
+ return (cmp_br_br(NRBR(op1), OBR(op2)));
+ }
+ break;
+ case LispComplex_t:
+ if (cmp_real_object(&zero, OCXI(op2)) == 0)
+ return (cmp_real_object(NREAL(op1), OCXR(op2)));
+ return (1);
+ default:
+ fatal_object_error(op2, NOT_A_NUMBER);
+ break;
+ }
+ }
+
+ return (0);
+}
+#endif
+
+static int
+cmp_object_object(LispObj *op1, LispObj *op2, int real)
+{
+ if (OBJECT_TYPE(op1) == LispComplex_t) {
+ if (real)
+ fatal_object_error(op1, NOT_A_REAL_NUMBER);
+ if (OBJECT_TYPE(op2) == LispComplex_t)
+ return (cmp_cx_cx(op1, op2));
+ else if (cmp_real_object(&zero, OCXI(op1)) == 0)
+ return (cmp_object_object(OCXR(op1), op2, real));
+ return (1);
+ }
+ else if (OBJECT_TYPE(op2) == LispComplex_t) {
+ if (real)
+ fatal_object_error(op1, NOT_A_REAL_NUMBER);
+ if (cmp_real_object(&zero, OCXI(op2)) == 0)
+ return (cmp_object_object(op1, OCXR(op2), real));
+ return (1);
+ }
+ else {
+ switch (OBJECT_TYPE(op1)) {
+ case LispFixnum_t:
+ switch (OBJECT_TYPE(op2)) {
+ case LispFixnum_t:
+ return (cmp_fi_fi(OFI(op1), OFI(op2)));
+ case LispInteger_t:
+ return (cmp_fi_fi(OFI(op1), OII(op2)));
+ case LispBignum_t:
+ return (cmp_fi_bi(OFI(op1), OBI(op2)));
+ case LispDFloat_t:
+ return (cmp_flonum((double)OFI(op1), ODF(op2)));
+ case LispRatio_t:
+ return (cmp_fi_fr(OFI(op1),
+ OFRN(op2), OFRD(op2)));
+ case LispBigratio_t:
+ return (cmp_fi_br(OFI(op1), OBR(op2)));
+ default:
+ break;
+ }
+ break;
+ case LispInteger_t:
+ switch (OBJECT_TYPE(op2)) {
+ case LispFixnum_t:
+ return (cmp_fi_fi(OII(op1), OFI(op2)));
+ case LispInteger_t:
+ return (cmp_fi_fi(OII(op1), OII(op2)));
+ case LispBignum_t:
+ return (cmp_fi_bi(OII(op1), OBI(op2)));
+ case LispDFloat_t:
+ return (cmp_flonum((double)OII(op1), ODF(op2)));
+ case LispRatio_t:
+ return (cmp_fi_fr(OII(op1),
+ OFRN(op2), OFRD(op2)));
+ case LispBigratio_t:
+ return (cmp_fi_br(OII(op1), OBR(op2)));
+ default:
+ break;
+ }
+ break;
+ case LispBignum_t:
+ switch (OBJECT_TYPE(op2)) {
+ case LispFixnum_t:
+ return (cmp_bi_fi(OBI(op1), OFI(op2)));
+ case LispInteger_t:
+ return (cmp_bi_fi(OBI(op1), OII(op2)));
+ case LispBignum_t:
+ return (cmp_bi_bi(OBI(op1), OBI(op2)));
+ case LispDFloat_t:
+ return (cmp_flonum(bi_getd(OBI(op1)), ODF(op2)));
+ case LispRatio_t:
+ return (cmp_bi_fr(OBI(op1),
+ OFRN(op2), OFRD(op2)));
+ case LispBigratio_t:
+ return (cmp_bi_br(OBI(op1), OBR(op2)));
+ default:
+ break;
+ }
+ break;
+ case LispDFloat_t:
+ switch (OBJECT_TYPE(op2)) {
+ case LispFixnum_t:
+ return (cmp_flonum(ODF(op1), (double)OFI(op2)));
+ case LispInteger_t:
+ return (cmp_flonum(ODF(op1), (double)OII(op2)));
+ case LispBignum_t:
+ return (cmp_flonum(ODF(op1), bi_getd(OBI(op2))));
+ case LispDFloat_t:
+ return (cmp_flonum(ODF(op1), ODF(op2)));
+ break;
+ case LispRatio_t:
+ return (cmp_flonum(ODF(op1),
+ (double)OFRN(op2) /
+ (double)OFRD(op2)));
+ case LispBigratio_t:
+ return (cmp_flonum(ODF(op1), br_getd(OBR(op2))));
+ default:
+ break;
+ }
+ break;
+ case LispRatio_t:
+ switch (OBJECT_TYPE(op2)) {
+ case LispFixnum_t:
+ return (cmp_fr_fi(OFRN(op1), OFRD(op1), OFI(op2)));
+ case LispInteger_t:
+ return (cmp_fr_fi(OFRN(op1), OFRD(op1), OII(op2)));
+ case LispBignum_t:
+ return (cmp_fr_bi(OFRN(op1), OFRD(op1), OBI(op2)));
+ case LispDFloat_t:
+ return (cmp_flonum((double)OFRN(op1) /
+ (double)OFRD(op1),
+ ODF(op2)));
+ case LispRatio_t:
+ return (cmp_fr_fr(OFRN(op1), OFRD(op1),
+ OFRN(op2), OFRD(op2)));
+ case LispBigratio_t:
+ return (cmp_fr_br(OFRN(op1), OFRD(op1), OBR(op2)));
+ default:
+ break;
+ }
+ break;
+ case LispBigratio_t:
+ switch (OBJECT_TYPE(op2)) {
+ case LispFixnum_t:
+ return (cmp_br_fi(OBR(op1), OFI(op2)));
+ case LispInteger_t:
+ return (cmp_br_fi(OBR(op1), OII(op2)));
+ case LispBignum_t:
+ return (cmp_br_bi(OBR(op1), OBI(op2)));
+ case LispDFloat_t:
+ return (cmp_flonum(br_getd(OBR(op1)), ODF(op2)));
+ case LispRatio_t:
+ return (cmp_br_fr(OBR(op1), OFRN(op2), OFRD(op2)));
+ case LispBigratio_t:
+ return (cmp_br_br(OBR(op1), OBR(op2)));
+ default:
+ break;
+ }
+ break;
+ default:
+ fatal_object_error(op1, NOT_A_NUMBER);
+ break;
+ }
+ }
+
+ fatal_object_error(op2, NOT_A_NUMBER);
+ return (0);
+}
+
+
+/************************************************************************
+ * FIXNUM
+ ************************************************************************/
+/*
+ * check if op1 + op2 will overflow
+ */
+static INLINE int
+fi_fi_add_overflow(long op1, long op2)
+{
+ long op = op1 + op2;
+
+ return (op1 > 0 ? op2 > op : op2 < op);
+}
+
+/*
+ * check if op1 - op2 will overflow
+ */
+static INLINE int
+fi_fi_sub_overflow(long op1, long op2)
+{
+ long op = op1 - op2;
+
+ return (((op1 < 0) ^ (op2 < 0)) && ((op < 0) ^ (op1 < 0)));
+}
+
+/*
+ * check if op1 * op2 will overflow
+ */
+static INLINE int
+fi_fi_mul_overflow(long op1, long op2)
+{
+#ifndef LONG64
+ double op = (double)op1 * (double)op2;
+
+ return (op > 2147483647.0 || op < -2147483648.0);
+#else
+ int shift, sign;
+ long mask;
+
+ if (op1 == 0 || op1 == 1 || op2 == 0 || op2 == 1)
+ return (0);
+
+ if (op1 == MINSLONG || op2 == MINSLONG)
+ return (1);
+
+ sign = (op1 < 0) ^ (op2 < 0);
+
+ if (op1 < 0)
+ op1 = -op1;
+ if (op2 < 0)
+ op2 = -op2;
+
+ for (shift = 0, mask = FI_MASK; shift < LONGSBITS; shift++, mask >>= 1)
+ if (op1 & mask)
+ break;
+ ++shift;
+ for (mask = FI_MASK; shift < LONGSBITS; shift++, mask >>= 1)
+ if (op2 & mask)
+ break;
+
+ return (shift < LONGSBITS);
+#endif
+}
+
+
+/************************************************************************
+ * BIGNUM
+ ************************************************************************/
+static void
+rbi_canonicalize(n_real *real)
+{
+ if (mpi_fiti(RBI(real))) {
+ long fi = mpi_geti(RBI(real));
+
+ RTYPE(real) = N_FIXNUM;
+ mpi_clear(RBI(real));
+ XFREE(RBI(real));
+ RFI(real) = fi;
+ }
+}
+
+
+/************************************************************************
+ * RATIO
+ ************************************************************************/
+static void
+rfr_canonicalize(n_real *real)
+{
+ long num, numerator, den, denominator, rest;
+
+ num = numerator = RFRN(real);
+ den = denominator = RFRD(real);
+ if (denominator == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ if (num == MINSLONG || den == MINSLONG) {
+ mpr *bigratio = XALLOC(mpr);
+
+ mpr_init(bigratio);
+ mpr_seti(bigratio, num, den);
+ RTYPE(real) = N_BIGRATIO;
+ RBR(real) = bigratio;
+ rbr_canonicalize(real);
+ return;
+ }
+
+ if (num < 0)
+ num = -num;
+ else if (num == 0) {
+ RFI(real) = 0;
+ RTYPE(real) = N_FIXNUM;
+ return;
+ }
+ for (;;) {
+ if ((rest = den % num) == 0)
+ break;
+ den = num;
+ num = rest;
+ }
+ if (den != 1) {
+ denominator /= num;
+ numerator /= num;
+ }
+ if (denominator < 0) {
+ numerator = -numerator;
+ denominator = -denominator;
+ }
+ if (denominator == 1) {
+ RTYPE(real) = N_FIXNUM;
+ RFI(real) = numerator;
+ }
+ else {
+ RFRN(real) = numerator;
+ RFRD(real) = denominator;
+ }
+}
+
+static void
+rbr_canonicalize(n_real *real)
+{
+ int fitnum, fitden;
+ long numerator, denominator;
+
+ mpr_canonicalize(RBR(real));
+ fitnum = mpi_fiti(RBRN(real));
+ fitden = mpi_fiti(RBRD(real));
+ if (fitnum && fitden) {
+ numerator = mpi_geti(RBRN(real));
+ denominator = mpi_geti(RBRD(real));
+ mpr_clear(RBR(real));
+ XFREE(RBR(real));
+ if (numerator == 0) {
+ RFI(real) = 0;
+ RTYPE(real) = N_FIXNUM;
+ }
+ else if (denominator == 1) {
+ RTYPE(real) = N_FIXNUM;
+ RFI(real) = numerator;
+ }
+ else {
+ RTYPE(real) = N_FIXRATIO;
+ RFRN(real) = numerator;
+ RFRD(real) = denominator;
+ }
+ }
+ else if (fitden) {
+ denominator = mpi_geti(RBRD(real));
+ if (denominator == 1) {
+ mpi *bigi = XALLOC(mpi);
+
+ mpi_init(bigi);
+ mpi_set(bigi, RBRN(real));
+ mpr_clear(RBR(real));
+ XFREE(RBR(real));
+ RTYPE(real) = N_BIGNUM;
+ RBI(real) = bigi;
+ }
+ else if (denominator == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+ }
+}
+
+
+/************************************************************************
+ * COMPLEX
+ ************************************************************************/
+static void
+ncx_canonicalize(n_number *num)
+{
+ if (NITYPE(num) == N_FIXNUM && NIFI(num) == 0)
+ num->complex = 0;
+}
+
+
+/************************************************************************
+ * DIVIDE
+ ************************************************************************/
+#define NDIVIDE_NOP 0
+#define NDIVIDE_ADD 1
+#define NDIVIDE_SUB 2
+static void
+ndivide_fi_fi(n_number *num, long div, int fun, int flo)
+{
+ long quo, rem;
+
+ if (NRFI(num) == MINSLONG || div == MINSLONG) {
+ LispObj integer;
+ mpi *bignum = XALLOC(mpi);
+
+ mpi_init(bignum);
+ mpi_seti(bignum, NRFI(num));
+ NRBI(num) = bignum;
+ NRTYPE(num) = N_BIGNUM;
+ integer.type = LispInteger_t;
+ integer.data.integer = div;
+ ndivide_xi_xi(num, &integer, fun, flo);
+ return;
+ }
+ else {
+ quo = NRFI(num) / div;
+ rem = NRFI(num) % div;
+ }
+
+ switch (fun) {
+ case NDIVIDE_CEIL:
+ if ((rem < 0 && div < 0) || (rem > 0 && div > 0)) {
+ ++quo;
+ rem -= div;
+ }
+ break;
+ case NDIVIDE_FLOOR:
+ if ((rem < 0 && div > 0) || (rem > 0 && div < 0)) {
+ --quo;
+ rem += div;
+ }
+ break;
+ case NDIVIDE_ROUND:
+ if (div > 0) {
+ if (rem > 0) {
+ if (rem >= (div + 1) / 2) {
+ ++quo;
+ rem -= div;
+ }
+ }
+ else {
+ if (rem <= (-div - 1) / 2) {
+ --quo;
+ rem += div;
+ }
+ }
+ }
+ else {
+ if (rem > 0) {
+ if (rem >= (-div + 1) / 2) {
+ --quo;
+ rem += div;
+ }
+ }
+ else {
+ if (rem <= (div - 1) / 2) {
+ ++quo;
+ rem -= div;
+ }
+ }
+ }
+ break;
+ }
+
+ NITYPE(num) = N_FIXNUM;
+ NIFI(num) = rem;
+ if (flo) {
+ NRTYPE(num) = N_FLONUM;
+ NRFF(num) = (double)quo;
+ }
+ else
+ NRFI(num) = quo;
+}
+
+static void
+ndivide_xi_xi(n_number *num, LispObj *div, int fun, int flo)
+{
+ LispType type = OBJECT_TYPE(div);
+ int state = NDIVIDE_NOP, dsign, rsign;
+ mpi *quo, *rem;
+
+ quo = XALLOC(mpi);
+ mpi_init(quo);
+ if (NRTYPE(num) == N_FIXNUM)
+ mpi_seti(quo, NRFI(num));
+ else
+ mpi_set(quo, NRBI(num));
+
+ rem = XALLOC(mpi);
+ mpi_init(rem);
+
+ switch (type) {
+ case LispFixnum_t:
+ mpi_seti(rem, OFI(div));
+ break;
+ case LispInteger_t:
+ mpi_seti(rem, OII(div));
+ break;
+ default:
+ mpi_set(rem, OBI(div));
+ }
+
+ dsign = mpi_sgn(rem);
+
+ mpi_divqr(quo, rem, quo, rem);
+ rsign = mpi_sgn(rem);
+
+ switch (fun) {
+ case NDIVIDE_CEIL:
+ if ((rsign < 0 && dsign < 0) || (rsign > 0 && dsign > 0))
+ state = NDIVIDE_ADD;
+ break;
+ case NDIVIDE_FLOOR:
+ if ((rsign < 0 && dsign > 0) || (rsign > 0 && dsign < 0))
+ state = NDIVIDE_SUB;
+ break;
+ case NDIVIDE_ROUND: {
+ mpi test;
+
+ mpi_init(&test);
+ switch (type) {
+ case LispFixnum_t:
+ mpi_seti(&test, OFI(div));
+ break;
+ case LispInteger_t:
+ mpi_seti(&test, OII(div));
+ break;
+ default:
+ mpi_set(&test, OBI(div));
+ }
+ if (dsign > 0) {
+ if (rsign > 0) {
+ mpi_addi(&test, &test, 1);
+ mpi_divi(&test, &test, 2);
+ if (mpi_cmp(rem, &test) >= 0)
+ state = NDIVIDE_ADD;
+ }
+ else {
+ mpi_neg(&test, &test);
+ mpi_subi(&test, &test, 1);
+ mpi_divi(&test, &test, 2);
+ if (mpi_cmp(rem, &test) <= 0)
+ state = NDIVIDE_SUB;
+ }
+ }
+ else {
+ if (rsign > 0) {
+ mpi_neg(&test, &test);
+ mpi_addi(&test, &test, 1);
+ mpi_divi(&test, &test, 2);
+ if (mpi_cmp(rem, &test) >= 0)
+ state = NDIVIDE_SUB;
+ }
+ else {
+ mpi_subi(&test, &test, 1);
+ mpi_divi(&test, &test, 2);
+ if (mpi_cmp(rem, &test) <= 0)
+ state = NDIVIDE_ADD;
+ }
+ }
+ mpi_clear(&test);
+ } break;
+ }
+
+ if (state == NDIVIDE_ADD) {
+ mpi_addi(quo, quo, 1);
+ switch (type) {
+ case LispFixnum_t:
+ mpi_subi(rem, rem, OFI(div));
+ break;
+ case LispInteger_t:
+ mpi_subi(rem, rem, OII(div));
+ break;
+ default:
+ mpi_sub(rem, rem, OBI(div));
+ }
+ }
+ else if (state == NDIVIDE_SUB) {
+ mpi_subi(quo, quo, 1);
+ switch (type) {
+ case LispFixnum_t:
+ mpi_addi(rem, rem, OFI(div));
+ break;
+ case LispInteger_t:
+ mpi_addi(rem, rem, OII(div));
+ break;
+ default:
+ mpi_add(rem, rem, OBI(div));
+ }
+ }
+
+ if (mpi_fiti(rem)) {
+ NITYPE(num) = N_FIXNUM;
+ NIFI(num) = mpi_geti(rem);
+ mpi_clear(rem);
+ XFREE(rem);
+ }
+ else {
+ NITYPE(num) = N_BIGNUM;
+ NIBI(num) = rem;
+ }
+
+ clear_real(NREAL(num));
+
+ if (flo) {
+ double dval = bi_getd(quo);
+
+ mpi_clear(quo);
+ XFREE(quo);
+ NRTYPE(num) = N_FLONUM;
+ NRFF(num) = dval;
+ }
+ else {
+ NRTYPE(num) = N_BIGNUM;
+ NRBI(num) = quo;
+ rbi_canonicalize(NREAL(num));
+ }
+}
+
+static void
+ndivide_flonum(n_number *number, double num, double div, int fun, int flo)
+{
+ double quo, rem, modp, tmp;
+
+ modp = modf(num / div, &quo);
+ rem = num - quo * div;
+
+ switch (fun) {
+ case NDIVIDE_CEIL:
+ if ((rem < 0.0 && div < 0.0) || (rem > 0.0 && div > 0.0)) {
+ quo += 1.0;
+ rem -= div;
+ }
+ break;
+ case NDIVIDE_FLOOR:
+ if ((rem < 0.0 && div > 0.0) || (rem > 0.0 && div < 0.0)) {
+ quo -= 1.0;
+ rem += div;
+ }
+ break;
+ case NDIVIDE_ROUND:
+ if (fabs(modp) != 0.5 || modf(quo * 0.5, &tmp) != 0.0) {
+ if (div > 0.0) {
+ if (rem > 0.0) {
+ if (rem >= div * 0.5) {
+ quo += 1.0;
+ rem -= div;
+ }
+ }
+ else {
+ if (rem <= div * -0.5) {
+ quo -= 1.0;
+ rem += div;
+ }
+ }
+ }
+ else {
+ if (rem > 0.0) {
+ if (rem >= div * -0.5) {
+ quo -= 1.0;
+ rem += div;
+ }
+ }
+ else {
+ if (rem <= div * 0.5) {
+ quo += 1.0;
+ rem -= div;
+ }
+ }
+ }
+ }
+ break;
+ }
+ if (!finite(quo) || !finite(rem))
+ fatal_error(FLOATING_POINT_OVERFLOW);
+
+ NITYPE(number) = N_FLONUM;
+ NIFF(number) = rem;
+
+ clear_real(NREAL(number));
+
+ if (flo) {
+ NRTYPE(number) = N_FLONUM;
+ NRFF(number) = quo;
+ }
+ else {
+ if ((long)quo == quo) {
+ NRTYPE(number) = N_FIXNUM;
+ NRFI(number) = (long)quo;
+ }
+ else {
+ mpi *bigi = XALLOC(mpi);
+
+ mpi_init(bigi);
+ mpi_setd(bigi, quo);
+ NRBI(number) = bigi;
+ NRTYPE(number) = N_BIGNUM;
+ }
+ }
+}
+
+static void
+ndivide_xi_xr(n_number *num, LispObj *div, int fun, int flo)
+{
+ int state = NDIVIDE_NOP, dsign, rsign;
+ mpi *quo;
+ mpr *rem;
+
+ quo = XALLOC(mpi);
+ mpi_init(quo);
+ if (NRTYPE(num) == N_FIXNUM)
+ mpi_seti(quo, NRFI(num));
+ else
+ mpi_set(quo, NRBI(num));
+
+ rem = XALLOC(mpr);
+ mpr_init(rem);
+
+ if (XOBJECT_TYPE(div) == LispRatio_t)
+ mpr_seti(rem, OFRN(div), OFRD(div));
+ else
+ mpr_set(rem, OBR(div));
+ dsign = mpi_sgn(mpr_num(rem));
+ mpi_mul(quo, quo, mpr_den(rem));
+
+ mpi_divqr(quo, mpr_num(rem), quo, mpr_num(rem));
+ mpr_canonicalize(rem);
+
+ rsign = mpi_sgn(mpr_num(rem));
+ if (mpr_fiti(rem)) {
+ if (mpi_geti(mpr_den(rem)) == 1) {
+ NITYPE(num) = N_FIXNUM;
+ NIFI(num) = mpi_geti(mpr_num(rem));
+ }
+ else {
+ NITYPE(num) = N_FIXRATIO;
+ NIFRN(num) = mpi_geti(mpr_num(rem));
+ NIFRD(num) = mpi_geti(mpr_den(rem));
+ }
+ mpr_clear(rem);
+ XFREE(rem);
+ }
+ else {
+ if (mpi_fiti(mpr_den(rem)) && mpi_geti(mpr_den(rem)) == 1) {
+ NITYPE(num) = N_BIGNUM;
+ NIBI(num) = mpr_num(rem);
+ mpi_clear(mpr_den(rem));
+ XFREE(rem);
+ }
+ else {
+ NITYPE(num) = N_BIGRATIO;
+ NIBR(num) = rem;
+ }
+ }
+
+ switch (fun) {
+ case NDIVIDE_CEIL:
+ if ((rsign < 0 && dsign < 0) || (rsign > 0 && dsign > 0))
+ state = NDIVIDE_ADD;
+ break;
+ case NDIVIDE_FLOOR:
+ if ((rsign < 0 && dsign > 0) || (rsign > 0 && dsign < 0))
+ state = NDIVIDE_SUB;
+ break;
+ case NDIVIDE_ROUND: {
+ n_real cmp;
+
+ set_real_object(&cmp, div);
+ div_real_real(&cmp, &two);
+ if (dsign > 0) {
+ if (rsign > 0) {
+ if (cmp_real_real(NIMAG(num), &cmp) >= 0)
+ state = NDIVIDE_ADD;
+ }
+ else {
+ neg_real(&cmp);
+ if (cmp_real_real(NIMAG(num), &cmp) <= 0)
+ state = NDIVIDE_SUB;
+ }
+ }
+ else {
+ if (rsign > 0) {
+ neg_real(&cmp);
+ if (cmp_real_real(NIMAG(num), &cmp) >= 0)
+ state = NDIVIDE_SUB;
+ }
+ else {
+ if (cmp_real_real(NIMAG(num), &cmp) <= 0)
+ state = NDIVIDE_ADD;
+ }
+ }
+ clear_real(&cmp);
+ } break;
+ }
+
+ if (state == NDIVIDE_ADD) {
+ mpi_addi(quo, quo, 1);
+ sub_real_object(NIMAG(num), div);
+ }
+ else if (state == NDIVIDE_SUB) {
+ mpi_subi(quo, quo, 1);
+ add_real_object(NIMAG(num), div);
+ }
+
+ clear_real(NREAL(num));
+
+ if (flo) {
+ double dval = bi_getd(quo);
+
+ mpi_clear(quo);
+ XFREE(quo);
+ NRTYPE(num) = N_FLONUM;
+ NRFF(num) = dval;
+ }
+ else {
+ NRBI(num) = quo;
+ NRTYPE(num) = N_BIGNUM;
+ rbi_canonicalize(NREAL(num));
+ }
+}
+
+static void
+ndivide_xr_xi(n_number *num, LispObj *div, int fun, int flo)
+{
+ LispType type = OBJECT_TYPE(div);
+ int state = NDIVIDE_NOP, dsign, rsign;
+ mpi *quo;
+ mpr *rem;
+
+ quo = XALLOC(mpi);
+ mpi_init(quo);
+ switch (type) {
+ case LispFixnum_t:
+ dsign = OFI(div) < 0 ? -1 : OFI(div) > 0 ? 1 : 0;
+ mpi_seti(quo, OFI(div));
+ break;
+ case LispInteger_t:
+ dsign = OII(div) < 0 ? -1 : OII(div) > 0 ? 1 : 0;
+ mpi_seti(quo, OII(div));
+ break;
+ default:
+ dsign = mpi_sgn(OBI(div));
+ mpi_set(quo, OBI(div));
+ break;
+ }
+
+ rem = XALLOC(mpr);
+ mpr_init(rem);
+ if (NRTYPE(num) == N_FIXRATIO) {
+ mpr_seti(rem, NRFRN(num), NRFRD(num));
+ mpi_muli(quo, quo, NRFRD(num));
+ }
+ else {
+ mpr_set(rem, NRBR(num));
+ mpi_mul(quo, quo, NRBRD(num));
+ }
+ mpi_divqr(quo, mpr_num(rem), mpr_num(rem), quo);
+ mpr_canonicalize(rem);
+
+ rsign = mpi_sgn(mpr_num(rem));
+ if (mpr_fiti(rem)) {
+ NITYPE(num) = N_FIXRATIO;
+ NIFRN(num) = mpi_geti(mpr_num(rem));
+ NIFRD(num) = mpi_geti(mpr_den(rem));
+ mpr_clear(rem);
+ XFREE(rem);
+ }
+ else {
+ NITYPE(num) = N_BIGRATIO;
+ NIBR(num) = rem;
+ }
+
+ switch (fun) {
+ case NDIVIDE_CEIL:
+ if ((rsign < 0 && dsign < 0) || (rsign > 0 && dsign > 0))
+ state = NDIVIDE_ADD;
+ break;
+ case NDIVIDE_FLOOR:
+ if ((rsign < 0 && dsign > 0) || (rsign > 0 && dsign < 0))
+ state = NDIVIDE_SUB;
+ break;
+ case NDIVIDE_ROUND: {
+ n_real cmp;
+
+ set_real_object(&cmp, div);
+ div_real_real(&cmp, &two);
+ if (dsign > 0) {
+ if (rsign > 0) {
+ if (cmp_real_real(NIMAG(num), &cmp) >= 0)
+ state = NDIVIDE_ADD;
+ }
+ else {
+ neg_real(&cmp);
+ if (cmp_real_real(NIMAG(num), &cmp) <= 0)
+ state = NDIVIDE_SUB;
+ }
+ }
+ else {
+ if (rsign > 0) {
+ neg_real(&cmp);
+ if (cmp_real_real(NIMAG(num), &cmp) >= 0)
+ state = NDIVIDE_SUB;
+ }
+ else {
+ if (cmp_real_real(NIMAG(num), &cmp) <= 0)
+ state = NDIVIDE_ADD;
+ }
+ }
+ clear_real(&cmp);
+ } break;
+ }
+
+ if (state == NDIVIDE_ADD) {
+ mpi_addi(quo, quo, 1);
+ sub_real_object(NIMAG(num), div);
+ }
+ else if (state == NDIVIDE_SUB) {
+ mpi_subi(quo, quo, 1);
+ add_real_object(NIMAG(num), div);
+ }
+
+ clear_real(NREAL(num));
+
+ if (flo) {
+ double dval = bi_getd(quo);
+
+ mpi_clear(quo);
+ XFREE(quo);
+ NRTYPE(num) = N_FLONUM;
+ NRFF(num) = dval;
+ }
+ else {
+ NRBI(num) = quo;
+ NRTYPE(num) = N_BIGNUM;
+ rbi_canonicalize(NREAL(num));
+ }
+}
+
+static void
+ndivide_xr_xr(n_number *num, LispObj *div, int fun, int flo)
+{
+ int state = NDIVIDE_NOP, dsign, rsign, modp;
+ mpr *bigr;
+ mpi *bigi;
+
+ bigr = XALLOC(mpr);
+ mpr_init(bigr);
+ if (NRTYPE(num) == N_FIXRATIO)
+ mpr_seti(bigr, NRFRN(num), NRFRD(num));
+ else
+ mpr_set(bigr, NRBR(num));
+
+ NITYPE(num) = N_BIGRATIO;
+ NIBR(num) = bigr;
+
+ if (OBJECT_TYPE(div) == LispRatio_t) {
+ dsign = OFRN(div) < 0 ? -1 : OFRN(div) > 0 ? 1 : 0;
+ mpi_muli(mpr_num(bigr), mpr_num(bigr), OFRD(div));
+ mpi_muli(mpr_den(bigr), mpr_den(bigr), OFRN(div));
+ }
+ else {
+ dsign = mpi_sgn(OBRN(div));
+ mpr_div(bigr, bigr, OBR(div));
+ }
+ modp = mpi_fiti(mpr_den(bigr)) && mpi_geti(mpr_den(bigr)) == 2;
+
+ bigi = XALLOC(mpi);
+ mpi_init(bigi);
+ mpi_divqr(bigi, mpr_num(bigr), mpr_num(bigr), mpr_den(bigr));
+
+ if (OBJECT_TYPE(div) == LispRatio_t)
+ mpi_seti(mpr_den(bigr), OFRD(div));
+ else
+ mpi_set(mpr_den(bigr), OBRD(div));
+ if (NRTYPE(num) == N_FIXRATIO)
+ mpi_muli(mpr_den(bigr), mpr_den(bigr), NRFRD(num));
+ else
+ mpi_mul(mpr_den(bigr), mpr_den(bigr), NRBRD(num));
+
+ clear_real(NREAL(num));
+ NRTYPE(num) = N_BIGNUM;
+ NRBI(num) = bigi;
+
+ rbr_canonicalize(NIMAG(num));
+ rsign = cmp_real_real(NIMAG(num), &zero);
+
+ switch (fun) {
+ case NDIVIDE_CEIL:
+ if ((rsign < 0 && dsign < 0) || (rsign > 0 && dsign > 0))
+ state = NDIVIDE_ADD;
+ break;
+ case NDIVIDE_FLOOR:
+ if ((rsign < 0 && dsign > 0) || (rsign > 0 && dsign < 0))
+ state = NDIVIDE_SUB;
+ break;
+ case NDIVIDE_ROUND:
+ if (!modp || (bigi->digs[0] & 1) == 1) {
+ n_real cmp;
+
+ set_real_object(&cmp, div);
+ div_real_real(&cmp, &two);
+ if (dsign > 0) {
+ if (rsign > 0) {
+ if (cmp_real_real(NIMAG(num), &cmp) >= 0)
+ state = NDIVIDE_ADD;
+ }
+ else {
+ neg_real(&cmp);
+ if (cmp_real_real(NIMAG(num), &cmp) <= 0)
+ state = NDIVIDE_SUB;
+ }
+ }
+ else {
+ if (rsign > 0) {
+ neg_real(&cmp);
+ if (cmp_real_real(NIMAG(num), &cmp) >= 0)
+ state = NDIVIDE_SUB;
+ }
+ else {
+ if (cmp_real_real(NIMAG(num), &cmp) <= 0)
+ state = NDIVIDE_ADD;
+ }
+ }
+ clear_real(&cmp);
+ }
+ break;
+ }
+
+ if (state == NDIVIDE_ADD) {
+ add_real_real(NREAL(num), &one);
+ sub_real_object(NIMAG(num), div);
+ }
+ else if (state == NDIVIDE_SUB) {
+ sub_real_real(NREAL(num), &one);
+ add_real_object(NIMAG(num), div);
+ }
+
+ if (NRTYPE(num) == N_BIGNUM) {
+ if (flo) {
+ double dval = bi_getd(bigi);
+
+ mpi_clear(bigi);
+ XFREE(bigi);
+ NRTYPE(num) = N_FLONUM;
+ NRFF(num) = dval;
+ }
+ else
+ rbi_canonicalize(NREAL(num));
+ }
+ else if (flo) {
+ NRTYPE(num) = N_FLONUM;
+ NRFF(num) = (double)NRFI(num);
+ }
+}
+
+
+/************************************************************************
+ * REAL COMPLEX
+ ************************************************************************/
+static void
+nadd_re_cx(n_number *num, LispObj *comp)
+{
+/*
+ Ra+Rb Ib
+ */
+ /* Ra+Rb */
+ add_real_object(NREAL(num), OCXR(comp));
+
+ /* Ib */
+ set_real_object(NIMAG(num), OCXI(comp));
+
+ num->complex = 1;
+
+ ncx_canonicalize(num);
+}
+
+static void
+nsub_re_cx(n_number *num, LispObj *comp)
+{
+/*
+ Ra-Rb -Ib
+ */
+ /* Ra-Rb */
+ sub_real_object(NREAL(num), OCXR(comp));
+
+ /* -Ib */
+ NITYPE(num) = N_FIXNUM;
+ NIFI(num) = -1;
+ mul_real_object(NIMAG(num), OCXI(comp));
+
+ num->complex = 1;
+
+ ncx_canonicalize(num);
+}
+
+static void
+nmul_re_cx(n_number *num, LispObj *comp)
+{
+/*
+ Ra*Rb Ra*Ib
+ */
+ /* copy before change */
+ set_real_real(NIMAG(num), NREAL(num));
+
+ /* Ra*Rb */
+ mul_real_object(NREAL(num), OCXR(comp));
+
+ /* Ra*Ib */
+ mul_real_object(NIMAG(num), OCXI(comp));
+
+ num->complex = 1;
+
+ ncx_canonicalize(num);
+}
+
+static void
+ndiv_re_cx(n_number *num, LispObj *comp)
+{
+/*
+ Ra*Rb -Ib*Ra
+ ----------- -----------
+ Rb*Rb+Ib*Ib Rb*Rb+Ib*Ib
+ */
+ n_real div, temp;
+
+ /* Rb*Rb */
+ set_real_object(&div, OCXR(comp));
+ mul_real_object(&div, OCXR(comp));
+
+ /* Ib*Ib */
+ set_real_object(&temp, OCXI(comp));
+ mul_real_object(&temp, OCXI(comp));
+
+ /* Rb*Rb+Ib*Ib */
+ add_real_real(&div, &temp);
+ clear_real(&temp);
+
+ /* -Ib*Ra */
+ NITYPE(num) = N_FIXNUM;
+ NIFI(num) = -1;
+ mul_real_object(NIMAG(num), OCXI(comp));
+ mul_real_real(NIMAG(num), NREAL(num));
+
+ /* Ra*Rb */
+ mul_real_object(NREAL(num), OCXR(comp));
+
+ div_real_real(NREAL(num), &div);
+ div_real_real(NIMAG(num), &div);
+ clear_real(&div);
+
+ num->complex = 1;
+
+ ncx_canonicalize(num);
+}
+
+
+/************************************************************************
+ * COMPLEX REAL
+ ************************************************************************/
+static void
+nadd_cx_re(n_number *num, LispObj *re)
+{
+/*
+ Ra+Rb Ia
+ */
+ add_real_object(NREAL(num), re);
+
+ ncx_canonicalize(num);
+}
+
+static void
+nsub_cx_re(n_number *num, LispObj *re)
+{
+/*
+ Ra-Rb Ia
+ */
+ sub_real_object(NREAL(num), re);
+
+ ncx_canonicalize(num);
+}
+
+static void
+nmul_cx_re(n_number *num, LispObj *re)
+{
+/*
+ Ra*Rb Ia*Rb
+ */
+ mul_real_object(NREAL(num), re);
+ mul_real_object(NIMAG(num), re);
+
+ ncx_canonicalize(num);
+}
+
+static void
+ndiv_cx_re(n_number *num, LispObj *re)
+{
+/*
+ Ra/Rb Ia/Rb
+ */
+ div_real_object(NREAL(num), re);
+ div_real_object(NIMAG(num), re);
+
+ ncx_canonicalize(num);
+}
+
+
+/************************************************************************
+ * COMPLEX COMPLEX
+ ************************************************************************/
+static void
+nadd_cx_cx(n_number *num, LispObj *comp)
+{
+/*
+ Ra+Rb Ia+Ib
+ */
+ add_real_object(NREAL(num), OCXR(comp));
+ add_real_object(NIMAG(num), OCXI(comp));
+
+ ncx_canonicalize(num);
+}
+
+static void
+nsub_cx_cx(n_number *num, LispObj *comp)
+{
+/*
+ Ra-Rb Ia-Ib
+ */
+ sub_real_object(NREAL(num), OCXR(comp));
+ sub_real_object(NIMAG(num), OCXI(comp));
+
+ ncx_canonicalize(num);
+}
+
+static void
+nmul_cx_cx(n_number *num, LispObj *comp)
+{
+/*
+ Ra*Rb-Ia*Ib Ra*Ib+Ia*Rb
+ */
+ n_real IaIb, RaIb;
+
+ set_real_real(&IaIb, NIMAG(num));
+ mul_real_object(&IaIb, OCXI(comp));
+
+ set_real_real(&RaIb, NREAL(num));
+ mul_real_object(&RaIb, OCXI(comp));
+
+ /* Ra*Rb-Ia*Ib */
+ mul_real_object(NREAL(num), OCXR(comp));
+ sub_real_real(NREAL(num), &IaIb);
+ clear_real(&IaIb);
+
+ /* Ra*Ib+Ia*Rb */
+ mul_real_object(NIMAG(num), OCXR(comp));
+ add_real_real(NIMAG(num), &RaIb);
+ clear_real(&RaIb);
+
+ ncx_canonicalize(num);
+}
+
+static void
+ndiv_cx_cx(n_number *num, LispObj *comp)
+{
+/*
+ Ra*Rb+Ia*Ib Ia*Rb-Ib*Ra
+ ----------- -----------
+ Rb*Rb+Ib*Ib Rb*Rb+Ib*Ib
+ */
+ n_real temp1, temp2;
+
+ /* IaIb */
+ set_real_real(&temp1, NIMAG(num));
+ mul_real_object(&temp1, OCXI(comp));
+
+ /* IbRa */
+ set_real_real(&temp2, NREAL(num));
+ mul_real_object(&temp2, OCXI(comp));
+
+ /* Ra*Rb+Ia*Ib */
+ mul_real_object(NREAL(num), OCXR(comp));
+ add_real_real(NREAL(num), &temp1);
+ clear_real(&temp1);
+
+ /* Ia*Rb-Ib*Ra */
+ mul_real_object(NIMAG(num), OCXR(comp));
+ sub_real_real(NIMAG(num), &temp2);
+ clear_real(&temp2);
+
+
+ /* Rb*Rb */
+ set_real_object(&temp1, OCXR(comp));
+ mul_real_object(&temp1, OCXR(comp));
+
+ /* Ib*Ib */
+ set_real_object(&temp2, OCXI(comp));
+ mul_real_object(&temp2, OCXI(comp));
+
+ /* Rb*Rb+Ib*Ib */
+ add_real_real(&temp1, &temp2);
+ clear_real(&temp2);
+
+ div_real_real(NREAL(num), &temp1);
+ div_real_real(NIMAG(num), &temp1);
+ clear_real(&temp1);
+
+ ncx_canonicalize(num);
+}
+
+static int
+cmp_cx_cx(LispObj *op1, LispObj *op2)
+{
+ int cmp;
+
+ cmp = cmp_object_object(OCXR(op1), OCXR(op2), 1);
+ if (cmp == 0)
+ cmp = cmp_object_object(OCXI(op1), OCXI(op2), 1);
+
+ return (cmp);
+}
+
+
+/************************************************************************
+ * FLONUM FLONUM
+ ************************************************************************/
+static void
+radd_flonum(n_real *real, double op1, double op2)
+{
+ double value = op1 + op2;
+
+ if (!finite(value))
+ fatal_error(FLOATING_POINT_OVERFLOW);
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ case N_FIXRATIO:
+ RTYPE(real) = N_FLONUM;
+ break;
+ case N_BIGNUM:
+ RCLEAR_BI(real);
+ RTYPE(real) = N_FLONUM;
+ break;
+ case N_BIGRATIO:
+ RCLEAR_BR(real);
+ RTYPE(real) = N_FLONUM;
+ break;
+ }
+ RFF(real) = value;
+}
+
+static void
+rsub_flonum(n_real *real, double op1, double op2)
+{
+ double value = op1 - op2;
+
+ if (!finite(value))
+ fatal_error(FLOATING_POINT_OVERFLOW);
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ case N_FIXRATIO:
+ RTYPE(real) = N_FLONUM;
+ break;
+ case N_BIGNUM:
+ RCLEAR_BI(real);
+ RTYPE(real) = N_FLONUM;
+ break;
+ case N_BIGRATIO:
+ RCLEAR_BR(real);
+ RTYPE(real) = N_FLONUM;
+ break;
+ }
+ RFF(real) = value;
+}
+
+static void
+rmul_flonum(n_real *real, double op1, double op2)
+{
+ double value = op1 * op2;
+
+ if (!finite(value))
+ fatal_error(FLOATING_POINT_OVERFLOW);
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ case N_FIXRATIO:
+ RTYPE(real) = N_FLONUM;
+ break;
+ case N_BIGNUM:
+ RCLEAR_BI(real);
+ RTYPE(real) = N_FLONUM;
+ break;
+ case N_BIGRATIO:
+ RCLEAR_BR(real);
+ RTYPE(real) = N_FLONUM;
+ break;
+ }
+ RFF(real) = value;
+}
+
+static void
+rdiv_flonum(n_real *real, double op1, double op2)
+{
+ double value;
+
+ if (op2 == 0.0)
+ fatal_error(DIVIDE_BY_ZERO);
+ value = op1 / op2;
+ if (!finite(value))
+ fatal_error(FLOATING_POINT_OVERFLOW);
+ switch (RTYPE(real)) {
+ case N_FIXNUM:
+ case N_FIXRATIO:
+ RTYPE(real) = N_FLONUM;
+ break;
+ case N_BIGNUM:
+ RCLEAR_BI(real);
+ RTYPE(real) = N_FLONUM;
+ break;
+ case N_BIGRATIO:
+ RCLEAR_BR(real);
+ RTYPE(real) = N_FLONUM;
+ break;
+ }
+ RFF(real) = value;
+}
+
+static int
+cmp_flonum(double op1, double op2)
+{
+ double value = op1 - op2;
+
+ if (!finite(value))
+ fatal_error(FLOATING_POINT_OVERFLOW);
+
+ return (value > 0.0 ? 1 : value < 0.0 ? -1 : 0);
+}
+
+
+/************************************************************************
+ * FIXNUM FIXNUM
+ ************************************************************************/
+static void
+rop_fi_fi_bi(n_real *real, long fi, int op)
+{
+ mpi *bigi = XALLOC(mpi);
+
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ if (op == NOP_ADD)
+ mpi_addi(bigi, bigi, fi);
+ else if (op == NOP_SUB)
+ mpi_subi(bigi, bigi, fi);
+ else
+ mpi_muli(bigi, bigi, fi);
+ RBI(real) = bigi;
+ RTYPE(real) = N_BIGNUM;
+}
+
+static INLINE void
+radd_fi_fi(n_real *real, long fi)
+{
+ if (!fi_fi_add_overflow(RFI(real), fi))
+ RFI(real) += fi;
+ else
+ rop_fi_fi_bi(real, fi, NOP_ADD);
+}
+
+static INLINE void
+rsub_fi_fi(n_real *real, long fi)
+{
+ if (!fi_fi_sub_overflow(RFI(real), fi))
+ RFI(real) -= fi;
+ else
+ rop_fi_fi_bi(real, fi, NOP_SUB);
+}
+
+static INLINE void
+rmul_fi_fi(n_real *real, long fi)
+{
+ if (!fi_fi_mul_overflow(RFI(real), fi))
+ RFI(real) *= fi;
+ else
+ rop_fi_fi_bi(real, fi, NOP_MUL);
+}
+
+static INLINE void
+rdiv_fi_fi(n_real *real, long fi)
+{
+ RTYPE(real) = N_FIXRATIO;
+ RFRN(real) = RFI(real);
+ RFRD(real) = fi;
+ rfr_canonicalize(real);
+}
+
+static INLINE int
+cmp_fi_fi(long op1, long op2)
+{
+ if (op1 > op2)
+ return (1);
+ else if (op1 < op2)
+ return (-1);
+
+ return (0);
+}
+
+
+/************************************************************************
+ * FIXNUM BIGNUM
+ ************************************************************************/
+static void
+rop_fi_bi_xi(n_real *real, mpi *bi, int nop)
+{
+ mpi *bigi = XALLOC(mpi);
+
+ mpi_init(bigi);
+ mpi_seti(bigi, RFI(real));
+ if (nop == NOP_ADD)
+ mpi_add(bigi, bigi, bi);
+ else if (nop == NOP_SUB)
+ mpi_sub(bigi, bigi, bi);
+ else
+ mpi_mul(bigi, bigi, bi);
+
+ if (mpi_fiti(bigi)) {
+ RFI(real) = mpi_geti(bigi);
+ mpi_clear(bigi);
+ XFREE(bigi);
+ }
+ else {
+ RBI(real) = bigi;
+ RTYPE(real) = N_BIGNUM;
+ }
+}
+
+static INLINE void
+radd_fi_bi(n_real *real, mpi *bi)
+{
+ rop_fi_bi_xi(real, bi, NOP_ADD);
+}
+
+static INLINE void
+rsub_fi_bi(n_real *real, mpi *bi)
+{
+ rop_fi_bi_xi(real, bi, NOP_SUB);
+}
+
+static INLINE void
+rmul_fi_bi(n_real *real, mpi *bi)
+{
+ rop_fi_bi_xi(real, bi, NOP_MUL);
+}
+
+static void
+rdiv_fi_bi(n_real *real, mpi *bi)
+{
+ mpr *bigr;
+
+ if (mpi_cmpi(bi, 0) == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ bigr = XALLOC(mpr);
+ mpr_init(bigr);
+ mpi_seti(mpr_num(bigr), RFI(real));
+ mpi_set(mpr_den(bigr), bi);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE int
+cmp_fi_bi(long fixnum, mpi *bignum)
+{
+ return (-mpi_cmpi(bignum, fixnum));
+}
+
+
+/************************************************************************
+ * FIXNUM FIXRATIO
+ ************************************************************************/
+static void
+rop_fi_fr_as_xr(n_real *real, long num, long den, int nop)
+{
+ int fit;
+ long value = 0, op = RFI(real);
+
+ fit = !fi_fi_mul_overflow(op, den);
+ if (fit) {
+ value = op * den;
+ if (nop == NOP_ADD)
+ fit = !fi_fi_add_overflow(value, num);
+ else
+ fit = !fi_fi_sub_overflow(value, num);
+ }
+ if (fit) {
+ if (nop == NOP_ADD)
+ RFRN(real) = value + num;
+ else
+ RFRN(real) = value - num;
+ RFRD(real) = den;
+ RTYPE(real) = N_FIXRATIO;
+ rfr_canonicalize(real);
+ }
+ else {
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpi_init(&iop);
+ mpi_seti(&iop, op);
+ mpi_muli(&iop, &iop, den);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, num, den);
+ if (nop == NOP_ADD)
+ mpi_add(mpr_num(bigr), &iop, mpr_num(bigr));
+ else
+ mpi_sub(mpr_num(bigr), &iop, mpr_num(bigr));
+ mpi_clear(&iop);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+ }
+}
+
+static void
+rop_fi_fr_md_xr(n_real *real, long num, long den, int nop)
+{
+ int fit;
+ long op = RFI(real);
+
+ if (nop == NOP_MUL)
+ fit = !fi_fi_mul_overflow(op, num);
+ else
+ fit = !fi_fi_mul_overflow(op, den);
+ if (fit) {
+ if (nop == NOP_MUL) {
+ RFRN(real) = op * num;
+ RFRD(real) = den;
+ }
+ else {
+ RFRN(real) = op * den;
+ RFRD(real) = num;
+ }
+ RTYPE(real) = N_FIXRATIO;
+ rfr_canonicalize(real);
+ }
+ else {
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpi_init(&iop);
+ mpi_seti(&iop, op);
+
+ mpr_init(bigr);
+ if (nop == NOP_MUL)
+ mpr_seti(bigr, num, den);
+ else
+ mpr_seti(bigr, den, num);
+ mpi_mul(mpr_num(bigr), mpr_num(bigr), &iop);
+ mpi_clear(&iop);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+ }
+}
+
+static INLINE void
+radd_fi_fr(n_real *real, long num, long den)
+{
+ rop_fi_fr_as_xr(real, num, den, NOP_ADD);
+}
+
+static INLINE void
+rsub_fi_fr(n_real *real, long num, long den)
+{
+ rop_fi_fr_as_xr(real, num, den, NOP_SUB);
+}
+
+static INLINE void
+rmul_fi_fr(n_real *real, long num, long den)
+{
+ rop_fi_fr_md_xr(real, num, den, NOP_MUL);
+}
+
+static INLINE void
+rdiv_fi_fr(n_real *real, long num, long den)
+{
+ rop_fi_fr_md_xr(real, num, den, NOP_DIV);
+}
+
+static INLINE int
+cmp_fi_fr(long fi, long num, long den)
+{
+ return (cmp_flonum((double)fi, (double)num / (double)den));
+}
+
+
+/************************************************************************
+ * FIXNUM BIGRATIO
+ ************************************************************************/
+static void
+rop_fi_br_as_xr(n_real *real, mpr *ratio, int nop)
+{
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpi_init(&iop);
+ mpi_seti(&iop, RFI(real));
+
+ mpr_init(bigr);
+ mpr_set(bigr, ratio);
+
+ mpi_mul(&iop, &iop, mpr_den(ratio));
+ if (nop == NOP_ADD)
+ mpi_add(mpr_num(bigr), &iop, mpr_num(bigr));
+ else
+ mpi_sub(mpr_num(bigr), &iop, mpr_num(bigr));
+
+ mpi_clear(&iop);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static void
+rop_fi_br_md_xr(n_real *real, mpr *ratio, int nop)
+{
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpi_init(&iop);
+ mpi_seti(&iop, RFI(real));
+
+ mpr_init(bigr);
+ if (nop == NOP_MUL)
+ mpr_set(bigr, ratio);
+ else
+ mpr_inv(bigr, ratio);
+
+ mpi_mul(mpr_num(bigr), &iop, mpr_num(bigr));
+
+ mpi_clear(&iop);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+radd_fi_br(n_real *real, mpr *ratio)
+{
+ rop_fi_br_as_xr(real, ratio, NOP_ADD);
+}
+
+static INLINE void
+rsub_fi_br(n_real *real, mpr *ratio)
+{
+ rop_fi_br_as_xr(real, ratio, NOP_SUB);
+}
+
+static INLINE void
+rmul_fi_br(n_real *real, mpr *ratio)
+{
+ rop_fi_br_md_xr(real, ratio, NOP_MUL);
+}
+
+static INLINE void
+rdiv_fi_br(n_real *real, mpr *ratio)
+{
+ rop_fi_br_md_xr(real, ratio, NOP_DIV);
+}
+
+static INLINE int
+cmp_fi_br(long op1, mpr *op2)
+{
+ return (-mpr_cmpi(op2, op1));
+}
+
+
+/************************************************************************
+ * BIGNUM FIXNUM
+ ************************************************************************/
+static INLINE void
+radd_bi_fi(n_real *real, long fi)
+{
+ mpi_addi(RBI(real), RBI(real), fi);
+ rbi_canonicalize(real);
+}
+
+static INLINE void
+rsub_bi_fi(n_real *real, long fi)
+{
+ mpi_subi(RBI(real), RBI(real), fi);
+ rbi_canonicalize(real);
+}
+
+static INLINE void
+rmul_bi_fi(n_real *real, long fi)
+{
+ mpi_muli(RBI(real), RBI(real), fi);
+ rbi_canonicalize(real);
+}
+
+static void
+rdiv_bi_fi(n_real *real, long fi)
+{
+ mpr *bigr;
+
+ if (RFI(real) == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ bigr = XALLOC(mpr);
+ mpr_init(bigr);
+ mpi_set(mpr_num(bigr), RBI(real));
+ mpi_seti(mpr_den(bigr), fi);
+ RCLEAR_BI(real);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE int
+cmp_bi_fi(mpi *bignum, long fi)
+{
+ return (mpi_cmpi(bignum, fi));
+}
+
+
+/************************************************************************
+ * BIGNUM BIGNUM
+ ************************************************************************/
+static INLINE void
+radd_bi_bi(n_real *real, mpi *bignum)
+{
+ mpi_add(RBI(real), RBI(real), bignum);
+ rbi_canonicalize(real);
+}
+
+static INLINE void
+rsub_bi_bi(n_real *real, mpi *bignum)
+{
+ mpi_sub(RBI(real), RBI(real), bignum);
+ rbi_canonicalize(real);
+}
+
+static INLINE void
+rmul_bi_bi(n_real *real, mpi *bignum)
+{
+ mpi_mul(RBI(real), RBI(real), bignum);
+ rbi_canonicalize(real);
+}
+
+static void
+rdiv_bi_bi(n_real *real, mpi *bignum)
+{
+ mpr *bigr;
+
+ if (mpi_cmpi(bignum, 0) == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+
+ bigr = XALLOC(mpr);
+ mpr_init(bigr);
+ mpi_set(mpr_num(bigr), RBI(real));
+ mpi_set(mpr_den(bigr), bignum);
+ RCLEAR_BI(real);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE int
+cmp_bi_bi(mpi *op1, mpi *op2)
+{
+ return (mpi_cmp(op1, op2));
+}
+
+
+/************************************************************************
+ * BIGNUM FIXRATIO
+ ************************************************************************/
+static void
+rop_bi_fr_as_xr(n_real *real, long num, long den, int nop)
+{
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpi_init(&iop);
+ mpi_set(&iop, RBI(real));
+ mpi_muli(&iop, &iop, den);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, num, den);
+
+ if (nop == NOP_ADD)
+ mpi_add(mpr_num(bigr), &iop, mpr_num(bigr));
+ else
+ mpi_sub(mpr_num(bigr), &iop, mpr_num(bigr));
+ mpi_clear(&iop);
+
+ RCLEAR_BI(real);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+rop_bi_fr_md_xr(n_real *real, long num, long den, int nop)
+{
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+
+ mpr_seti(bigr, num, den);
+
+ if (nop == NOP_MUL)
+ mpi_mul(mpr_num(bigr), RBI(real), mpr_num(bigr));
+ else {
+ mpi_mul(mpr_den(bigr), RBI(real), mpr_den(bigr));
+ mpr_inv(bigr, bigr);
+ }
+
+ RCLEAR_BI(real);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+radd_bi_fr(n_real *real, long num, long den)
+{
+ rop_bi_fr_as_xr(real, num, den, NOP_ADD);
+}
+
+static INLINE void
+rsub_bi_fr(n_real *real, long num, long den)
+{
+ rop_bi_fr_as_xr(real, num, den, NOP_SUB);
+}
+
+static INLINE void
+rmul_bi_fr(n_real *real, long num, long den)
+{
+ rop_bi_fr_md_xr(real, num, den, NOP_MUL);
+}
+
+static INLINE void
+rdiv_bi_fr(n_real *real, long num, long den)
+{
+ rop_bi_fr_md_xr(real, num, den, NOP_DIV);
+}
+
+static int
+cmp_bi_fr(mpi *bignum, long num, long den)
+{
+ int cmp;
+ mpr cmp1, cmp2;
+
+ mpr_init(&cmp1);
+ mpi_set(mpr_num(&cmp1), bignum);
+ mpi_seti(mpr_den(&cmp1), 1);
+
+ mpr_init(&cmp2);
+ mpr_seti(&cmp2, num, den);
+
+ cmp = mpr_cmp(&cmp1, &cmp2);
+ mpr_clear(&cmp1);
+ mpr_clear(&cmp2);
+
+ return (cmp);
+}
+
+
+/************************************************************************
+ * BIGNUM BIGRATIO
+ ************************************************************************/
+static void
+rop_bi_br_as_xr(n_real *real, mpr *bigratio, int nop)
+{
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpi_init(&iop);
+ mpi_set(&iop, RBI(real));
+ mpr_init(bigr);
+ mpr_set(bigr, bigratio);
+
+ mpi_mul(&iop, &iop, mpr_den(bigratio));
+
+ if (nop == NOP_ADD)
+ mpi_add(mpr_num(bigr), &iop, mpr_num(bigr));
+ else
+ mpi_sub(mpr_num(bigr), &iop, mpr_num(bigr));
+ mpi_clear(&iop);
+
+ RCLEAR_BI(real);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static void
+rop_bi_br_md_xr(n_real *real, mpr *bigratio, int nop)
+{
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ if (nop == NOP_MUL)
+ mpr_set(bigr, bigratio);
+ else
+ mpr_inv(bigr, bigratio);
+
+ mpi_mul(mpr_num(bigr), RBI(real), mpr_num(bigr));
+
+ RCLEAR_BI(real);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+radd_bi_br(n_real *real, mpr *bigratio)
+{
+ rop_bi_br_as_xr(real, bigratio, NOP_ADD);
+}
+
+static INLINE void
+rsub_bi_br(n_real *real, mpr *bigratio)
+{
+ rop_bi_br_as_xr(real, bigratio, NOP_SUB);
+}
+
+static INLINE void
+rmul_bi_br(n_real *real, mpr *bigratio)
+{
+ rop_bi_br_md_xr(real, bigratio, NOP_MUL);
+}
+
+static INLINE void
+rdiv_bi_br(n_real *real, mpr *bigratio)
+{
+ rop_bi_br_md_xr(real, bigratio, NOP_DIV);
+}
+
+static int
+cmp_bi_br(mpi *bignum, mpr *bigratio)
+{
+ int cmp;
+ mpr cmp1;
+
+ mpr_init(&cmp1);
+ mpi_set(mpr_num(&cmp1), bignum);
+ mpi_seti(mpr_den(&cmp1), 1);
+
+ cmp = mpr_cmp(&cmp1, bigratio);
+ mpr_clear(&cmp1);
+
+ return (cmp);
+}
+
+
+/************************************************************************
+ * FIXRATIO FIXNUM
+ ************************************************************************/
+static void
+rop_fr_fi_as_xr(n_real *real, long op, int nop)
+{
+ int fit;
+ long value = 0, num = RFRN(real), den = RFRD(real);
+
+ fit = !fi_fi_mul_overflow(op, den);
+
+ if (fit) {
+ value = op * den;
+ if (nop == NOP_ADD)
+ fit = !fi_fi_add_overflow(value, num);
+ else
+ fit = !fi_fi_sub_overflow(value, num);
+ }
+ if (fit) {
+ if (nop == NOP_ADD)
+ RFRN(real) = num + value;
+ else
+ RFRN(real) = num - value;
+ rfr_canonicalize(real);
+ }
+ else {
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, num, den);
+ mpi_init(&iop);
+ mpi_seti(&iop, op);
+ mpi_muli(&iop, &iop, den);
+ if (nop == NOP_ADD)
+ mpi_add(mpr_num(bigr), mpr_num(bigr), &iop);
+ else
+ mpi_sub(mpr_num(bigr), mpr_num(bigr), &iop);
+ mpi_clear(&iop);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+ }
+}
+
+static void
+rop_fr_fi_md_xr(n_real *real, long op, int nop)
+{
+ long num = RFRN(real), den = RFRD(real);
+
+ if (nop == NOP_MUL) {
+ if (!fi_fi_mul_overflow(op, num)) {
+ RFRN(real) = op * num;
+ rfr_canonicalize(real);
+ return;
+ }
+ }
+ else if (!fi_fi_mul_overflow(op, den)) {
+ RFRD(real) = op * den;
+ rfr_canonicalize(real);
+ return;
+ }
+
+ {
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, num, den);
+ if (nop == NOP_MUL)
+ mpr_muli(bigr, bigr, op);
+ else
+ mpr_divi(bigr, bigr, op);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+ }
+}
+
+static INLINE void
+radd_fr_fi(n_real *real, long op)
+{
+ rop_fr_fi_as_xr(real, op, NOP_ADD);
+}
+
+static INLINE void
+rsub_fr_fi(n_real *real, long op)
+{
+ rop_fr_fi_as_xr(real, op, NOP_SUB);
+}
+
+static INLINE void
+rmul_fr_fi(n_real *real, long op)
+{
+ rop_fr_fi_md_xr(real, op, NOP_MUL);
+}
+
+static INLINE void
+rdiv_fr_fi(n_real *real, long op)
+{
+ rop_fr_fi_md_xr(real, op, NOP_DIV);
+}
+
+static INLINE int
+cmp_fr_fi(long num, long den, long fixnum)
+{
+ return (cmp_flonum((double)num / (double)den, (double)fixnum));
+}
+
+
+/************************************************************************
+ * FIXRATIO BIGNUM
+ ************************************************************************/
+static void
+rop_fr_bi_as_xr(n_real *real, mpi *bignum, int nop)
+{
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, RFRN(real), RFRD(real));
+
+ mpi_init(&iop);
+ mpi_set(&iop, bignum);
+ mpi_muli(&iop, &iop, RFRD(real));
+
+ if (nop == NOP_ADD)
+ mpi_add(mpr_num(bigr), mpr_num(bigr), &iop);
+ else
+ mpi_sub(mpr_num(bigr), mpr_num(bigr), &iop);
+ mpi_clear(&iop);
+
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static void
+rop_fr_bi_md_xr(n_real *real, mpi *bignum, int nop)
+{
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, RFRN(real), RFRD(real));
+
+ if (nop == NOP_MUL)
+ mpi_mul(mpr_num(bigr), mpr_num(bigr), bignum);
+ else
+ mpi_mul(mpr_den(bigr), mpr_den(bigr), bignum);
+
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+radd_fr_bi(n_real *real, mpi *bignum)
+{
+ rop_fr_bi_as_xr(real, bignum, NOP_ADD);
+}
+
+static INLINE void
+rsub_fr_bi(n_real *real, mpi *bignum)
+{
+ rop_fr_bi_as_xr(real, bignum, NOP_SUB);
+}
+
+static INLINE void
+rmul_fr_bi(n_real *real, mpi *bignum)
+{
+ rop_fr_bi_md_xr(real, bignum, NOP_MUL);
+}
+
+static INLINE void
+rdiv_fr_bi(n_real *real, mpi *bignum)
+{
+ rop_fr_bi_md_xr(real, bignum, NOP_DIV);
+}
+
+static int
+cmp_fr_bi(long num, long den, mpi *bignum)
+{
+ int cmp;
+ mpr cmp1, cmp2;
+
+ mpr_init(&cmp1);
+ mpr_seti(&cmp1, num, den);
+
+ mpr_init(&cmp2);
+ mpi_set(mpr_num(&cmp2), bignum);
+ mpi_seti(mpr_den(&cmp2), 1);
+
+ cmp = mpr_cmp(&cmp1, &cmp2);
+ mpr_clear(&cmp1);
+ mpr_clear(&cmp2);
+
+ return (cmp);
+}
+
+
+/************************************************************************
+ * FIXRATIO FIXRATIO
+ ************************************************************************/
+static void
+rop_fr_fr_as_xr(n_real *real, long num2, long den2, int nop)
+{
+ int fit;
+ long num1 = RFRN(real), den1 = RFRD(real), num = 0, den = 0;
+
+ fit = !fi_fi_mul_overflow(num1, den2);
+ if (fit) {
+ num = num1 * den2;
+ fit = !fi_fi_mul_overflow(num2, den1);
+ if (fit) {
+ den = num2 * den1;
+ if (nop == NOP_ADD) {
+ if ((fit = !fi_fi_add_overflow(num, den)) != 0)
+ num += den;
+ }
+ else if ((fit = !fi_fi_sub_overflow(num, den)) != 0)
+ num -= den;
+ if (fit) {
+ fit = !fi_fi_mul_overflow(den1, den2);
+ if (fit)
+ den = den1 * den2;
+ }
+ }
+ }
+ if (fit) {
+ RFRN(real) = num;
+ RFRD(real) = den;
+ rfr_canonicalize(real);
+ }
+ else {
+ mpi iop;
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, num1, den1);
+ mpi_muli(mpr_den(bigr), mpr_den(bigr), den2);
+ mpi_init(&iop);
+ mpi_seti(&iop, num2);
+ mpi_muli(&iop, &iop, den1);
+ mpi_muli(mpr_num(bigr), mpr_num(bigr), den2);
+ if (nop == NOP_ADD)
+ mpi_add(mpr_num(bigr), mpr_num(bigr), &iop);
+ else
+ mpi_sub(mpr_num(bigr), mpr_num(bigr), &iop);
+ mpi_clear(&iop);
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+ }
+}
+
+static void
+rop_fr_fr_md_xr(n_real *real, long num2, long den2, int nop)
+{
+ int fit;
+ long num1 = RFRN(real), den1 = RFRD(real), num = 0, den = 0;
+
+ if (nop == NOP_MUL) {
+ fit = !fi_fi_mul_overflow(num1, num2) && !fi_fi_mul_overflow(den1, den2);
+ if (fit) {
+ num = num1 * num2;
+ den = den1 * den2;
+ }
+ }
+ else {
+ fit = !fi_fi_mul_overflow(num1, den2) && !fi_fi_mul_overflow(den1, num2);
+ if (fit) {
+ num = num1 * den2;
+ den = den1 * num2;
+ }
+ }
+
+ if (fit) {
+ RFRN(real) = num;
+ RFRD(real) = den;
+ rfr_canonicalize(real);
+ }
+ else {
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+
+ if (nop == NOP_MUL) {
+ mpr_seti(bigr, num1, den1);
+ mpi_muli(mpr_num(bigr), mpr_num(bigr), num2);
+ mpi_muli(mpr_den(bigr), mpr_den(bigr), den2);
+ }
+ else {
+ mpr_seti(bigr, num1, num2);
+ mpi_muli(mpr_num(bigr), mpr_num(bigr), den2);
+ mpi_muli(mpr_den(bigr), mpr_den(bigr), den1);
+ }
+
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+ }
+}
+
+static INLINE void
+radd_fr_fr(n_real *real, long num, long den)
+{
+ rop_fr_fr_as_xr(real, num, den, NOP_ADD);
+}
+
+static INLINE void
+rsub_fr_fr(n_real *real, long num, long den)
+{
+ rop_fr_fr_as_xr(real, num, den, NOP_SUB);
+}
+
+static INLINE void
+rmul_fr_fr(n_real *real, long num, long den)
+{
+ rop_fr_fr_md_xr(real, num, den, NOP_MUL);
+}
+
+static INLINE void
+rdiv_fr_fr(n_real *real, long num, long den)
+{
+ rop_fr_fr_md_xr(real, num, den, NOP_DIV);
+}
+
+static INLINE int
+cmp_fr_fr(long num1, long den1, long num2, long den2)
+{
+ return (cmp_flonum((double)num1 / (double)den1,
+ (double)num2 / (double)den2));
+}
+
+
+/************************************************************************
+ * FIXRATIO BIGRATIO
+ ************************************************************************/
+static void
+rop_fr_br_asmd_xr(n_real *real, mpr *bigratio, int nop)
+{
+ mpr *bigr = XALLOC(mpr);
+
+ mpr_init(bigr);
+ mpr_seti(bigr, RFRN(real), RFRD(real));
+
+ switch (nop) {
+ case NOP_ADD:
+ mpr_add(bigr, bigr, bigratio);
+ break;
+ case NOP_SUB:
+ mpr_sub(bigr, bigr, bigratio);
+ break;
+ case NOP_MUL:
+ mpr_mul(bigr, bigr, bigratio);
+ break;
+ default:
+ mpr_div(bigr, bigr, bigratio);
+ break;
+ }
+
+ RBR(real) = bigr;
+ RTYPE(real) = N_BIGRATIO;
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+radd_fr_br(n_real *real, mpr *bigratio)
+{
+ rop_fr_br_asmd_xr(real, bigratio, NOP_ADD);
+}
+
+static INLINE void
+rsub_fr_br(n_real *real, mpr *bigratio)
+{
+ rop_fr_br_asmd_xr(real, bigratio, NOP_SUB);
+}
+
+static INLINE void
+rmul_fr_br(n_real *real, mpr *bigratio)
+{
+ rop_fr_br_asmd_xr(real, bigratio, NOP_MUL);
+}
+
+static INLINE void
+rdiv_fr_br(n_real *real, mpr *bigratio)
+{
+ rop_fr_br_asmd_xr(real, bigratio, NOP_DIV);
+}
+
+static int
+cmp_fr_br(long num, long den, mpr *bigratio)
+{
+ int cmp;
+ mpr cmp1;
+
+ mpr_init(&cmp1);
+ mpr_seti(&cmp1, num, den);
+
+ cmp = mpr_cmp(&cmp1, bigratio);
+ mpr_clear(&cmp1);
+
+ return (cmp);
+}
+
+
+/************************************************************************
+ * BIGRATIO FIXNUM
+ ************************************************************************/
+static void
+rop_br_fi_asmd_xr(n_real *real, long fixnum, int nop)
+{
+ mpr *bigratio = RBR(real);
+
+ switch (nop) {
+ case NOP_ADD:
+ mpr_addi(bigratio, bigratio, fixnum);
+ break;
+ case NOP_SUB:
+ mpr_subi(bigratio, bigratio, fixnum);
+ break;
+ case NOP_MUL:
+ mpr_muli(bigratio, bigratio, fixnum);
+ break;
+ default:
+ if (fixnum == 0)
+ fatal_error(DIVIDE_BY_ZERO);
+ mpr_divi(bigratio, bigratio, fixnum);
+ break;
+ }
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+radd_br_fi(n_real *real, long fixnum)
+{
+ rop_br_fi_asmd_xr(real, fixnum, NOP_ADD);
+}
+
+static INLINE void
+rsub_br_fi(n_real *real, long fixnum)
+{
+ rop_br_fi_asmd_xr(real, fixnum, NOP_SUB);
+}
+
+static INLINE void
+rmul_br_fi(n_real *real, long fixnum)
+{
+ rop_br_fi_asmd_xr(real, fixnum, NOP_MUL);
+}
+
+static INLINE void
+rdiv_br_fi(n_real *real, long fixnum)
+{
+ rop_br_fi_asmd_xr(real, fixnum, NOP_DIV);
+}
+
+static int
+cmp_br_fi(mpr *bigratio, long fixnum)
+{
+ int cmp;
+ mpr cmp2;
+
+ mpr_init(&cmp2);
+ mpr_seti(&cmp2, fixnum, 1);
+ cmp = mpr_cmp(bigratio, &cmp2);
+ mpr_clear(&cmp2);
+
+ return (cmp);
+}
+
+
+/************************************************************************
+ * BIGRATIO BIGNUM
+ ************************************************************************/
+static void
+rop_br_bi_as_xr(n_real *real, mpi *bignum, int nop)
+{
+ mpi iop;
+
+ mpi_init(&iop);
+ mpi_set(&iop, bignum);
+
+ mpi_mul(&iop, &iop, RBRD(real));
+ if (nop == NOP_ADD)
+ mpi_add(RBRN(real), RBRN(real), &iop);
+ else
+ mpi_sub(RBRN(real), RBRN(real), &iop);
+ mpi_clear(&iop);
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+radd_br_bi(n_real *real, mpi *bignum)
+{
+ rop_br_bi_as_xr(real, bignum, NOP_ADD);
+}
+
+static INLINE void
+rsub_br_bi(n_real *real, mpi *bignum)
+{
+ rop_br_bi_as_xr(real, bignum, NOP_SUB);
+}
+
+static INLINE void
+rmul_br_bi(n_real *real, mpi *bignum)
+{
+ mpi_mul(RBRN(real), RBRN(real), bignum);
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+rdiv_br_bi(n_real *real, mpi *bignum)
+{
+ mpi_mul(RBRD(real), RBRD(real), bignum);
+ rbr_canonicalize(real);
+}
+
+static int
+cmp_br_bi(mpr *bigratio, mpi *bignum)
+{
+ int cmp;
+ mpr cmp1;
+
+ mpr_init(&cmp1);
+ mpi_set(mpr_num(&cmp1), bignum);
+ mpi_seti(mpr_den(&cmp1), 1);
+
+ cmp = mpr_cmp(bigratio, &cmp1);
+ mpr_clear(&cmp1);
+
+ return (cmp);
+}
+
+
+/************************************************************************
+ * BIGRATIO FIXRATIO
+ ************************************************************************/
+static void
+rop_br_fr_asmd_xr(n_real *real, long num, long den, int nop)
+{
+ mpr *bigratio = RBR(real), rop;
+
+ mpr_init(&rop);
+ mpr_seti(&rop, num, den);
+ switch (nop) {
+ case NOP_ADD:
+ mpr_add(bigratio, bigratio, &rop);
+ break;
+ case NOP_SUB:
+ mpr_sub(bigratio, bigratio, &rop);
+ break;
+ case NOP_MUL:
+ mpr_mul(bigratio, bigratio, &rop);
+ break;
+ default:
+ mpr_div(bigratio, bigratio, &rop);
+ break;
+ }
+ mpr_clear(&rop);
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+radd_br_fr(n_real *real, long num, long den)
+{
+ rop_br_fr_asmd_xr(real, num, den, NOP_ADD);
+}
+
+static INLINE void
+rsub_br_fr(n_real *real, long num, long den)
+{
+ rop_br_fr_asmd_xr(real, num, den, NOP_SUB);
+}
+
+static INLINE void
+rmul_br_fr(n_real *real, long num, long den)
+{
+ rop_br_fr_asmd_xr(real, num, den, NOP_MUL);
+}
+
+static INLINE void
+rdiv_br_fr(n_real *real, long num, long den)
+{
+ rop_br_fr_asmd_xr(real, num, den, NOP_DIV);
+}
+
+static int
+cmp_br_fr(mpr *bigratio, long num, long den)
+{
+ int cmp;
+ mpr cmp2;
+
+ mpr_init(&cmp2);
+ mpr_seti(&cmp2, num, den);
+ cmp = mpr_cmp(bigratio, &cmp2);
+ mpr_clear(&cmp2);
+
+ return (cmp);
+}
+
+
+/************************************************************************
+ * BIGRATIO BIGRATIO
+ ************************************************************************/
+static INLINE void
+radd_br_br(n_real *real, mpr *bigratio)
+{
+ mpr_add(RBR(real), RBR(real), bigratio);
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+rsub_br_br(n_real *real, mpr *bigratio)
+{
+ mpr_sub(RBR(real), RBR(real), bigratio);
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+rmul_br_br(n_real *real, mpr *bigratio)
+{
+ mpr_mul(RBR(real), RBR(real), bigratio);
+ rbr_canonicalize(real);
+}
+
+static INLINE void
+rdiv_br_br(n_real *real, mpr *bigratio)
+{
+ mpr_div(RBR(real), RBR(real), bigratio);
+ rbr_canonicalize(real);
+}
+
+static INLINE int
+cmp_br_br(mpr *op1, mpr *op2)
+{
+ return (mpr_cmp(op1, op2));
+}
diff --git a/lisp/modules/indent.lsp b/lisp/modules/indent.lsp
new file mode 100644
index 0000000..4a7f7aa
--- /dev/null
+++ b/lisp/modules/indent.lsp
@@ -0,0 +1,1420 @@
+;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/indent.lsp,v 1.7 2003/01/29 03:05:53 paulo Exp $
+;;
+
+(provide "indent")
+(require "xedit")
+(in-package "XEDIT")
+
+(defconstant indent-spaces '(#\Tab #\Space))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; The final indentation function.
+;; Parameters:
+;; indent
+;; Number of spaces to insert
+;; offset
+;; Offset to where indentation should be added
+;; no-tabs
+;; If set, tabs aren't inserted
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun indent-text (indent offset &optional no-tabs
+ &aux start line length index current tabs spaces string
+ barrier base result (point (point))
+ )
+
+ ;; Initialize
+ (setq
+ start (scan offset :eol :left)
+ line (read-text start (- offset start))
+ length (length line)
+ index (1- length)
+ current 0
+ base 0
+ )
+
+ (and (minusp indent) (setq indent 0))
+
+ ;; Skip any spaces after offset, "paranoia check"
+ (while (member (char-after offset) indent-spaces)
+ (incf offset)
+ )
+
+ ;; Check if there are only spaces before `offset' and the line `start'
+ (while (and (>= index 0) (member (char line index) indent-spaces))
+ (decf index)
+ )
+
+ ;; `index' will be zero if there are only spaces in the `line'
+ (setq barrier (+ start (incf index)))
+
+ ;; Calculate `base' unmodifiable indentation, if any
+ (dotimes (i index)
+ (if (char= (char line i) #\Tab)
+ (incf base (- 8 (rem base 8)))
+ (incf base)
+ )
+ )
+
+ ;; If any non blank character would need to be deleted
+ (and (> base indent) (return-from indent-text nil))
+
+ ;; Calculate `current' indentation
+ (setq current base)
+ (while (< index length)
+ (if (char= (char line index) #\Tab)
+ (incf current (- 8 (rem current 8)))
+ (incf current)
+ )
+ (incf index)
+ )
+
+ ;; Maybe could also "optimize" the indentation even if it is already
+ ;; correct, removing spaces "inside" tabs.
+ (when (/= indent current)
+ (if no-tabs
+ (setq
+ length (- indent base)
+ result (+ barrier length)
+ string (make-string length :initial-element #\Space)
+ )
+ (progn
+ (multiple-value-setq (tabs spaces) (floor (- indent base) 8))
+ (setq
+ length (+ tabs spaces)
+ result (+ barrier length)
+ string (make-string length :initial-element #\Tab)
+ )
+ (fill string #\Space :start tabs)
+ )
+ )
+
+ (replace-text barrier offset string)
+ (and (>= offset point) (>= point barrier) (goto-char result))
+ )
+)
+(compile 'indent-text)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper function, returns indentation of a given offset
+;; If `align' is set, stop once a non blank character is seen, that
+;; is, use `offset' only as a line identifier
+;; If `resolve' is set, it means that the offset is just a hint, it
+;; maybe anywhere in the line
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun offset-indentation (offset &key resolve align
+ &aux
+ char
+ line
+ (start (scan offset :eol :left))
+ (indent 0))
+ (if resolve
+ (loop
+ (if (characterp (setq char (char-after start)))
+ (if (char= char #\Tab)
+ (incf indent (- 8 (rem indent 8)))
+ ;; Not a tab, check if is a space
+ (if (char= char #\Space)
+ (incf indent)
+ ;; Not a tab neither a space
+ (return indent)
+ )
+ )
+ ;; EOF found
+ (return indent)
+ )
+ ;; Increment offset to check next character
+ (incf start)
+ )
+ (progn
+ (setq line (read-text start (- offset start)))
+ (dotimes (i (length line) indent)
+ (if (char= (setq char (char line i)) #\Tab)
+ (incf indent (- 8 (rem indent 8)))
+ (progn
+ (or align (member char indent-spaces)
+ (return indent)
+ )
+ (incf indent)
+ )
+ )
+ )
+ )
+ )
+)
+(compile 'offset-indentation)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; A default/fallback indentation function, just copy indentation
+;; of previous line.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun default-indent (syntax syntable)
+ (let
+ (
+ (offset (scan (point) :eol :left))
+ start
+ left
+ right
+ )
+
+ syntable ;; XXX hack to not generate warning about unused
+ ;; variable, should be temporary (until unused
+ ;; variables can be declared as such)
+
+ (if
+ (or
+ ;; if indentation is disabled
+ (and
+ (hash-table-p (syntax-options syntax))
+ (gethash :disable-indent (syntax-options syntax))
+ )
+ ;; or if not at the start of a new line
+ (> (scan offset :eol :right) offset)
+ )
+ (return-from default-indent)
+ )
+
+ (setq left offset)
+ (loop
+ (setq
+ start left
+ left (scan start :eol :left :count 2)
+ right (scan left :eol :right)
+ )
+ ;; if start of file reached
+ (and (>= left start) (return))
+ (when
+ (setq
+ start
+ (position-if-not
+ #'(lambda (char) (member char indent-spaces))
+ (read-text left (- right left))
+ )
+ )
+
+ ;; indent the current line
+ (indent-text (offset-indentation (+ left start) :align t) offset)
+ (return)
+ )
+ )
+ )
+)
+(compile 'default-indent)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper function
+;; Clear line before cursor if it is empty
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun indent-clear-empty-line (&aux left offset right line index)
+ (setq
+ offset (scan (point) :eol :left)
+ left (scan offset :eol :left :count 2)
+ right (scan left :eol :right)
+ )
+
+ ;; If not at the first line in the file and line is not already empty
+ (when (and (/= offset left) (/= left right))
+ (setq
+ line (read-text left (- right left))
+ index (1- (length line))
+ )
+ (while (and (>= index 0) (member (char line index) indent-spaces))
+ (decf index)
+ )
+ ;; If line was only spaces
+ (and (minusp index) (replace-text left right ""))
+ )
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Macro to be called whenever an indentation rule decides that
+;; the parser is done.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro indent-macro-terminate (&optional result)
+ `(return-from ind-terminate-block ,result)
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Like indent-terminate, but "rejects" the input for the current line
+;; and terminates the loop.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro indent-macro-reject (&optional result)
+ `(progn
+ (setq ind-state ind-prev-state)
+ (return-from ind-terminate-block ,result)
+ )
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Like indent-reject, but "rejects" anything before the current token
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro indent-macro-reject-left (&optional result)
+ `(progn
+ (setq ind-state ind-matches)
+ (return-from ind-terminate-block ,result)
+ )
+)
+
+
+(defstruct indtoken
+ regex ;; a string, character or regex
+ token ;; the resulting token, nil or a keyword
+ begin ;; begin a new table
+ switch ;; switch to another table
+ ;; begin and switch fields are used like the ones for the syntax highlight
+ ;; syntoken structure.
+ label ;; filed at compile time
+ code ;; code to execute when it matches
+)
+
+(defstruct indtable
+ label ;; a keyword, name of the table
+ tokens ;; list of indtoken structures
+ tables ;; list of indtable structures
+ augments ;; augment list
+)
+
+(defstruct indaugment
+ labels ;; list of keywords labeling tables
+)
+
+(defstruct indinit
+ variables ;; list of variables and optional initialization
+ ;; Format of variables must be suitable to LET*, example of call:
+ ;; (indinit
+ ;; var1 ;; initialized to NIL
+ ;; (var2 (afun)) ;; initialized to the value returned by AFUN
+ ;; )
+)
+
+(defstruct indreduce
+ token ;; reduced token
+ rules ;; list of rules
+ label ;; unique label associated with rule, this
+ ;; field is automatically filled in the
+ ;; compilation process. this field exists
+ ;; to allow several indreduce definitions
+ ;; that result in the same token
+ check ;; FORM evaluated, if T apply reduce rule
+ code ;; PROGN to be called when a rule matches
+)
+
+;; NOTE, unlike "reduce" rules, "resolve" rules cannot be duplicated
+(defstruct indresolve
+ match ;; the matched token (or a list of tokens)
+ code ;; PROGN to apply for this token
+)
+
+(defstruct indent
+ reduces ;; list of indreduce structures
+ tables ;; list of indtable structures
+ inits ;; initialization list
+ resolves ;; list of indresolve structures
+ token-code ;; code to execute when a token matches
+ check-code ;; code to execute before applying a reduce rule
+ reduce-code ;; code to execute after reduce rule
+ resolve-code ;; code to execute when matching a token
+)
+
+(defmacro defindent (variable label &rest lists)
+ `(if (boundp ',variable)
+ ,variable
+ (progn
+ (proclaim '(special ,variable))
+ (setq ,variable (compile-indent-table ,label ,@lists))
+ )
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Create an indent token.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro indtoken (pattern token
+ &key icase nospec begin switch code (nosub t))
+ (setq pattern (re-comp (eval pattern) :icase icase :nospec nospec :nosub nosub))
+ (when (consp (re-exec pattern "" :notbol t :noteol t))
+ (error "INDTOKEN: regex ~A matches empty string" pattern)
+ )
+
+ ;; result of macro, return token structure
+ (make-indtoken
+ :regex pattern
+ :token token
+ :begin begin
+ :switch switch
+ :code code
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Create an indentation table. Basically a list of indentation tokens.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun indtable (label &rest definitions)
+ ;; check for simple errors
+ (unless (keywordp label)
+ (error "INDTABLE: ~A is not a keyword" label)
+ )
+ (dolist (item definitions)
+ (unless
+ (or
+ (atom item)
+ (indtoken-p item)
+ (indtable-p item)
+ (indaugment-p item)
+ )
+ (error "INDTABLE: invalid indent table argument ~A" item)
+ )
+ )
+
+ ;; return indent table structure
+ (make-indtable
+ :label label
+ :tokens (remove-if-not #'indtoken-p definitions)
+ :tables (remove-if-not #'indtable-p definitions)
+ :augments (remove-if-not #'indaugment-p definitions)
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Add identifier to list of augment tables.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun indaugment (&rest keywords)
+ (dolist (keyword keywords)
+ (unless (keywordp keyword)
+ (error "INDAUGMENT: bad indent table label ~A" keyword)
+ )
+ )
+
+ ;; return augment list structure
+ (make-indaugment :labels keywords)
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Add variables to initialization list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro indinit (&rest variables)
+ (make-indinit :variables variables)
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Create a "reduction rule"
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro indreduce (token check rules &rest code &aux nullp consp)
+ ;; check for simple errors
+ (unless (or (keywordp token) (null token))
+ (error "INDREDUCE: ~A is not a keyword" token)
+ )
+ (dolist (rule rules)
+ (or (listp rule) (error "INDREDUCE: invalid indent rule ~A" rule))
+ ;; XXX This test is not enough, maybe should add some sort of
+ ;; runtime check to avoid circularity.
+ (and (eq token (car rule)) (null (cdr rule))
+ (error "INDREDUCE: ~A reduces to ~A" token)
+ )
+ (dolist (item rule)
+ (and (or nullp consp) (not (keywordp item))
+ (error "INDREDUCE: a keyword must special pattern")
+ )
+ (if (consp item)
+ (progn
+ (unless
+ (or
+ (and
+ (eq (car item) 'not)
+ (keywordp (cadr item))
+ (null (cddr item))
+ )
+ (and
+ (eq (car item) 'or)
+ (null (member-if-not #'keywordp (cdr item)))
+ )
+ )
+ (error "INDREDUCE: syntax error parsing ~A" item)
+ )
+ (setq consp t)
+ )
+ (progn
+ (setq nullp (null item) consp nil)
+ (unless (or (keywordp item) nullp (eq item t))
+ (error "INDREDUCE: ~A is not a keyword" item)
+ )
+ )
+ )
+ )
+; (and consp
+; (error "INDREDUCE: pattern must be followed by keyword")
+; )
+ )
+
+ ;; result of macro, return indent reduce structure
+ (make-indreduce
+ :token token
+ :check check
+ :rules (remove-if #'null rules)
+ :code code
+ )
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Create a "resolve rule"
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro indresolve (match &rest code)
+ ;; check for simple errors
+ (if (consp match)
+ (dolist (token match)
+ (or (keywordp token) (error "INDRESOLVE: ~A is not a keyword" token))
+ )
+ (or (keywordp match) (error "INDRESOLVE: ~A is not a keyword" match))
+ )
+
+ ;; result of macro, return indent resolve structure
+ (make-indresolve
+ :match match
+ :code code
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper function for compile-indent-table. Returns a list of all
+;; tables and tokens for a given table, including tokens and tables
+;; of children.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun list-indtable-elements (table &aux result sub-result)
+ (setq result (cons (indtable-tokens table) (indtable-tables table)))
+ (dolist (child (indtable-tables table))
+ (setq sub-result (list-indtable-elements child))
+ (rplaca result (append (car result) (car sub-result)))
+ (rplacd result (append (cdr result) (cdr sub-result)))
+ )
+ ;; Return pair of all nested tokens and tables
+ result
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; First pass adding augumented tokens to a table, done in two passes
+;; to respect inheritance order.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun compile-indent-augment-list (table table-list &aux labels augment tokens)
+
+ ;; Create a list of all augment tables.
+ (dolist (augment (indtable-augments table))
+ (setq labels (append labels (indaugment-labels augment)))
+ )
+
+ ;; Remove duplicates and references to "itself", without warnings?
+ (setq
+ labels
+ (remove (indtable-label table) (remove-duplicates labels :from-end t))
+ )
+
+ ;; Check if the specified indent tables exists!
+ (dolist (label labels)
+ (unless
+ (setq augment (car (member label table-list :key #'indtable-label)))
+ (error "COMPILE-INDENT-AUGMENT-LIST: Cannot augment ~A in ~A"
+ label
+ (indtable-label table)
+ )
+ )
+
+ ;; Increase list of tokens.
+ (setq tokens (append tokens (indtable-tokens augment)))
+ )
+
+ ;; Store the tokens in the augment list. They will be added
+ ;; to the indent table in the second pass.
+ (setf (indtable-augments table) tokens)
+
+ ;; Recurse on every child table.
+ (dolist (child (indtable-tables table))
+ (compile-indent-augment-list child table-list)
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Last pass adding augmented tokens to a table.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun link-indent-augment-list (table)
+ (setf
+ (indtable-tokens table)
+ (remove-duplicates
+ (nconc (indtable-tokens table) (indtable-augments table))
+ :key #'indtoken-regex
+ :test #'equal
+ :from-end t
+ )
+
+ ;; Don't need to keep this list anymore.
+ (indtable-augments table)
+ ()
+ )
+
+ (dolist (child (indtable-tables table))
+ (link-indent-augment-list child)
+ )
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Compile the indent reduction rules
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun compile-indent-reduces (reduces
+ &aux need label check rules reduce
+ check-code reduce-code)
+ (dolist (item reduces)
+ (setq
+ label (indreduce-label item)
+ check (indreduce-check item)
+ rules (indreduce-rules item)
+ reduce (indreduce-code item)
+ need (and
+ rules
+ (not label)
+ (or
+ reduce
+ (null check)
+ (not (constantp check))
+ )
+ )
+ )
+ (when need
+ (and (null label) (setq label (intern (string (gensym)) 'keyword)))
+
+ (setf (indreduce-label item) label)
+
+ (and
+ (or (null check)
+ (not (constantp check))
+ )
+ (setq
+ check (list (list 'eq '*ind-label* label) check)
+ check-code (nconc check-code (list check))
+ )
+ )
+
+ (and reduce
+ (setq
+ reduce (cons (list 'eq '*ind-label* label) reduce)
+ reduce-code (nconc reduce-code (list reduce))
+ )
+ )
+ )
+ )
+
+ ;; XXX Instead of using COND, could/should use CASE
+ ;; TODO Implement a smart CASE in the bytecode compiler, if
+ ;; possible, should generate a hashtable, or a table
+ ;; of indexes (for example when all elements in the cases
+ ;; are characters) and then jump directly to the code.
+ (if check-code
+ (setq check-code (cons 'cond (nconc check-code '((t t)))))
+ (setq check-code t)
+ )
+ (and reduce-code (setq reduce-code (cons 'cond reduce-code)))
+
+ (values check-code reduce-code)
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Compile the indent resolve code
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun compile-indent-resolves (resolves &aux match resolve resolve-code)
+ (and
+ (/=
+ (length resolves)
+ (length (remove-duplicates resolves :key #'indresolve-match))
+ )
+ ;; XXX Could do a more complete job and tell what is wrong...
+ (error "COMPILE-INDENT-RESOLVES: duplicated labels")
+ )
+
+ (dolist (item resolves)
+ (when (setq resolve (indresolve-code item))
+ (setq
+ match
+ (indresolve-match item)
+
+ resolve
+ (cons
+ (if (listp match)
+ (list 'member '*ind-token* `',match :test `#'eq)
+ (list 'eq '*ind-token* match)
+ )
+ resolve
+ )
+
+ resolve-code
+ (nconc resolve-code (list resolve))
+ )
+ )
+ )
+
+ (and resolve-code (cons 'cond resolve-code))
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Create an indentation table
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun compile-indent-table (name &rest lists
+ &aux main elements switches begins tables symbols
+ label code token-code check-code reduce-code
+ (inits (remove-if-not #'indinit-p lists))
+ (reduces (remove-if-not #'indreduce-p lists))
+ (resolves (remove-if-not #'indresolve-p lists))
+ )
+ (setq
+ lists (delete-if
+ #'(lambda (object)
+ (or
+ (indinit-p object)
+ (indreduce-p object)
+ (indresolve-p object)
+ )
+ )
+ lists)
+ main (apply #'indtable name lists)
+ elements (list-indtable-elements main)
+ switches (remove-if #'null (car elements) :key #'indtoken-switch)
+ begins (remove-if #'null (car elements) :key #'indtoken-begin)
+ tables (cons main (cdr elements))
+ )
+
+ ;; Check for typos in the keywords, or for not defined indent tables.
+ (dolist (item (mapcar #'indtoken-switch switches))
+ (unless
+ (or (and (integerp item) (minusp item))
+ (member item tables :key #'indtable-label)
+ )
+ (error "COMPILE-INDENT-TABLE: SWITCH ~A cannot be matched" item)
+ )
+ )
+ (dolist (item (mapcar #'indtoken-begin begins))
+ (unless (member item tables :key #'indtable-label)
+ (error "COMPILE-INDENT-TABLE: BEGIN ~A cannot be matched" item)
+ )
+ )
+
+ ;; Build augment list.
+ (compile-indent-augment-list main tables)
+ (link-indent-augment-list main)
+
+ ;; Change switch and begin fields to point to the indent table
+ (dolist (item switches)
+ (if (keywordp (indtoken-switch item))
+ (setf
+ (indtoken-switch item)
+ (car (member (indtoken-switch item) tables :key #'indtable-label))
+ )
+ )
+ )
+ (dolist (item begins)
+ (setf
+ (indtoken-begin item)
+ (car (member (indtoken-begin item) tables :key #'indtable-label))
+ )
+ )
+
+ ;; Build initialization list
+ (dolist (init inits)
+ (setq symbols (nconc symbols (indinit-variables init)))
+ )
+
+ ;; Build token code
+ (dolist (item (car elements))
+ (when (setq code (indtoken-code item))
+ (setf
+ label
+ (intern (string (gensym)) 'keyword)
+
+ (indtoken-label item)
+ label
+
+ code
+ (list (list 'eq '*ind-label* label) code)
+
+ token-code
+ (nconc token-code (list code))
+ )
+ )
+ )
+
+ (multiple-value-setq
+ (check-code reduce-code)
+ (compile-indent-reduces reduces)
+ )
+
+ (make-indent
+ :tables tables
+ :inits symbols
+ :reduces reduces
+ :resolves resolves
+ :token-code (and token-code (cons 'cond token-code))
+ :check-code check-code
+ :reduce-code reduce-code
+ :resolve-code (compile-indent-resolves resolves)
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Search rule-pattern in match-pattern
+;; Returns offset of match, and it's length, if any
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun indent-search-rule (rule-pattern match-pattern
+ &aux start rule rulep matchp test offset length)
+ (if (member-if-not #'keywordp rule-pattern)
+ ;; rule has wildcards
+ (progn
+ (setq
+ rulep rule-pattern
+ matchp match-pattern
+ start match-pattern
+ )
+ (loop
+ (setq rule (car rulep))
+ (cond
+ ;; Special pattern
+ ((consp rule)
+ (if (eq (car rule) 'not)
+ (progn
+ (setq
+ test (cadr rule)
+ rulep (cdr rulep)
+ rule (car rulep)
+ )
+ (while
+ (and
+ ;; something to match
+ matchp
+ ;; NOT match is true
+ (not (eq (car matchp) test))
+ ;; next match is not true
+ (not (eq (car matchp) rule))
+ )
+ (setq matchp (cdr matchp))
+ )
+ (if (eq (car matchp) rule)
+ ;; rule matched
+ (setq
+ matchp (cdr matchp)
+ rulep (cdr rulep)
+ )
+ ;; failed
+ (setq
+ rulep rule-pattern
+ matchp (cdr start)
+ start matchp
+ )
+ )
+ )
+ ;; (eq (car rule) 'or)
+ (progn
+ (if (member (car matchp) (cdr rule) :test #'eq)
+ (setq rulep (cdr rulep) matchp (cdr matchp))
+ ;; failed
+ (progn
+ ;; end of match found!
+ (and (null matchp) (return))
+ ;; reset search
+ (setq
+ rulep rule-pattern
+ matchp (cdr start)
+ start matchp
+ )
+ )
+ )
+ )
+ )
+ )
+
+ ;; Skip until end of match-pattern or rule is found
+ ((null rule)
+ (setq rulep (cdr rulep))
+ ;; If matches everything
+ (if (null rulep)
+ (progn (setq matchp nil) (return))
+ ;; If next token cannot be matched
+ (unless
+ (setq
+ matchp
+ (member (car rulep) matchp :test #'eq)
+ )
+ (setq rulep rule-pattern)
+ (return)
+ )
+ )
+ (setq rulep (cdr rulep) matchp (cdr matchp))
+ )
+
+ ;; Matched
+ ((eq rule t)
+ ;; If there isn't a rule to skip
+ (and (null matchp) (return))
+ (setq rulep (cdr rulep) matchp (cdr matchp))
+ )
+
+ ;; Matched
+ ((eq rule (car matchp))
+ (setq rulep (cdr rulep) matchp (cdr matchp))
+ )
+
+ ;; No match
+ (t
+ ;; end of match found!
+ (and (null matchp) (return))
+ ;; reset search
+ (setq
+ rulep rule-pattern
+ matchp (cdr start)
+ start matchp
+ )
+ )
+ )
+
+ ;; if everything matched
+ (or rulep (return))
+ )
+
+ ;; All rules matched
+ (unless rulep
+ ;; Calculate offset and length of match
+ (setq offset 0 length 0)
+ (until (eq match-pattern start)
+ (setq
+ offset (1+ offset)
+ match-pattern (cdr match-pattern)
+ )
+ )
+ (until (eq match-pattern matchp)
+ (setq
+ length (1+ length)
+ match-pattern (cdr match-pattern)
+ )
+ )
+ )
+ )
+ ;; no wildcards
+ (and (setq offset (search rule-pattern match-pattern :test #'eq))
+ (setq length (length rule-pattern))
+ )
+ )
+
+ (values offset length)
+)
+(compile 'indent-search-rule)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Indentation parser
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro indent-macro (ind-definition ind-offset &optional ind-no-tabs)
+ `(prog*
+ (
+ ;; Current indentation table
+ (ind-table (car (indent-tables ,ind-definition)))
+
+ ;; The parser rules
+ (ind-reduces (indent-reduces ,ind-definition))
+
+ ;; Token list for the table
+ (ind-tokens (indtable-tokens ind-table))
+
+ ;; Stack of nested tables/states
+ ind-stack
+
+ ;; indentation to be used
+ (*indent* 0)
+
+ ;; offset to apply indentation
+ *offset*
+
+ ;; Number of lines read
+ (*ind-lines* 1)
+
+ ;; Matched token
+ *ind-token*
+
+ ;; list of tokens after current match, should not be changed
+ *ind-token-list*
+
+ ;; label associated with rule
+ *ind-label*
+
+ ;; offset of match
+ *ind-offset*
+
+ ;; length of match
+ *ind-length*
+
+ ;; insert position
+ (*ind-point* (point))
+
+ (ind-from (scan ,ind-offset :eol :left))
+ (ind-to ,ind-offset)
+ (ind-line (read-text ind-from (- ind-to ind-from)))
+
+ ;; start of current line
+ (*ind-start* ind-from)
+
+ ;; State information
+ ind-state
+
+ ;; For use with (indent-macro-reject)
+ ind-prev-state
+
+ ;; Matches for the current line
+ ind-matches
+
+ ;; Matched tokens not yet used
+ ind-cache
+
+ ;; Pattern being tested
+ ind-token
+
+ ;; Used when searching for a regex
+ ind-match
+
+ ;; Table to change
+ ind-change
+
+ ;; Length of ind-line
+ (ind-length (length ind-line))
+
+ ;; Don't parse after this offset
+ (ind-end ind-length)
+
+ ;; Temporary variables used during loops
+ ind-left
+ ind-right
+ ind-tleft
+ ind-tright
+
+ ;; Set when start of file is found
+ ind-startp
+
+ ;; Flag for regex search
+ (ind-noteol (< ind-to (scan ind-from :eol :right)))
+
+ ;; Initialization variables expanded here
+ ,@(indent-inits (eval ind-definition))
+ )
+
+ ;; Initial input already read
+ (go :ind-loop)
+
+;------------------------------------------------------------------------
+; Read a text line
+:ind-read
+ (setq
+ ind-to ind-from
+ ind-from (scan ind-from :eol :left :count 2)
+ )
+ ;; If start of file reached
+ (and (= ind-to ind-from) (setq ind-startp t) (go :ind-process))
+
+ (setq
+ *ind-lines* (1+ *ind-lines*)
+ ind-to (scan ind-from :eol :right)
+ ind-line (read-text ind-from (- ind-to ind-from))
+ ind-length (length ind-line)
+ ind-end ind-length
+ ind-noteol nil
+ ind-cache nil
+ ind-prev-state ind-state
+ )
+
+;------------------------------------------------------------------------
+; Loop parsing backwards
+:ind-loop
+ (setq ind-matches nil)
+ (dolist (token ind-tokens)
+ ;; Prepare to loop
+ (setq
+ ind-token (indtoken-regex token)
+ ind-left 0
+ )
+ ;; While the pattern matches
+ (loop
+ (setq ind-right ind-left)
+ (if
+ (consp
+ (setq
+ ind-match
+ (re-exec
+ ind-token
+ ind-line
+ :start ind-left
+ :end ind-end
+ :notbol (> ind-left 0)
+ :noteol ind-noteol
+ )
+ )
+ )
+
+ ;; Remember about match
+ (setq
+ ind-match (car ind-match)
+ ind-left (cdr ind-match)
+ ind-matches (cons (cons token ind-match) ind-matches)
+ )
+
+ ;; No match
+ (return)
+ )
+ ;; matched an empty string
+ (and (= ind-left ind-right) (incf ind-left))
+
+ ;; matched a single eol or bol
+ (and (>= ind-left ind-end) (return))
+ )
+ )
+
+ ;; Add new matches to cache
+ (when ind-matches
+ (setq
+ ind-cache
+ (stable-sort
+ (nconc (nreverse ind-matches) ind-cache) #'< :key #'cadr
+ )
+ )
+ )
+
+ ;; If nothing in the cache
+ (or ind-cache (go :ind-process))
+
+ (setq
+ ind-left (cadar ind-cache)
+ ind-right (cddar ind-cache)
+ ind-matches (cdr ind-cache)
+ )
+
+ ;; If only one element in the cache
+ (or ind-matches (go :ind-parse))
+
+ (setq
+ ind-tleft (cadar ind-matches)
+ ind-tright (cddar ind-matches)
+ )
+
+ ;; Remove overlaps
+ (loop
+ (if (or (>= ind-tleft ind-right) (<= ind-tright ind-left))
+ ;; No overlap
+ (progn
+ (setq
+ ind-left ind-tleft
+ ind-right ind-tright
+ ind-matches (cdr ind-matches)
+ )
+ ;; If everything checked
+ (or ind-matches (return))
+ )
+ ;; Overlap found
+ (progn
+ (if (consp (cdr ind-matches))
+ ;; There are yet items to be checked
+ (progn
+ (rplaca ind-matches (cadr ind-matches))
+ (rplacd ind-matches (cddr ind-matches))
+ )
+ ;; Last item
+ (progn
+ (rplacd (last ind-cache 2) nil)
+ (return)
+ )
+ )
+ )
+ )
+
+ ;; Prepare for next check
+ (setq
+ ind-tleft (cadar ind-matches)
+ ind-tright (cddar ind-matches)
+ )
+ )
+
+;------------------------------------------------------------------------
+; Process the matched tokens
+:ind-parse
+ (setq ind-cache (nreverse ind-cache))
+
+:ind-parse-loop
+ (or (setq ind-match (car ind-cache)) (go :ind-process))
+
+ (setq
+ ind-cache (cdr ind-cache)
+ ind-token (car ind-match)
+ )
+
+ (or (member ind-token ind-tokens :test #'eq)
+ (go :ind-parse-loop)
+ )
+
+ ;; If a state should be added
+ (when (setq ind-change (indtoken-token ind-token))
+ (setq
+ ind-left (cadr ind-match)
+ ind-right (cddr ind-match)
+
+ *ind-offset*
+ (+ ind-from ind-left)
+
+ *ind-length*
+ (- ind-right ind-left)
+
+ ind-state
+ (cons
+ (cons ind-change (cons *ind-offset* *ind-length*))
+ ind-state
+ )
+
+ *ind-label*
+ (indtoken-label ind-token)
+ )
+
+ ;; Expand token code
+ ,(indent-token-code (eval ind-definition))
+ )
+
+ ;; Check if needs to switch to another table
+ (when (setq ind-change (indtoken-switch ind-token))
+ ;; Need to switch to a previous table
+ (if (integerp ind-change)
+ ;; Relative switch
+ (while (and ind-stack (minusp ind-change))
+ (setq
+ ind-table (pop ind-stack)
+ ind-change (1+ ind-change)
+ )
+ )
+ ;; Search table in the stack
+ (until
+ (or
+ (null ind-stack)
+ (eq
+ (setq ind-table (pop ind-stack))
+ ind-change
+ )
+ )
+ )
+ )
+
+ ;; If no match or stack became empty
+ (and (null ind-table)
+ (setq
+ ind-table
+ (car (indent-tables ,ind-definition))
+ )
+ )
+ )
+
+ ;; Check if needs to start a new table
+ ;; XXX use ind-tleft to reduce number of local variables
+ (when (setq ind-tleft (indtoken-begin ind-token))
+ (setq
+ ind-change ind-tleft
+ ind-stack (cons ind-table ind-stack)
+ ind-table ind-change
+ )
+ )
+
+ ;; If current "indent pattern table" changed
+ (when ind-change
+ (setq
+ ind-tokens (indtable-tokens ind-table)
+ ind-cache (nreverse ind-cache)
+ ind-end (cadr ind-match)
+ ind-noteol (> ind-length ind-end)
+ )
+ (go :ind-loop)
+ )
+
+ (and ind-cache (go :ind-parse-loop))
+
+;------------------------------------------------------------------------
+; Everything checked, process result
+:ind-process
+
+ ;; If stack is not empty, don't apply rules
+ (and ind-stack (not ind-startp) (go :ind-read))
+
+ (block ind-terminate-block
+ (setq ind-cache nil ind-tleft 0 ind-change (mapcar #'car ind-state))
+ (dolist (entry ind-reduces)
+ (setq
+ *ind-token* (indreduce-token entry)
+ *ind-label* (indreduce-label entry)
+ )
+ (dolist (rule (indreduce-rules entry))
+ (loop
+ ;; Check if reduction can be applied
+ (or
+ (multiple-value-setq
+ (ind-match ind-length)
+ (indent-search-rule rule ind-change)
+ )
+ (return)
+ )
+
+ (setq
+ ;; First element matched
+ ind-matches (nthcdr ind-match ind-state)
+
+ ;; Offset of match
+ *ind-offset* (cadar ind-matches)
+
+ *ind-token-list* (nthcdr ind-match ind-change)
+
+ ;; Length of match, note that *ind-length*
+ ;; Will be transformed to zero bellow if
+ ;; the rule is deleting entries.
+ *ind-length*
+ (if (> ind-length 1)
+ (progn
+ (setq
+ ;; XXX using ind-tright, to reduce
+ ;; number of local variables...
+ ind-tright
+ (nth (1- ind-length) ind-matches)
+
+ ind-right
+ (+ (cadr ind-tright)
+ (cddr ind-tright)
+ )
+ )
+ (- ind-right *ind-offset*)
+ )
+ (cddar ind-matches)
+ )
+ )
+
+ ;; XXX using ind-tleft as a counter, to reduce
+ ;; number of used variables...
+ (and (>= (incf ind-tleft) 1000)
+ ;; Should never apply so many reduce rules on
+ ;; every iteration, if needs to, something is
+ ;; wrong in the indentation definition...
+ (error "~D INDREDUCE iterations, ~
+ now checking (~A ~A)"
+ ind-tleft *ind-token* rule
+ )
+ )
+
+ ;; Check if should apply the reduction
+ (or
+ ;; Expand check code
+ ,(indent-check-code (eval ind-definition))
+ (return)
+ )
+
+ (if (null *ind-token*)
+ ;; Remove match
+ (progn
+ (setq *ind-length* 0)
+ (if (= ind-match 0)
+ ;; Matched the first entry
+ (setq
+ ind-state
+ (nthcdr ind-length ind-matches)
+ )
+ (progn
+ (setq
+ ind-matches
+ (nthcdr (1- ind-match) ind-state)
+ )
+ (rplacd
+ ind-matches
+ (nthcdr (1+ ind-length) ind-matches)
+ )
+ )
+ )
+ )
+
+ ;; Substitute/simplify
+ (progn
+ (rplaca (car ind-matches) *ind-token*)
+ (when (> ind-length 1)
+ (rplacd (cdar ind-matches) *ind-length*)
+ (rplacd
+ ind-matches
+ (nthcdr ind-length ind-matches)
+ )
+ )
+ )
+ )
+ (setq
+ ind-cache t
+ ind-change (mapcar #'car ind-state)
+ )
+
+ ;; Expand reduce code
+ ,(indent-reduce-code (eval ind-definition))
+ )
+ )
+ )
+
+ ;; ind-cache will be T if at least one change was done
+ (and ind-cache (go :ind-process))
+
+ ;; Start of file reached
+ (or ind-startp (go :ind-read))
+
+ ) ;; end of ind-terminate-block
+
+
+ (block ind-terminate-block
+ (setq *ind-token-list* (mapcar #'car ind-state))
+ (dolist (item ind-state)
+ (setq
+ *ind-token* (car item)
+ *ind-offset* (cadr item)
+ *ind-length* (cddr item)
+ )
+ ;; Expand resolve code
+ ,(indent-resolve-code (eval ind-definition))
+ (setq *ind-token-list* (cdr *ind-token-list*))
+ )
+ )
+
+ (and (integerp *indent*)
+ (integerp *offset*)
+ (indent-text *indent* *offset* ,ind-no-tabs)
+ )
+ )
+)
diff --git a/lisp/modules/lisp.lsp b/lisp/modules/lisp.lsp
new file mode 100644
index 0000000..55d5e6c
--- /dev/null
+++ b/lisp/modules/lisp.lsp
@@ -0,0 +1,174 @@
+;;
+;; Copyright (c) 2001 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/lisp.lsp,v 1.10 2002/12/20 04:32:47 paulo Exp $
+;;
+(provide "lisp")
+
+(in-package "LISP")
+
+(export '(
+ second third fourth fifth sixth seventh eighth ninth tenth
+ pathname merge-pathnames
+ logtest signum
+ alphanumericp copy-seq push pop prog prog*
+ with-open-file with-output-to-string
+))
+
+(defun second (a) (nth 1 a))
+(defun third (a) (nth 2 a))
+(defun fourth (a) (nth 3 a))
+(defun fifth (a) (nth 4 a))
+(defun sixth (a) (nth 5 a))
+(defun seventh (a) (nth 6 a))
+(defun eighth (a) (nth 7 a))
+(defun ninth (a) (nth 8 a))
+(defun tenth (a) (nth 9 a))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; pathnames
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun pathname (filename)
+ (values (parse-namestring filename)))
+
+(defun merge-pathnames (pathname &optional defaults default-version)
+ (if (null default-version)
+ (parse-namestring pathname nil defaults)
+ (parse-namestring pathname nil
+ (make-pathname :defaults defaults :version default-version))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; math
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun logtest (integer1 integer2)
+ (not (zerop (logand integer1 integer2))))
+
+(defun signum (number)
+ (if (zerop number) number (/ number (abs number))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; misc functions/macros
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun alphanumericp (char)
+ (or (alpha-char-p char) (not (null (digit-char-p char)))))
+
+(defun copy-seq (sequence)
+ (subseq sequence 0))
+
+(defmacro prog (init &rest body)
+ `(block nil (let ,init (tagbody ,@body))))
+
+(defmacro prog* (init &rest body)
+ `(block nil (let* ,init (tagbody ,@body))))
+
+(defmacro with-open-file (file &rest body)
+ `(let ((,(car file) (open ,@(cdr file))))
+ (unwind-protect
+ (progn ,@body)
+ (if ,(car file) (close ,(car file))))))
+
+(defmacro with-output-to-string (stream &rest body)
+ `(let ((,(car stream) (make-string-output-stream)))
+ (unwind-protect
+ (progn ,@body (get-output-stream-string ,(car stream)))
+ (and ,(car stream) (close ,(car stream))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; setf
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defsetf car (list) (value) `(progn (rplaca ,list ,value) ,value))
+(defsetf cdr (list) (value) `(progn (rplacd ,list ,value) ,value))
+
+(defsetf caar (list) (value) `(progn (rplaca (car ,list) ,value) ,value))
+(defsetf cadr (list) (value) `(progn (rplaca (cdr ,list) ,value) ,value))
+(defsetf cdar (list) (value) `(progn (rplacd (car ,list) ,value) ,value))
+(defsetf cddr (list) (value) `(progn (rplacd (cdr ,list) ,value) ,value))
+(defsetf caaar (list) (value) `(progn (rplaca (caar ,list) ,value) ,value))
+(defsetf caadr (list) (value) `(progn (rplaca (cadr ,list) ,value) ,value))
+(defsetf cadar (list) (value) `(progn (rplaca (cdar ,list) ,value) ,value))
+(defsetf caddr (list) (value) `(progn (rplaca (cddr ,list) ,value) ,value))
+(defsetf cdaar (list) (value) `(progn (rplacd (caar ,list) ,value) ,value))
+(defsetf cdadr (list) (value) `(progn (rplacd (cadr ,list) ,value) ,value))
+(defsetf cddar (list) (value) `(progn (rplacd (cdar ,list) ,value) ,value))
+(defsetf cdddr (list) (value) `(progn (rplacd (cddr ,list) ,value) ,value))
+(defsetf caaaar (list) (value) `(progn (rplaca (caaar ,list) ,value) ,value))
+(defsetf caaadr (list) (value) `(progn (rplaca (caadr ,list) ,value) ,value))
+(defsetf caadar (list) (value) `(progn (rplaca (cadar ,list) ,value) ,value))
+(defsetf caaddr (list) (value) `(progn (rplaca (caddr ,list) ,value) ,value))
+(defsetf cadaar (list) (value) `(progn (rplaca (cdaar ,list) ,value) ,value))
+(defsetf cadadr (list) (value) `(progn (rplaca (cdadr ,list) ,value) ,value))
+(defsetf caddar (list) (value) `(progn (rplaca (cddar ,list) ,value) ,value))
+(defsetf cadddr (list) (value) `(progn (rplaca (cdddr ,list) ,value) ,value))
+(defsetf cdaaar (list) (value) `(progn (rplacd (caaar ,list) ,value) ,value))
+(defsetf cdaadr (list) (value) `(progn (rplacd (caadr ,list) ,value) ,value))
+(defsetf cdadar (list) (value) `(progn (rplacd (cadar ,list) ,value) ,value))
+(defsetf cdaddr (list) (value) `(progn (rplacd (caddr ,list) ,value) ,value))
+(defsetf cddaar (list) (value) `(progn (rplacd (cdaar ,list) ,value) ,value))
+(defsetf cddadr (list) (value) `(progn (rplacd (cdadr ,list) ,value) ,value))
+(defsetf cdddar (list) (value) `(progn (rplacd (cddar ,list) ,value) ,value))
+(defsetf cddddr (list) (value) `(progn (rplacd (cdddr ,list) ,value) ,value))
+
+(defsetf first (list) (value) `(progn (rplaca ,list ,value) ,value))
+(defsetf second (list) (value) `(progn (rplaca (nthcdr 1 ,list) ,value) ,value))
+(defsetf third (list) (value) `(progn (rplaca (nthcdr 2 ,list) ,value) ,value))
+(defsetf fourth (list) (value) `(progn (rplaca (nthcdr 3 ,list) ,value) ,value))
+(defsetf fifth (list) (value) `(progn (rplaca (nthcdr 4 ,list) ,value) ,value))
+(defsetf sixth (list) (value) `(progn (rplaca (nthcdr 5 ,list) ,value) ,value))
+(defsetf seventh (list) (value) `(progn (rplaca (nthcdr 6 ,list) ,value) ,value))
+(defsetf eighth (list) (value) `(progn (rplaca (nthcdr 7 ,list) ,value) ,value))
+(defsetf ninth (list) (value) `(progn (rplaca (nthcdr 8 ,list) ,value) ,value))
+(defsetf tenth (list) (value) `(progn (rplaca (nthcdr 9 ,list) ,value) ,value))
+
+(defsetf rest (list) (value) `(progn (rplacd ,list ,value) ,value))
+
+(defun lisp::nth-store (index list value)
+ (rplaca (nthcdr index list) value) value)
+(defsetf nth lisp::nth-store)
+
+(defsetf aref (array &rest indices) (value)
+ `(lisp::vector-store ,array ,@indices ,value))
+
+(defsetf get (symbol key &optional default) (value)
+ `(lisp::put ,symbol ,key ,value))
+
+(defsetf symbol-plist lisp::set-symbol-plist)
+
+(defsetf gethash (key hash-table &optional default) (value)
+ `(lisp::puthash ,key ,hash-table ,value))
+
+(defsetf char lisp::char-store)
+(defsetf schar lisp::char-store)
+(defsetf elt lisp::elt-store)
+(defsetf svref lisp::elt-store)
+(defsetf documentation lisp::documentation-store)
+
+(defsetf symbol-value set)
+
+(defsetf subseq (sequence start &optional end) (value)
+ `(progn (replace ,sequence ,value :start1 ,start :end1 ,end) ,value))
diff --git a/lisp/modules/progmodes/c.lsp b/lisp/modules/progmodes/c.lsp
new file mode 100644
index 0000000..bc4474b
--- /dev/null
+++ b/lisp/modules/progmodes/c.lsp
@@ -0,0 +1,1118 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/c.lsp,v 1.26 2003/01/29 03:05:54 paulo Exp $
+;;
+
+(require "syntax")
+(require "indent")
+(in-package "XEDIT")
+
+(defsynprop *prop-format*
+ "format"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "RoyalBlue2"
+ :underline t
+)
+
+(defsynoptions *c-DEFAULT-style*
+ ;; Positive number. Basic indentation.
+ (:indentation . 4)
+
+ ;; Boolean. Support for GNU style indentation.
+ (:brace-indent . nil)
+
+ ;; Boolean. Add one indentation level to case and default?
+ (:case-indent . t)
+
+ ;; Boolean. Remove one indentation level for labels?
+ (:label-dedent . t)
+
+ ;; Boolean. Add one indentation level to continuations?
+ (:cont-indent . t)
+
+ ;; Boolean. Move cursor to the indent column after pressing <Enter>?
+ (:newline-indent . t)
+
+ ;; Boolean. Set to T if tabs shouldn't be used to fill indentation.
+ (:emulate-tabs . nil)
+
+ ;; Boolean. Force a newline before braces?
+ (:newline-before-brace . nil)
+
+ ;; Boolean. Force a newline after braces?
+ (:newline-after-brace . nil)
+
+ ;; Boolean. Force a newline after semicolons?
+ (:newline-after-semi . nil)
+
+ ;; Boolean. Only calculate indentation after pressing <Enter>?
+ ;; This may be useful if the parser does not always
+ ;; do what the user expects...
+ (:only-newline-indent . nil)
+
+ ;; Boolean. Remove extra spaces from previous line.
+ ;; This should default to T when newline-indent is not NIL.
+ (:trim-blank-lines . t)
+
+ ;; Boolean. If this hash-table entry is set, no indentation is done.
+ ;; Useful to temporarily disable indentation.
+ (:disable-indent . nil)
+)
+
+;; BSD like style
+(defsynoptions *c-BSD-style*
+ (:indentation . 8)
+ (:brace-indent . nil)
+ (:case-indent . nil)
+ (:label-dedent . t)
+ (:cont-indent . t)
+ (:newline-indent . t)
+ (:emulate-tabs . nil)
+ (:newline-before-brace . nil)
+ (:newline-after-brace . t)
+ (:newline-after-semi . t)
+ (:trim-blank-lines . t)
+)
+
+;; GNU like style
+(defsynoptions *c-GNU-style*
+ (:indentation . 2)
+ (:brace-indent . t)
+ (:case-indent . nil)
+ (:label-dedent . t)
+ (:cont-indent . t)
+ (:newline-indent . nil)
+ (:emulate-tabs . nil)
+ (:newline-before-brace . t)
+ (:newline-after-brace . t)
+ (:newline-after-semi . t)
+ (:trim-blank-lines . nil)
+)
+
+;; K&R like style
+(defsynoptions *c-K&R-style*
+ (:indentation . 5)
+ (:brace-indent . nil)
+ (:case-indent . nil)
+ (:label-dedent . t)
+ (:cont-indent . t)
+ (:newline-indent . t)
+ (:emulate-tabs . t)
+ (:newline-before-brace . t)
+ (:newline-after-brace . t)
+ (:newline-after-semi . t)
+ (:trim-blank-lines . t)
+)
+
+(defvar *c-styles* '(
+ ("xedit" . *c-DEFAULT-style*)
+ ("BSD" . *c-BSD-style*)
+ ("GNU" . *c-GNU-style*)
+ ("K&R" . *c-K&R-style*)
+))
+
+(defvar *c-mode-options* *c-DEFAULT-style*)
+; (setq *c-mode-options* *c-gnu-style*)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; This is a very lazy "pattern matcher" for the C language.
+;; If the syntax in the code is not correct, it may get confused, and
+;; because it is "lazy" some wrong constructs will be recognized as
+;; correct when reducing patterns.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defindent *c-mode-indent* :main
+ ;; this must be the first token
+ (indtoken "^\\s*" :start-of-line)
+ (indtoken "\\<case\\>" :c-case)
+ (indtoken "\\<default\\>" :c-default)
+ (indtoken "\\<do\\>" :do)
+ (indtoken "\\<if\\>" :c-if)
+ (indtoken "\\<else\\>" :c-else)
+ (indtoken "\\<for\\>" :c-for)
+ (indtoken "\\<switch\\>" :c-switch)
+ (indtoken "\\<while\\>" :c-while)
+ ;; Match identifiers and numbers as an expression
+ (indtoken "\\w+" :expression)
+ (indtoken ";" :semi :nospec t)
+ (indtoken "," :comma :nospec t)
+ (indtoken ":" :collon :nospec t)
+ ;; Ignore spaces before collon, this avoids dedenting ternary
+ ;; and bitfield definitions as the parser does not distinguish
+ ;; labels from those, another option would be to use the pattern
+ ;; "\\w+:", but this way should properly handle labels generated
+ ;; by macros, example: `MACRO_LABEL(value):'
+ (indtoken "\\s+:" nil)
+
+ (indinit (c-braces 0))
+ (indtoken "{"
+ :obrace
+ :nospec t
+ :code (decf c-braces)
+ )
+ (indtoken "}"
+ :cbrace
+ :nospec t
+ :begin :braces
+ :code (incf c-braces)
+ )
+ (indtable :braces
+ (indtoken "{"
+ :obrace
+ :nospec t
+ :switch -1
+ :code (decf c-braces)
+ )
+ (indtoken "}"
+ :cbrace
+ :nospec t
+ :begin :braces
+ :code (incf c-braces)
+ )
+ )
+
+ (indinit (c-bra 0))
+ (indtoken ")" :cparen :nospec t :code (incf c-bra))
+ (indtoken "(" :oparen :nospec t :code (decf c-bra))
+ (indtoken "]" :cbrack :nospec t :code (incf c-bra))
+ (indtoken "[" :obrack :nospec t :code (decf c-bra))
+ (indtoken "\\\\$" :continuation)
+
+ ;; C++ style comment, disallow other tokens to match inside comment
+ (indtoken "//.*$" nil)
+
+ (indtoken "#" :hash :nospec t)
+
+ ;; if in the same line, reduce now, this must be done because the
+ ;; delimiters are identical
+ (indtoken "'([^\\']|\\\\.)*'" :expression)
+ (indtoken "\"([^\\\"]|\\\\.)*\"" :expression)
+
+ (indtoken "\"" :cstring :nospec t :begin :string)
+
+ (indtoken "'" :cconstant :nospec t :begin :constant)
+
+ (indtoken "*/" :ccomment :nospec t :begin :comment)
+ ;; this must be the last token
+ (indtoken "$" :end-of-line)
+
+ (indtable :string
+ ;; Ignore escaped characters
+ (indtoken "\\." nil)
+ ;; Return to the toplevel when the start of the string is found
+ (indtoken "\"" :ostring :nospec t :switch -1)
+ )
+ (indtable :constant
+ ;; Ignore escaped characters
+ (indtoken "\\." nil)
+ ;; Return to the toplevel when the start of the character is found
+ (indtoken "'" :oconstant :nospec t :switch -1)
+ )
+ (indtable :comment
+ (indtoken "/*" :ocomment :nospec t :switch -1)
+ )
+
+ ;; "Complex" statements
+ (indinit (c-complex 0) (c-cases 0))
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Order of reduce rules here is important, process comment,
+ ;; continuations, preprocessor and set states when an eol is found.
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+ (indinit (c-offset (point-max))
+ (c-prev-offset c-offset)
+ )
+ (indreduce :indent
+ t
+ ((:start-of-line))
+ (and (= *ind-start* *ind-offset*)
+ (setq
+ *offset* (+ *ind-offset* *ind-length*)
+ )
+ )
+ (setq
+ c-prev-offset c-offset
+ c-offset *ind-offset*
+ )
+ )
+
+ ;; Delete comments
+ (indreduce nil
+ t
+ ((:ocomment nil :ccomment))
+ )
+
+ ;; Join in a single token to simplify removal of possible multiline
+ ;; preprocessor directives
+ (indinit c-continuation)
+ (indreduce :continuation
+ t
+ ((:continuation :end-of-line))
+ (setq c-continuation t)
+ )
+
+ (indreduce :eol
+ t
+ ((:end-of-line))
+ ;; Anything after the eol offset is safe to parse now
+ (setq c-continuation nil)
+ )
+
+ ;; Delete blank lines
+ (indreduce nil
+ t
+ ((:indent :eol))
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Preprocessor
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indreduce nil
+ (>= *ind-offset* *ind-start*)
+ ((:indent :hash))
+ (setq *indent* 0)
+ (indent-macro-reject-left)
+ )
+ (indreduce nil
+ t
+ ((:indent :hash nil :eol))
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Expressions
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indreduce :expression
+ t
+ ;; Reduce to a single expression
+ ((:expression :parens)
+ (:expression :bracks)
+ (:expression :expression)
+ ;; These may be multiline
+ (:ostring (not :ostring) :cstring)
+ (:oconstant (not :oconstant) :cconstant)
+ )
+ )
+
+ (indreduce :expression
+ t
+ ((:expression :eol :indent :expression)
+ (:expression :eol :expression)
+ )
+ )
+
+ (indreduce :exp-comma
+ t
+ ((:expression :comma)
+ )
+ )
+
+ ;; A semicollon, start a statement
+ (indreduce :stat
+ t
+ ((:semi))
+ )
+
+ ;; Expression following (possibly empty) statement
+ (indreduce :stat
+ t
+ (((or :expression :exp-comma) :stat))
+ )
+
+ ;; Multiline statements
+ (indreduce :stat
+ t
+ (((or :expression :exp-comma) :eol :indent :stat)
+ ;; rule below may have removed the :indent
+ ((or :expression :exp-comma) :eol :stat)
+ )
+ )
+
+ (indinit c-exp-indent)
+ ;; XXX This rule avoids parsing large amounts of code
+ (indreduce :stat
+ t
+ ;; Eat eol if following expression
+ ((:indent :stat :eol)
+ (:indent :stat)
+ )
+ (if
+ (or
+ (null c-exp-indent)
+ (/= (cdar c-exp-indent) (+ *ind-offset* *ind-length*))
+ )
+ ;; A new statement, i.e. not just joining a multiline one
+ (push
+ (cons
+ (offset-indentation *ind-offset* :resolve t)
+ (+ *ind-offset* *ind-length*)
+ )
+ c-exp-indent
+ )
+ ;; Update start of statement
+ (rplaca
+ (car c-exp-indent)
+ (offset-indentation *ind-offset* :resolve t)
+ )
+ )
+ (when (consp (cdr c-exp-indent))
+ (if (and
+ (zerop c-complex)
+ (zerop c-cases)
+ (zerop c-bra)
+ (= (caar c-exp-indent) (caadr c-exp-indent))
+ )
+ ;; Two statements with the same indentation
+ (progn
+ (setq *indent* (caar c-exp-indent))
+ (indent-macro-reject-left)
+ )
+ ;; Different indentation or complex state
+ (progn
+ (rplacd c-exp-indent nil)
+ (setq c-complex 0)
+ )
+ )
+ )
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Handle braces
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indreduce :stat
+ ;; If block finishes before current line, group as a statement
+ (< (+ *ind-offset* *ind-length*) *ind-start*)
+ ((:obrace (not :obrace) :cbrace))
+ )
+ (indreduce :obrace
+ ;; If not in the first line
+ (< *ind-offset* *ind-start*)
+ ;; If the opening { is the first non blank char in the line
+ ((:indent :obrace))
+ (setq *indent* (offset-indentation (+ *ind-offset* *ind-length*)))
+
+ ;; XXX This may be the starting brace of a switch
+ (setq c-case-flag nil)
+ (indent-macro-reject-left)
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Labels
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; XXX this frequently doesn't do what is expected, should redefine
+ ;; some rules, as it frequently will dedent while typing something
+ ;; like test ? exp1 : exp2
+ ;; ^ dedents here because it reduces everything
+ ;; before ':' to a single :expression token.
+ (indreduce :label
+ t
+ ((:indent :expression :collon :eol))
+ (when (and *label-dedent* (>= *ind-offset* *ind-start*))
+ (setq
+ *indent*
+ (- (offset-indentation *ind-offset* :resolve t) *base-indent*)
+ )
+ (indent-macro-reject-left)
+ )
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Handle if
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indreduce :if
+ t
+ ((:c-if :parens)
+ )
+ (incf c-complex)
+ )
+
+ (indreduce :else
+ t
+ ((:c-else))
+ (incf c-complex)
+ )
+
+ ;; Join
+ (indreduce :else-if
+ t
+ ((:else :if)
+ (:else :eol :indent :if)
+ )
+ (incf c-complex)
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Handle for
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Join with the parentheses
+ (indreduce :for
+ t
+ ((:c-for :parens)
+ )
+ (incf c-complex)
+ )
+ ;; Before current line, simplify
+ (indreduce :stat
+ (< (+ *ind-offset* *ind-length*) *ind-point*)
+ ((:for :stat)
+ )
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Handle while and do
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indreduce :while
+ t
+ ((:c-while :parens)
+ ;; Assume that it is yet being edited, or adjusting indentation
+ (:c-while)
+ )
+ (incf c-complex)
+ )
+ (indreduce :stat
+ t
+ ((:do :stat :while)
+ (:while :stat)
+ )
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Handle switch
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indinit c-case-flag)
+
+ (indreduce :switch
+ t
+ ((:c-switch :parens)
+ )
+ )
+ ;; Transform in a statement
+ (indreduce :stat
+ (< (+ *ind-offset* *ind-length*) *ind-start*)
+ ((:switch :stat)
+ ;; Do it now or some rule may stop parsing, and calculate
+ ;; a wrong indentation for nested switches
+ (:switch :eol :indent :stat)
+ )
+ )
+ ;; An open switch
+ (indreduce :obrace
+ (and
+ (<= c-braces 0)
+ (> *ind-start* *ind-offset*)
+ )
+ ((:indent :switch :obrace)
+ )
+ (setq
+ *indent* (offset-indentation *ind-offset* :resolve t)
+ c-case-flag nil
+ )
+ (indent-macro-reject-left)
+ )
+ (indreduce :obrace
+ (and
+ (<= c-braces 0)
+ (> *ind-start* *ind-offset*)
+ )
+ ((:indent :switch :eol :indent :obrace)
+ )
+ (setq
+ *indent* (- (offset-indentation *ind-offset* :resolve t) *base-indent*)
+ c-case-flag nil
+ )
+ (and *brace-indent* (incf *indent* *base-indent*))
+ (indent-macro-reject-left)
+ )
+ ;; Before current line
+ (indreduce :case
+ (and
+ (or
+ (not *case-indent*)
+ (prog1 c-case-flag (setq c-case-flag t))
+ )
+ (<= c-braces 0)
+ (< *ind-offset* *ind-start*)
+ )
+ ((:indent :case)
+ )
+ (setq
+ *indent* (offset-indentation *ind-offset* :resolve t)
+ c-case-flag nil
+ )
+ (indent-macro-reject-left)
+ )
+ (indreduce :case
+ t
+ ((:c-case :expression :collon)
+ (:c-default :collon)
+ ;; Assume that it is yet being edited, or adjusting indentation
+ (:c-case)
+ (:c-default)
+ )
+ (and (>= *ind-offset* *ind-start*)
+ (incf c-cases)
+ )
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Handle parentheses and brackets
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Reduce matches
+ (indreduce :parens
+ t
+ ((:oparen (not :oparen) :cparen))
+ (when
+ (and
+ (< *ind-offset* *ind-start*)
+ (> (+ *ind-offset* *ind-length*) *ind-start*)
+ )
+ (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))
+ (indent-macro-reject-left)
+ )
+ )
+ (indreduce :bracks
+ t
+ ((:obrack (not :obrack) :cbrack))
+ (when
+ (and
+ (< *ind-offset* *ind-start*)
+ (> (+ *ind-offset* *ind-length*) *ind-start*)
+ )
+ (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))
+ (indent-macro-reject-left)
+ )
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Assuming previous lines have correct indentation, this allows
+ ;; resolving the indentation fastly
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Line ended with an open brace
+ (indreduce :obrace
+ (< *ind-offset* *ind-start*)
+ ((:indent (or :for :while :if :else-if :else :do) :obrace)
+ )
+ (setq *indent* (offset-indentation *ind-offset* :resolve t))
+ (indent-macro-reject-left)
+ )
+ ;; Adjust indentation level if current line starts with an open brace
+ (indreduce nil
+ (< *ind-offset* *ind-start* (+ *ind-offset* *ind-length*))
+ ;; Just set initial indentation
+ ((:indent (or :for :while :if :else-if :else :do) :eol :indent :obrace)
+ )
+ (setq
+ *indent*
+ (- (offset-indentation *ind-offset* :resolve t) *base-indent*)
+ )
+ (and *brace-indent* (incf *indent* *base-indent*))
+ (indent-macro-reject-left)
+ )
+ ;; Previous rule failed, current line does not start with an open brace
+ (indreduce :flow
+ ;; first statement is in current line
+ (and
+ (<= c-braces 0)
+ (> (+ *ind-offset* *ind-length*) *ind-start* *ind-offset*)
+ )
+ ((:indent (or :for :while :if :else-if :else :do) :eol :indent)
+ )
+ (setq *indent* (offset-indentation *ind-offset* :resolve t))
+ (indent-macro-reject-left)
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Simplify, remove old (:eol :indent)
+ ;; This must be the last rule, to avoid not matching the
+ ;; rules for fast calculation of indentation above
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indreduce nil
+ (> *ind-offset* c-prev-offset)
+ ((:eol :indent))
+ )
+
+
+ (indinit (c-flow 0))
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; If
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indinit c-if-flow)
+ (indresolve :if
+ (and (< *ind-offset* *ind-start*)
+ (push c-flow c-if-flow)
+ (incf *indent* *base-indent*)
+ (incf c-flow)
+ )
+ )
+ (indresolve (:else-if :else)
+ (when c-if-flow
+ (while (< c-flow (car c-if-flow))
+ (incf *indent* *base-indent*)
+ (incf c-flow)
+ )
+ (or (eq *ind-token* :else-if) (pop c-if-flow))
+ )
+ (and (< *ind-offset* *ind-start*)
+ (incf *indent* *base-indent*)
+ (incf c-flow)
+ )
+ )
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; For/while/do
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indinit c-do-flow)
+ (indresolve (:for :while :do)
+ (if (eq *ind-token* :do)
+ (and (< *ind-offset* *ind-start*) (push c-flow c-do-flow))
+ (when (and c-do-flow (eq *ind-token* :while))
+ (while (< c-flow (car c-do-flow))
+ (incf *indent* *base-indent*)
+ (incf c-flow)
+ )
+ (pop c-do-flow)
+ )
+ )
+ (and (< *ind-offset* *ind-start*)
+ (incf *indent* *base-indent*)
+ (incf c-flow)
+ )
+ )
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Switch
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indresolve :switch
+ (setq c-case-flag nil)
+ )
+ (indresolve (:case :c-case)
+ (if (< *ind-offset* *ind-start*)
+ (or c-case-flag
+ (setq
+ *indent*
+ (+ (offset-indentation *ind-offset* :resolve t)
+ *base-indent*
+ )
+ )
+ )
+ (if c-case-flag
+ (and (= (decf c-cases) 0)
+ (decf *indent* *base-indent*)
+ )
+ (or *case-indent*
+ (decf *indent* *base-indent*)
+ )
+ )
+ )
+ (setq c-case-flag t)
+ )
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Braces/flow control
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indresolve :flow
+ (incf *indent* *base-indent*)
+ )
+ (indresolve :obrace
+ (and (< *ind-offset* *ind-start*)
+ (incf *indent* *base-indent*)
+ )
+ )
+ (indresolve :cbrace
+ (decf *indent* *base-indent*)
+ (and *case-indent* c-case-flag
+ (decf *indent* *base-indent*)
+ (setq c-case-flag nil)
+ )
+ (and (not *offset*) (>= *ind-offset* *ind-start*)
+ (setq *offset* *ind-offset*)
+ )
+ )
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Statements
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indresolve :stat
+ (when (< *ind-offset* *ind-start*)
+ (while (> c-flow 0)
+ (setq
+ *indent* (- *indent* *base-indent*)
+ c-flow (1- c-flow)
+ )
+ )
+ )
+ (and
+ *cont-indent*
+ (< *ind-offset* *ind-start*)
+ (> (+ *ind-offset* *ind-length*) *ind-start*)
+ (incf *indent* *base-indent*)
+ )
+ )
+
+ (indresolve :expression
+ (and
+ *cont-indent*
+ (zerop c-bra)
+ (> *indent* 0)
+ (< *ind-offset* *ind-start*)
+ (> (+ *ind-offset* *ind-length*) *ind-start*)
+ (incf *indent* *base-indent*)
+ )
+ )
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Open
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indresolve (:oparen :obrack)
+ (and (< *ind-offset* *ind-start*)
+ (setq *indent* (1+ (offset-indentation *ind-offset* :align t)))
+ )
+ )
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Find a "good" offset to start parsing backwards, so that it should
+;; always generate the same results.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun c-offset-indent (&aux char (point (point)))
+ ;; Skip spaces forward
+ (while (member (setq char (char-after point)) indent-spaces)
+ (incf point)
+ )
+ (or (characterp char) (return-from c-offset-indent point))
+
+ ;; Skip word chars
+ (when (alphanumericp char)
+ (while (and (setq char (char-after point)) (alphanumericp char))
+ (incf point)
+ )
+ (or (characterp char) (return-from c-offset-indent point))
+
+ ;; Skip spaces forward
+ (while (member (setq char (char-after point)) indent-spaces)
+ (incf point)
+ )
+ (or (characterp char) (return-from c-offset-indent point))
+ )
+
+ ;; don't include " or ' to avoid parsing strings "inverted"
+ (if (member char '(#\Newline #\" #\')) point (1+ point))
+)
+(compile 'c-offset-indent)
+
+(defun c-should-indent (options)
+ (when (hash-table-p options)
+ ;; check if previous line has extra spaces
+ (and (gethash :trim-blank-lines options)
+ (indent-clear-empty-line)
+ )
+
+ ;; indentation disabled?
+ (and (gethash :disable-indent options)
+ (return-from c-should-indent)
+ )
+
+ (let*
+ (
+ (point (point))
+ (start (scan point :eol :left))
+ (char (char-before point))
+ offset
+ match
+ text
+ )
+
+ ;; at the start of an empty file
+ (or (characterp char)
+ (return-from c-should-indent)
+ )
+
+ ;; if at bol and should indent only when starting a line
+ (and (gethash :only-newline-indent options)
+ (return-from c-should-indent (= point start))
+ )
+
+ (and
+ (char= char #\;)
+ (gethash :newline-after-semi options)
+ (return-from c-should-indent t)
+ )
+
+ ;; if one of these was typed, must check indentation
+ (and (member char '(#\{ #\} #\: #\] #\) #\#))
+ (return-from c-should-indent t)
+ )
+
+ ;; at the start of a line
+ (and (= point start)
+ (return-from c-should-indent (gethash :newline-indent options))
+ )
+
+ ;; if first character
+ (and (= point (1+ start))
+ (return-from c-should-indent t)
+ )
+
+ ;; check if is the first non-blank character in a new line
+ (when
+ (and
+ (gethash :cont-indent options)
+ (= point (scan point :eol :right))
+ (alphanumericp char)
+ )
+ (setq offset (1- point))
+ (while
+ (and
+ (> offset start)
+ (member (char-before offset) indent-spaces)
+ )
+ (decf offset)
+ )
+ ;; line has only one character with possible spaces before it
+ (and (<= offset start)
+ (return-from c-should-indent t)
+ )
+ )
+
+ ;; check for keywords that change indentation
+ (when (alphanumericp char)
+ (setq offset (1- point))
+ (while
+ (and
+ (alphanumericp (char-before offset))
+ (> offset start)
+ )
+ (decf offset)
+ )
+ (setq
+ text (read-text offset (- point offset))
+ match (re-exec #.(re-comp "(case|else|while)\\w?\\>")
+ text)
+ )
+ (and
+ (consp match)
+ (return-from c-should-indent (<= (- (caar match) offset) 2))
+ )
+ )
+ )
+ )
+ ;; Should not indent
+ nil
+)
+(compile 'c-should-indent)
+
+
+(defun c-indent-check (syntax syntable options
+ &aux start point char left brace change)
+ (setq
+ point (point)
+ char (char-before point)
+ left point
+ brace (member char '(#\{ #\}))
+ )
+
+ (when
+ (and brace (gethash :newline-before-brace options))
+ (setq start (scan point :eol :left))
+ (while
+ (and
+ (> (decf left) start)
+ (member (char-before left) indent-spaces)
+ )
+ ;; skip blanks
+ )
+ (when (> left start)
+ (replace-text left left (string #\Newline))
+ (c-indent syntax syntable)
+ (setq change t)
+ )
+ )
+
+ (when
+ (or
+ (and brace (not change) (gethash :newline-after-brace options))
+ (and (char= char #\;) (gethash :newline-after-semi options))
+ )
+ (setq left (point))
+ (replace-text left left (string #\Newline))
+ (goto-char (1+ left))
+ (c-indent syntax syntable)
+ )
+)
+
+(defun c-indent (syntax syntable)
+ (let*
+ (
+ (options (syntax-options syntax))
+ *base-indent*
+ *brace-indent*
+ *case-indent*
+ *label-dedent*
+ *cont-indent*
+ )
+
+ (or (c-should-indent options) (return-from c-indent))
+
+ (setq
+ *base-indent* (gethash :indentation options 4)
+ *brace-indent* (gethash :brace-indent options nil)
+ *case-indent* (gethash :case-indent options t)
+ *label-dedent* (gethash :label-dedent options t)
+ *cont-indent* (gethash :cont-indent options t)
+ )
+
+ (indent-macro
+ *c-mode-indent*
+ (c-offset-indent)
+ (gethash :emulate-tabs options)
+ )
+
+ (c-indent-check syntax syntable options)
+ )
+)
+(compile 'c-indent)
+
+(defsyntax *c-mode* :main nil #'c-indent *c-mode-options*
+ ;; All recognized C keywords.
+ (syntoken
+ (string-concat
+ "\\<("
+ "asm|auto|break|case|catch|char|class|const|continue|default|"
+ "delete|do|double|else|enum|extern|float|for|friend|goto|if|"
+ "inline|int|long|new|operator|private|protected|public|register|"
+ "return|short|signed|sizeof|static|struct|switch|template|this|"
+ "throw|try|typedef|union|unsigned|virtual|void|volatile|while"
+ ")\\>")
+ :property *prop-keyword*)
+
+ ;; Numbers, this is optional, comment this rule if xedit is
+ ;; too slow to load c files.
+ (syntoken
+ (string-concat
+ "\\<("
+ ;; Integers
+ "(\\d+|0x\\x+)(u|ul|ull|l|ll|lu|llu)?|"
+ ;; Floats
+ "\\d+\\.?\\d*(e[+-]?\\d+)?[lf]?"
+ ")\\>")
+ :icase t
+ :property *prop-number*
+ )
+
+ ;; String start rule.
+ (syntoken "\"" :nospec t :begin :string :contained t)
+
+ ;; Character start rule.
+ (syntoken "'" :nospec t :begin :character :contained t)
+
+ ;; Preprocessor start rule.
+ (syntoken "^\\s*#\\s*\\w+" :begin :preprocessor :contained t)
+
+ ;; Comment start rule.
+ (syntoken "/*" :nospec t :begin :comment :contained t)
+
+ ;; C++ style comments.
+ (syntoken "//.*" :property *prop-comment*)
+
+ ;; Punctuation, this is also optional, comment this rule if xedit is
+ ;; too slow to load c files.
+ (syntoken "[][(){}/*+:;=<>,&.!%|^~?-][][(){}*+:;=<>,&.!%|^~?-]?"
+ :property *prop-punctuation*)
+
+
+ ;; Rules for comments.
+ (syntable :comment *prop-comment* #'default-indent
+ ;; Match nested comments as an error.
+ (syntoken "/*" :nospec t :property *prop-error*)
+
+ (syntoken "XXX|TODO|FIXME" :property *prop-annotation*)
+
+ ;; Rule to finish a comment.
+ (syntoken "*/" :nospec t :switch -1)
+ )
+
+ ;; Rules for strings.
+ (syntable :string *prop-string* #'default-indent
+ ;; Ignore escaped characters, this includes \".
+ (syntoken "\\\\.")
+
+ ;; Match, most, printf arguments.
+ (syntoken "%%|%([+-]?\\d+)?(l?[deEfgiouxX]|[cdeEfgiopsuxX])"
+ :property *prop-format*)
+
+ ;; Ignore continuation in the next line.
+ (syntoken "\\\\$")
+
+ ;; Rule to finish a string.
+ (syntoken "\"" :nospec t :switch -1)
+
+ ;; Don't allow strings continuing in the next line.
+ (syntoken ".?$" :begin :error)
+ )
+
+ ;; Rules for characters.
+ (syntable :character *prop-constant* nil
+ ;; Ignore escaped characters, this includes \'.
+ (syntoken "\\\\.")
+
+ ;; Ignore continuation in the next line.
+ (syntoken "\\\\$")
+
+ ;; Rule to finish a character constant.
+ (syntoken "'" :nospec t :switch -1)
+
+ ;; Don't allow constants continuing in the next line.
+ (syntoken ".?$" :begin :error)
+ )
+
+ ;; Rules for preprocessor.
+ (syntable :preprocessor *prop-preprocessor* #'default-indent
+ ;; Preprocessor includes comments.
+ (syntoken "/*" :nospec t :begin :comment :contained t)
+
+ ;; Ignore lines finishing with a backslash.
+ (syntoken "\\\\$")
+
+ ;; Return to previous state if end of line found.
+ (syntoken ".?$" :switch -1)
+ )
+
+ (syntable :error *prop-error* nil
+ (syntoken "^.*$" :switch -2)
+ )
+
+ ;; You may also want to comment this rule if the parsing is
+ ;; noticeably slow.
+ (syntoken "\\c" :property *prop-control*)
+)
diff --git a/lisp/modules/progmodes/html.lsp b/lisp/modules/progmodes/html.lsp
new file mode 100644
index 0000000..86f8eea
--- /dev/null
+++ b/lisp/modules/progmodes/html.lsp
@@ -0,0 +1,327 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/html.lsp,v 1.3 2002/10/06 17:11:48 paulo Exp $
+;;
+
+(require "syntax")
+(in-package "XEDIT")
+
+#|
+ This is not a validation tool for html.
+
+ It is possible to, using macros generate all combinations of text attributes,
+ to properly handle <b>...<i>...</i>...</b> etc, as well as generating macros
+ to automatically closing tags, but for now this file was built to work as an
+ experience with the syntax highlight code.
+|#
+
+(defsynprop *prop-html-default*
+ "default"
+ :font "-*-lucida-medium-r-*-*-14-*-*-*-*-*-*-1"
+ :foreground "Gray10")
+
+(defsynprop *prop-html-bold*
+ "bold"
+ :font "-*-lucida-bold-r-*-*-14-*-*-*-*-*-*-1"
+ :foreground "Gray15")
+
+(defsynprop *prop-html-italic*
+ "italic"
+ :font "-*-lucida-medium-i-*-*-14-*-*-*-*-*-*-1"
+ :foreground "Gray10")
+
+(defsynprop *prop-html-pre*
+ "pre"
+ :font "-*-courier-medium-r-*-*-14-*-*-*-*-*-*-1"
+ :foreground "Gray10")
+
+(defsynprop *prop-html-link*
+ "link"
+ :font "-*-lucida-medium-r-*-*-14-*-*-*-*-*-*-1"
+ :foreground "Blue"
+ :underline "t")
+
+(defsynprop *prop-html-small*
+ "small"
+ :font "-*-lucida-medium-r-*-*-10-*-*-*-*-*-*-1"
+ :foreground "Gray10")
+
+(defsynprop *prop-html-big*
+ "big"
+ :font "-*-lucida-medium-r-*-*-20-*-*-*-*-*-*-1"
+ :foreground "Gray15")
+
+(defsynprop *prop-html-name*
+ "name"
+ :font "-*-lucida-bold-r-*-*-14-*-*-*-*-*-*-1"
+ :foreground "Black"
+ :background "rgb:e/f/e")
+
+(defsynprop *prop-html-h1*
+ "h1"
+ :font "-*-lucida-bold-r-*-*-20-*-*-*-*-*-*-1"
+ :foreground "Gray15")
+
+(defsynprop *prop-html-h2*
+ "h2"
+ :font "-*-lucida-bold-r-*-*-17-*-*-*-*-*-*-1"
+ :foreground "Gray15")
+
+(defsynprop *prop-html-h4*
+ "h4"
+ :font "-*-lucida-bold-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "Gray15")
+
+(defsynprop *prop-html-h5*
+ "h5"
+ :font "-*-lucida-bold-r-*-*-10-*-*-*-*-*-*-1"
+ :foreground "Gray15")
+
+(defsynprop *prop-html-li*
+ "li"
+ :font "-*-lucida-bold-r-*-*-8-*-*-*-*-*-*-1"
+ :foreground "rgb:0/5/0"
+ :underline t)
+
+(defsynprop *prop-html-hr*
+ "hr"
+ :font "-*-courier-bold-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "rgb:0/5/0"
+ :overstrike t)
+
+(defsynprop *prop-html-title*
+ "title"
+ :font "-*-lucida-medium-r-*-*-14-*-*-*-*-*-*-1"
+ :foreground "Red3"
+ :underline "t")
+
+(defsynprop *prop-html-tag*
+ "tag"
+ :font "-*-courier-medium-r-*-*-10-*-*-*-*-*-*-1"
+ :foreground "green4")
+
+(defsynprop *prop-html-string*
+ "string"
+ :font "-*-lucida-medium-r-*-*-10-*-*-*-*-*-*-1"
+ :foreground "RoyalBlue2")
+
+(defsynprop *prop-html-comment*
+ "comment"
+ :font "-*-courier-medium-o-*-*-10-*-*-*-*-*-*-1"
+ :foreground "SlateBlue3")
+
+(defsynprop *prop-html-entity*
+ "entity"
+ :font "-*-lucida-medium-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "Red4")
+
+(defsynprop *prop-html-unknown*
+ "unknown"
+ :font "-*-courier-bold-r-*-*-10-*-*-*-*-*-*-1"
+ :foreground "yellow"
+ :background "red")
+
+(defmacro html-syntoken (name)
+ `(syntoken (string-concat "<" ,name "\\>")
+ :icase t :contained t
+ :begin (intern (string-concat ,name "$") 'keyword)))
+(defmacro html-syntable (name property)
+ `(let
+ ((label (intern (string-concat ,name "$") 'keyword))
+ (nested-label (intern (string (gensym)) 'keyword)))
+ (syntable label *prop-html-tag* nil
+ (synaugment :generic-tag)
+ (syntoken ">" :nospec t :property *prop-html-tag* :begin nested-label)
+ (syntable nested-label ,property nil
+ (syntoken (string-concat "</" ,name ">")
+ :icase t :nospec t :property *prop-html-tag* :switch -2)
+ (syntoken (string-concat "</" ,name "\\s*$")
+ :icase t :contained t :begin :continued-end-tag)
+ (synaugment :main)))))
+
+
+(defsyntax *html-mode* :main *prop-html-default* nil nil
+ (syntoken "<!--" :nospec t :contained t :begin :comment)
+ (syntable :comment *prop-html-comment* nil
+ (syntoken "-->" :nospec t :switch -1))
+ (syntoken "&([a-zA-Z0-9_.-]+|#\\x\\x?);?" :property *prop-html-entity*)
+ (syntoken "<li>" :nospec t :icase t :property *prop-html-li*)
+ (syntoken "<hr>" :nospec t :icase t :property *prop-html-hr*)
+
+ (syntoken "<img\\>" :icase t :contained t :begin :tag)
+ (syntoken "<(p|br)>" :icase t :property *prop-html-tag*)
+
+ ;; If in the toplevel, unbalanced!
+ ;; XXX When adding new nested tables, don't forget to update this pattern.
+ (syntoken
+ (string-concat
+ "</("
+ "b|strong|i|em|address|pre|code|tt|small|big|a|span|div|"
+ "h1|h2|h3|h4|h5|title|font|ol|ul|dl|dt|dd|menu"
+ ")\\>")
+ :icase t :property *prop-html-unknown* :begin :unbalanced)
+ (syntable :unbalanced *prop-html-unknown* nil
+ (syntoken ">" :nospec t :switch :main)
+ (synaugment :generic-tag)
+ )
+
+ #||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+ ;; XXX ONLY add a rule for "html", "head" and "body" if you want to do a
+ ;; more complete check for common errors. If you add those rules, it will
+ ;; reparse the entire file at every character typed (unless there are
+ ;; errors in which case the parser resets the state).
+ ;; For visualization only that would be OK...
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||#
+
+ (html-syntoken "b")
+ (html-syntable "b" *prop-html-bold*)
+ (html-syntoken "strong")
+ (html-syntable "strong" *prop-html-bold*)
+
+ (html-syntoken "i")
+ (html-syntable "i" *prop-html-italic*)
+ (html-syntoken "em")
+ (html-syntable "em" *prop-html-italic*)
+ (html-syntoken "address")
+ (html-syntable "address" *prop-html-italic*)
+
+ (html-syntoken "pre")
+ (html-syntable "pre" *prop-html-pre*)
+ (html-syntoken "code")
+ (html-syntable "code" *prop-html-pre*)
+ (html-syntoken "tt")
+ (html-syntable "tt" *prop-html-pre*)
+
+ (html-syntoken "small")
+ (html-syntable "small" *prop-html-small*)
+
+ (html-syntoken "big")
+ (html-syntable "big" *prop-html-big*)
+
+ ;; Cannot hack html-syntoken and html-syntable to handle this,
+ ;; as the option to <a may be in the next line.
+ (syntoken "<a\\>" :icase t :contained t :begin :a)
+ (syntable :a *prop-html-tag* nil
+ ;; Tag is open
+ (syntoken "\\<href\\>" :icase t :begin :a-href)
+ (syntoken "\\<name\\>" :icase t :begin :a-name)
+ (syntoken "<" :nospec t :property *prop-html-unknown* :switch -2)
+ (synaugment :generic-tag)
+ (syntoken ">" :nospec t :begin :a-generic-text)
+ (syntable :a-href *prop-html-tag* nil
+ (syntoken ">" :nospec t :begin :a-href-text)
+ (synaugment :generic-tag)
+ (syntable :a-href-text *prop-html-link* nil
+ (syntoken "</a>"
+ :icase t :nospec t :property *prop-html-tag* :switch -3)
+ (syntoken "</a\\s*$" :icase t :begin :continued-nested-end-tag)
+ (synaugment :main)
+ )
+ )
+ (syntable :a-name *prop-html-tag* nil
+ (syntoken ">" :nospec t :begin :a-name-text)
+ (synaugment :generic-tag)
+ (syntable :a-name-text *prop-html-name* nil
+ (syntoken "</a>"
+ :icase t :nospec t :property *prop-html-tag* :switch -3)
+ (syntoken "</a\\s*$" :icase t :begin :continued-nested-end-tag)
+ (synaugment :main)
+ )
+ )
+ (syntable :a-generic-text nil nil
+ (syntoken "</a>"
+ :icase t :nospec t :property *prop-html-tag* :switch -2)
+ (syntoken "<a/\\s$" :icase t :begin :continued-end-tag)
+ (synaugment :main)
+ )
+ )
+
+ ;; Do nothing, just check start/end tags
+ (html-syntoken "ol")
+ (html-syntable "ol" nil)
+ (html-syntoken "ul")
+ (html-syntable "ul" nil)
+ (html-syntoken "dl")
+ (html-syntable "dl" nil)
+ ;; Maybe <dt> and <dd> should be in a special table, to not require
+ ;; and ending tag.
+ ;; XXX Maybe should also add a table for <p>.
+ (html-syntoken "dt")
+ (html-syntable "dt" nil)
+ (html-syntoken "dd")
+ (html-syntable "dd" nil)
+
+ (html-syntoken "span")
+ (html-syntable "span" nil)
+ (html-syntoken "div")
+ (html-syntable "div" nil)
+ (html-syntoken "menu")
+ (html-syntable "menu" nil)
+
+ (html-syntoken "h1")
+ (html-syntable "h1" *prop-html-h1*)
+ (html-syntoken "h2")
+ (html-syntable "h2" *prop-html-h2*)
+ (html-syntoken "h3")
+ (html-syntable "h3" *prop-html-bold*)
+ (html-syntoken "h4")
+ (html-syntable "h4" *prop-html-h4*)
+ (html-syntoken "h5")
+ (html-syntable "h5" *prop-html-h5*)
+ (html-syntoken "title")
+ (html-syntable "title" *prop-html-title*)
+
+ (html-syntoken "font")
+ (html-syntable "font" *prop-control*)
+
+ (syntoken "<" :nospec t :contained t :begin :tag)
+ (syntable :generic-tag *prop-html-tag* nil
+ (syntoken "\"" :nospec t :contained t :begin :string)
+ (syntoken "<" :nospec t :property *prop-html-unknown*)
+ )
+ (syntable :tag *prop-html-tag* nil
+ (syntoken ">" :nospec t :switch -1)
+ (synaugment :generic-tag)
+ )
+ ;; Tag ended in a newline, common practice...
+ (syntable :continued-end-tag *prop-html-tag* nil
+ (syntoken ">" :nospec t :switch -3)
+ (synaugment :generic-tag)
+ )
+ (syntable :continued-nested-end-tag *prop-html-tag* nil
+ (syntoken ">" :nospec t :switch -4)
+ (synaugment :generic-tag)
+ )
+
+ (syntable :string *prop-html-string* nil
+ (syntoken "\\\\.")
+ (syntoken "\"" :nospec t :switch -1)
+ )
+)
diff --git a/lisp/modules/progmodes/imake.lsp b/lisp/modules/progmodes/imake.lsp
new file mode 100644
index 0000000..ea34ed6
--- /dev/null
+++ b/lisp/modules/progmodes/imake.lsp
@@ -0,0 +1,188 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/imake.lsp,v 1.2 2002/10/06 17:11:48 paulo Exp $
+;;
+
+(require "syntax")
+(in-package "XEDIT")
+
+(defsynprop *prop-shell*
+ "shell"
+ :font "*courier-bold-r*12*"
+ :foreground "Red4"
+)
+
+(defsynprop *prop-variable*
+ "variable"
+ :font "*courier-medium-r*12*"
+ :foreground "Red3"
+)
+
+;; The syntax-highlight definition does not try to flag errors, just show
+;; tabs in the start of lines for better visualization.
+(defsynprop *prop-tabulation*
+ "tabulation"
+ :font "*courier-medium-r*12*"
+ :background "Gray90"
+)
+
+(defsynprop *prop-xcomm*
+ "xcomm"
+ :font "*courier-medium-o*12*"
+ :foreground "SkyBlue4"
+)
+
+
+(defsyntax *imake-mode* :main nil nil nil
+ (syntoken "^\\s*XCOMM\\W?.*$"
+ :property *prop-xcomm*)
+
+ (syntoken "^\\t+"
+ :property *prop-tabulation*)
+
+ (syntoken "$("
+ :nospec t
+ :begin :shell
+ :property *prop-shell*)
+
+ (syntoken "[][(){};$<=>&@/\\,.:~!|*?'`+-]"
+ :property *prop-shell*)
+
+ ;; Preprocessor start rule.
+ (syntoken "^\\s*#\\s*\\w+"
+ :begin :preprocessor
+ :contained t)
+
+ ;; Comment start rule.
+ (syntoken "/*"
+ :nospec t
+ :begin :comment
+ :contained t)
+
+ ;; String start rule.
+ (syntoken "\""
+ :begin :string
+ :nospec t
+ :contained t)
+
+ ;; Quoted string start rule.
+ (syntoken "\\\""
+ :begin :quoted-string
+ :nospec t
+ :contained t)
+
+ (syntable :shell *prop-variable* nil
+ (syntoken ")"
+ :nospec t
+ :property *prop-shell*
+ :switch -1)
+ )
+
+ ;; Rules for comments.
+ (syntable :comment *prop-comment* nil
+
+ ;; Match nested comments as an error.
+ (syntoken "/*"
+ :nospec t
+ :property *prop-error*)
+
+ (syntoken "XXX|TODO|FIXME"
+ :property *prop-annotation*)
+
+ ;; Rule to finish a comment.
+ (syntoken "*/"
+ :nospec t
+ :switch -1)
+ )
+
+ ;; Rules for preprocessor.
+ (syntable :preprocessor *prop-preprocessor* nil
+
+ ;; Preprocessor includes comments.
+ (syntoken "/*"
+ :nospec t
+ :begin :comment
+ :contained t)
+
+ ;; Visualization help, show tabs in the start of lines.
+ (syntoken "^\\t+"
+ :property *prop-tabulation*)
+
+ ;; Ignore lines finishing with a backslash.
+ (syntoken "\\\\$")
+
+ ;; Return to previous state if end of line found.
+ (syntoken ".?$"
+ :switch -1)
+ )
+
+ ;; Rules for strings.
+ (syntable :string *prop-string* nil
+
+ ;; Ignore escaped characters, this includes \".
+ (syntoken "\\\\.")
+
+ ;; Ignore continuation in the next line.
+ (syntoken "\\\\$")
+
+ ;; Rule to finish a string.
+ (syntoken "\""
+ :nospec t
+ :switch -1)
+
+ ;; Don't allow strings continuing in the next line.
+ (syntoken ".?$"
+ :begin :error)
+ )
+
+ ;; Rules for quoted strings.
+ (syntable :quoted-string *prop-constant* nil
+
+ ;; Rule to finish the quoted string.
+ (syntoken "\\\""
+ :nospec t
+ :switch -1)
+
+ ;; Ignore escaped characters
+ (syntoken "\\\\.")
+
+ ;; Ignore continuation in the next line.
+ (syntoken "\\\\$")
+
+ ;; Don't allow strings continuing in the next line.
+ (syntoken ".?$"
+ :begin :error)
+ )
+
+ (syntable :error *prop-error* nil
+ (syntoken "^.*$"
+ :switch -2)
+ )
+)
diff --git a/lisp/modules/progmodes/lisp.lsp b/lisp/modules/progmodes/lisp.lsp
new file mode 100644
index 0000000..ebf2c10
--- /dev/null
+++ b/lisp/modules/progmodes/lisp.lsp
@@ -0,0 +1,384 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/lisp.lsp,v 1.9 2003/01/30 02:46:26 paulo Exp $
+;;
+
+(require "syntax")
+(require "indent")
+(in-package "XEDIT")
+
+(defsynprop *prop-special*
+ "special"
+ :font "*courier-bold-r*12*"
+ :foreground "NavyBlue"
+)
+
+(defsynprop *prop-quote*
+ "quote"
+ :font "*courier-bold-r*12*"
+ :foreground "Red3"
+)
+
+(defsynprop *prop-package*
+ "package"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "Gold4"
+)
+
+(defsynprop *prop-unreadable*
+ "unreadable"
+ :font "*courier-medium-r*12*"
+ :foreground "Gray25"
+ :underline t
+)
+
+(defsynoptions *lisp-DEFAULT-style*
+ ;; Positive number. Basic indentation.
+ (:indentation . 2)
+
+ ;; Boolean. Move cursor to the indent column after pressing <Enter>?
+ (:newline-indent . t)
+
+ ;; Boolean. Use spaces instead of tabs to fill indentation?
+ (:emulate-tabs . nil)
+
+ ;; Boolean. Remove extra spaces from previous line.
+ ;; This should default to T when newline-indent is not NIL.
+ (:trim-blank-lines . t)
+
+ ;; Boolean. If this hash-table entry is set, no indentation is done.
+ ;; Useful to temporarily disable indentation.
+ (:disable-indent . nil)
+)
+
+(defvar *lisp-mode-options* *lisp-DEFAULT-style*)
+
+(defindent *lisp-mode-indent* :main
+ ;; this must be the first token
+ (indtoken "^\\s*" :indent
+ :code (or *offset* (setq *offset* (+ *ind-offset* *ind-length*))))
+ ;; ignore single line comments
+ (indtoken ";.*$" nil)
+ ;; multiline comments
+ (indtoken "|#" :comment :nospec t :begin :comment)
+ ;; characters
+ (indtoken "#\\\\(\\W|\\w+(-\\w+)?)" :character)
+ ;; numbers
+ (indtoken
+ (string-concat
+ "(\\<|[+-])\\d+("
+ ;; integers
+ "(\\>|\\.(\\s|$))|"
+ ;; ratios
+ "/\\d+\\>|"
+ ;;floats
+ "\\.?\\d*([SsFfDdLlEe][+-]?\\d+)?\\>"
+ ")")
+ :number)
+ ;; symbols, with optional package
+ (indtoken
+ (string-concat
+ ;; optional package name and ending ':'
+ "([A-Za-z_0-9%-]+:)?"
+ ;; internal symbol if after package name, or keyword
+ ":?"
+ ;; symbol name
+ "[][{}A-Za-z_0-9!$%&/<=>^~*+-]+")
+ :symbol)
+ ;; strings in the same line
+ (indtoken "\"([^\\\"]|\\\\.)*\"" :string)
+ ;; multiline strings
+ (indtoken "\"" :cstring :nospec t :begin :string)
+ ;; "quoted" symbols in the same line
+ (indtoken "\\|([^\\|]|\\\\.)*\\|" :symbol)
+ ;; multiline
+ (indtoken "|" :csymbol :nospec t :begin :symbol)
+ (indtoken "#" :hash :nospec t)
+
+ (indinit (parens 0))
+ (indtoken "(" :oparen :nospec t :code (incf parens))
+ (indtoken ")" :cparen :nospec t :code (decf parens))
+
+ (indtable :comment
+ ;; multiline comments can nest
+ (indtoken "|#" nil :nospec t :begin :comment)
+ (indtoken "#|" nil :nospec t :switch -1))
+
+ (indtable :string
+ ;; Ignore escaped characters
+ (indtoken "\\." nil)
+ ;; Return to the toplevel when the start of the string is found
+ (indtoken "\"" :ostring :nospec t :switch -1))
+
+ (indtable :symbol
+ ;; Ignore escaped characters
+ (indtoken "\\." nil)
+ ;; Return to the toplevel when the start of the symbol is found
+ (indtoken "|" :osymbol :nospec t :switch -1))
+
+ ;; ignore comments
+ (indreduce nil
+ t
+ ((:comment)))
+
+ ;; reduce multiline strings
+ (indreduce :string
+ t
+ ((:ostring (not :ostring) :cstring)))
+
+ ;; reduce multiline symbols
+ (indreduce :symbol
+ t
+ ((:osymbol (not :osymbol) :csymbol)))
+
+ ;; reduce basic types, don't care if inside list or not
+ (indreduce :element
+ t
+ ((:number)
+ (:string)
+ (:character)
+ (:element :element)
+ (:indent :element)))
+
+ (indreduce :symbol
+ t
+ ((:symbol :symbol)
+ (:symbol :element)
+ (:indent :symbol)))
+
+ ;; the "real" indentation value, to make easier parsing code like:
+ ;; (foo (bar (baz (blah
+ ;; ^ ^
+ ;; | |
+ ;; indent |
+ ;; effective indentation to be used
+ (indinit (indent 0))
+
+ ;; indentation values of opening parenthesis.
+ (indinit stack)
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; if before current line and open parenthesis >= 0, use indentation
+ ;; of current line to calculate relative indentation.
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (indreduce :oparen ;; simple list?
+ (and (>= parens 0) (< *ind-offset* *ind-start*))
+ ((:indent :oparen))
+ (setq
+ *indent* (offset-indentation (+ *ind-offset* *ind-length*) :resolve t)
+ indent *indent*)
+ (indent-macro-reject-left))
+
+ ;; reduce list if there isn't indentation change
+ (indreduce :element
+ t
+ ((:oparen (not :oparen) :cparen)))
+
+ (indresolve :oparen
+ (setq
+ *indent*
+ (offset-indentation
+ (+ *ind-offset* *ind-length* -1 *base-indent*) :align t))
+ (push *indent* stack)
+ (incf indent *base-indent*)
+ (if (< *indent* indent) (setq *indent* indent)))
+
+ (indresolve :cparen
+ (decf indent *base-indent*)
+ (setq *indent* (pop stack))
+ (if (null stack)
+ (setq *indent* indent)
+ (setq *indent* (car stack))))
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Find a "good" offset to start parsing backwards, so that it should
+;; always generate the same results.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun lisp-offset-indent (&aux char (point (scan (point) :eol :left)))
+ ;; skip spaces
+ (while (member (setq char (char-after point)) indent-spaces)
+ (incf point))
+ (if (member char '(#\))) (1+ point) point))
+
+(defun lisp-should-indent (options &aux char point start)
+ (when (hash-table-p options)
+ ;; check if previous line has extra spaces
+ (and (gethash :trim-blank-lines options)
+ (indent-clear-empty-line))
+
+ ;; indentation disabled?
+ (and (gethash :disable-indent options)
+ (return-from lisp-should-indent))
+
+ (setq
+ point (point)
+ char (char-before (point))
+ start (scan point :eol :left))
+
+ ;; at the start of a line
+ (and (= point start)
+ (return-from lisp-should-indent (gethash :newline-indent options)))
+
+ ;; if first character
+ (and (= point (1+ start)) (return-from lisp-should-indent t))
+
+ ;; if closing parenthesis and first nonblank char
+ (when (and (characterp char) (char= char #\)))
+ (decf point)
+ (while
+ (and (> point start) (member (char-before point) indent-spaces))
+ (decf point))
+ (return-from lisp-should-indent (<= point start)))
+ )
+ ;; should not indent
+ nil)
+
+(defun lisp-indent (syntax syntable)
+ (let*
+ ((options (syntax-options syntax))
+ *base-indent*)
+
+ (or (lisp-should-indent options) (return-from lisp-indent))
+
+ (setq *base-indent* (gethash :indentation options 2))
+
+ (indent-macro
+ *lisp-mode-indent*
+ (lisp-offset-indent)
+ (gethash :emulate-tabs options))))
+
+(compile 'lisp-indent)
+
+(defsyntax *lisp-mode* :main nil #'lisp-indent *lisp-mode-options*
+ ;; highlight car and parenthesis
+ (syntoken "\\(+\\s*[][{}A-Za-z_0-9!$%&/<=>?^~*:+-]*\\)*"
+ :property *prop-keyword*)
+ (syntoken "\\)+" :property *prop-keyword*)
+
+ ;; nil and t
+ (syntoken "\\<(nil|t)\\>" :icase t :property *prop-special*)
+
+ (syntoken "|" :nospec t :begin :unreadable :contained t)
+
+ ;; keywords
+ (syntoken ":[][{}A-Za-z_0-9!$%&/<=>^~+-]+" :property *prop-constant*)
+
+ ;; special symbol.
+ (syntoken "\\*[][{}A-Za-z_0-9!$%&7=?^~+-]+\\*"
+ :property *prop-special*)
+
+ ;; special identifiers
+ (syntoken "&(aux|key|optional|rest)\\>" :icase t :property *prop-constant*)
+
+ ;; numbers
+ (syntoken
+ ;; since lisp is very liberal in what can be a symbol, this pattern
+ ;; will not always work as expected, since \< and \> will not properly
+ ;; work for all characters that may be in a symbol name
+ (string-concat
+ "(\\<|[+-])\\d+("
+ ;; integers
+ "(\\>|\\.(\\s|$))|"
+ ;; ratios
+ "/\\d+\\>|"
+ ;;floats
+ "\\.?\\d*([SsFfDdLlEe][+-]?\\d+)?\\>"
+ ")")
+ :property *prop-number*)
+
+ ;; characters
+ (syntoken "#\\\\(\\W|\\w+(-\\w+)?)" :property *prop-constant*)
+
+ ;; quotes
+ (syntoken "[`'.]|,@?" :property *prop-quote*)
+
+ ;; package names
+ (syntoken "[A-Za-z_0-9%-]+::?" :property *prop-package*)
+
+ ;; read time evaluation
+ (syntoken "#\\d+#" :property *prop-preprocessor*)
+ (syntoken "#([+'cCsS-]|\\d+[aA=])?" :begin :preprocessor :contained t)
+
+ (syntoken "\\c" :property *prop-control*)
+
+ ;; symbols, do nothing, just resolve conflicting matches
+ (syntoken "[][{}A-Za-z_0-9!$%&/<=>^~*+-]+")
+
+ (syntable :simple-comment *prop-comment* nil
+ (syntoken "$" :switch -1)
+ (syntoken "XXX|FIXME|TODO" :property *prop-annotation*))
+
+ (syntable :comment *prop-comment* nil
+ ;; comments can nest
+ (syntoken "#|" :nospec t :begin :comment)
+ ;; return to previous state
+ (syntoken "|#" :nospec t :switch -1)
+ (syntoken "XXX|FIXME|TODO" :property *prop-annotation*))
+
+ (syntable :unreadable *prop-unreadable* nil
+ ;; ignore escaped characters
+ (syntoken "\\\\.")
+ (syntoken "|" :nospec t :switch -1))
+
+ (syntable :string *prop-string* nil
+ ;; ignore escaped characters
+ (syntoken "\\\\.")
+ (syntoken "\"" :nospec t :switch -1))
+
+ (syntable :preprocessor *prop-preprocessor* nil
+ ;; a symbol
+ (syntoken "[][{}A-Za-z_0-9!$%&/<=>^~:*+-]+" :switch -1)
+
+ ;; conditional expression
+ (syntoken "(" :nospec t :begin :preprocessor-expression :contained t)
+
+ (syntable :preprocessor-expression *prop-preprocessor* nil
+ ;; recursive
+ (syntoken "(" :nospec t :begin :preprocessor-recursive :contained t)
+ (syntoken ")" :nospec t :switch -2)
+
+ (syntable :preprocessor-recursive *prop-preprocessor* nil
+ (syntoken "(" :nospec t
+ :begin :preprocessor-recursive
+ :contained t)
+ (syntoken ")" :nospec t :switch -1)
+ (synaugment :comments-and-strings))
+ (synaugment :comments-and-strings))
+ (synaugment :comments-and-strings))
+
+ (syntable :comments-and-strings nil nil
+ (syntoken "\"" :nospec t :begin :string :contained t)
+ (syntoken "#|" :nospec t :begin :comment :contained t)
+ (syntoken ";" :begin :simple-comment :contained t))
+
+ (synaugment :comments-and-strings)
+)
diff --git a/lisp/modules/progmodes/make.lsp b/lisp/modules/progmodes/make.lsp
new file mode 100644
index 0000000..d5cbc00
--- /dev/null
+++ b/lisp/modules/progmodes/make.lsp
@@ -0,0 +1,135 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/make.lsp,v 1.2 2002/10/06 17:11:48 paulo Exp $
+;;
+
+(require "syntax")
+(in-package "XEDIT")
+
+(defsynprop *prop-shell*
+ "shell"
+ :font "*courier-bold-r*12*"
+ :foreground "Red4"
+)
+
+(defsynprop *prop-variable*
+ "variable"
+ :font "*courier-medium-r*12*"
+ :foreground "Red3"
+)
+
+;; The syntax-highlight definition does not try to flag errors, just show
+;; tabs in the start of lines for better visualization.
+(defsynprop *prop-tabulation*
+ "tabulation"
+ :font "*courier-medium-r*12*"
+ :background "Gray90"
+)
+
+
+(defsyntax *make-mode* :main nil nil nil
+ (syntoken "^\\t+" :property *prop-tabulation*)
+
+ (syntoken "^\\.\\w+" :property *prop-keyword*)
+
+ (syntoken "$("
+ :nospec t
+ :begin :shell
+ :property *prop-shell*)
+
+ (syntoken "[][(){};$<=>&@/\\,.:~!|*?'`+-]"
+ :property *prop-shell*)
+
+ ;; Preprocessor start rule.
+ (syntoken "#.*"
+ :property *prop-comment*)
+
+ ;; String start rule.
+ (syntoken "\""
+ :begin :string
+ :nospec t
+ :contained t)
+
+ ;; Quoted string start rule.
+ (syntoken "\\\""
+ :begin :quoted-string
+ :nospec t
+ :contained t)
+
+ (syntable :shell *prop-variable* nil
+ (syntoken ")"
+ :nospec t
+ :property *prop-shell*
+ :switch -1)
+ )
+
+ ;; Rules for strings.
+ (syntable :string *prop-string* nil
+
+ ;; Ignore escaped characters, this includes \".
+ (syntoken "\\\\.")
+
+ ;; Ignore continuation in the next line.
+ (syntoken "\\\\$")
+
+ ;; Rule to finish a string.
+ (syntoken "\""
+ :nospec t
+ :switch -1)
+
+ ;; Don't allow strings continuing in the next line.
+ (syntoken ".?$"
+ :begin :error)
+ )
+
+ ;; Rules for quoted strings.
+ (syntable :quoted-string *prop-constant* nil
+
+ ;; Rule to finish the quoted string.
+ (syntoken "\\\""
+ :nospec t
+ :switch -1)
+
+ ;; Ignore escaped characters
+ (syntoken "\\\\.")
+
+ ;; Ignore continuation in the next line.
+ (syntoken "\\\\$")
+
+ ;; Don't allow strings continuing in the next line.
+ (syntoken ".?$"
+ :begin :error)
+ )
+
+ (syntable :error *prop-error* nil
+ (syntoken "^.*$"
+ :switch -2)
+ )
+)
diff --git a/lisp/modules/progmodes/man.lsp b/lisp/modules/progmodes/man.lsp
new file mode 100644
index 0000000..77a59a8
--- /dev/null
+++ b/lisp/modules/progmodes/man.lsp
@@ -0,0 +1,160 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/man.lsp,v 1.2 2002/10/06 17:11:48 paulo Exp $
+;;
+
+(require "syntax")
+(in-package "XEDIT")
+
+(defsynprop *prop-man-b*
+ "b"
+ :font "*courier-bold-r*12*"
+ :foreground "gray12"
+)
+
+(defsynprop *prop-man-i*
+ "i"
+ :font "*courier-medium-o*12*"
+ :foreground "black"
+)
+
+(defsynprop *prop-man-bi*
+ "bi"
+ :font "*courier-bold-o*12*"
+ :foreground "gray20"
+)
+
+(defsynprop *prop-man-th*
+ "th"
+ :font "-*-courier-*-*-*-*-18-*-*-*-*-*-*-1"
+ :foreground "Red3"
+)
+
+(defsynprop *prop-man-sh*
+ "sh"
+ :font "-*-courier-*-*-*-*-14-*-*-*-*-*-*-1"
+ :foreground "OrangeRed3"
+)
+
+(defsynprop *prop-man-ss*
+ "ss"
+ :font "-*-courier-*-*-*-*-12-*-*-*-*-*-*-1"
+ :foreground "Gold4"
+)
+
+(defsynprop *prop-man-escape*
+ "escape"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "RoyalBlue4"
+)
+
+(defsynprop *prop-man-string*
+ "string"
+ :font "*lucidatypewriter-bold-r*12*"
+ :foreground "RoyalBlue3"
+; :underline t
+)
+
+(defmacro man-syntoken (pattern)
+ `(syntoken (string-concat "^\\.(" ,pattern ")(\\s+|$)")
+ :icase t
+; :contained t
+ :property *prop-preprocessor*
+ :begin (intern (string ,pattern) 'keyword)))
+
+(defmacro man-syntable (pattern property)
+ `(syntable (intern (string ,pattern) 'keyword) ,property nil
+ (syntoken "$" :switch -1)
+ (synaugment :extras)))
+
+
+(defsyntax *man-mode* :main nil nil nil
+ (syntoken "^\\.\\\\\".*"
+ :property *prop-comment*)
+
+ (man-syntoken "b|br|nm")
+ (man-syntable "b|br|nm" *prop-man-b*)
+
+ (man-syntoken "i|ir|ri|ip")
+ (man-syntable "i|ir|ri|ip" *prop-man-i*)
+
+ (man-syntoken "th|dt")
+ (man-syntable "th|dt" *prop-man-th*)
+
+ (man-syntoken "sh")
+ (man-syntable "sh" *prop-man-sh*)
+
+ (man-syntoken "ss")
+ (man-syntable "ss" *prop-man-ss*)
+
+ (man-syntoken "bi")
+ (man-syntable "bi" *prop-man-bi*)
+
+ ;; Anything not matched...
+ (syntoken "^\\.[a-z][a-z](\\s+|$)"
+ :icase t
+ :property *prop-preprocessor*)
+
+ (syntable :extras nil nil
+ (syntoken "\\<__\\l+__\\>"
+ :property *prop-constant*)
+ (syntoken "\\\\fB"
+ :property *prop-preprocessor*
+ :begin :b)
+ (syntoken "\\\\fI"
+ :property *prop-preprocessor*
+ :begin :i)
+ (syntoken "\\\\f\\u"
+ :property *prop-preprocessor*)
+
+ (syntoken "\\\\\\*?."
+ :property *prop-man-escape*)
+
+ (syntoken "\""
+ :property *prop-man-string*)
+
+ (syntable :i *prop-man-i* nil
+ (syntoken "$"
+ :switch :main)
+ (syntoken "\\\\f\\u"
+ :property *prop-preprocessor*
+ :switch -1)
+ )
+ (syntable :b *prop-man-b* nil
+ (syntoken "$"
+ :switch :main)
+ (syntoken "\\\\f\\u"
+ :property *prop-preprocessor*
+ :switch -1)
+ )
+ )
+
+ (synaugment :extras)
+)
diff --git a/lisp/modules/progmodes/rpm.lsp b/lisp/modules/progmodes/rpm.lsp
new file mode 100644
index 0000000..bd0cc6c
--- /dev/null
+++ b/lisp/modules/progmodes/rpm.lsp
@@ -0,0 +1,166 @@
+;;
+;; Copyright (c) 2003 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/rpm.lsp,v 1.1 2003/01/16 03:50:46 paulo Exp $
+;;
+
+(require "syntax")
+(in-package "XEDIT")
+
+;; Only for testing, unifinished, good for viewing but too slow for real use...
+#|
+(defsynprop *prop-rpm-special*
+ "rpm-special"
+ :font "*courier-bold-r*12*"
+ :foreground "NavyBlue"
+)
+
+(defsynprop *prop-rpm-escape*
+ "rpm-escape"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "Red3")
+
+;; main package is implicit
+(defsyntax *rpm-mode* :package nil nil nil
+ (syntable :sections nil nil
+ (syntoken "^%package"
+ :icase t
+ :switch :package
+ ;; XXX :begin :package was added just to test finishing and
+ ;; starting a new syntax-table, unfortunately if using it
+ ;; this way, frequently the entire file will be reparsed
+ ;; at every character typed.
+ ;; TODO study these cases and implement code to avoid it,
+ ;; the easiest way is limiting the number of backtracked lines,
+ ;; the screen contents sometimes could not correctly reflect
+ ;; file contents in this case...
+ :begin :package
+ :property *prop-rpm-special*)
+ (syntoken "^%(build|setup|install|pre|preun|post|postun)\\>"
+ :icase t
+ :property *prop-rpm-special*
+ :switch :package
+ :begin :shell)
+ ;; %changelog, XXX no rules to return to the toplevel
+ (syntoken "^%changelog\\>"
+ :icase t
+ :switch :package
+ :begin :changelog
+ :property *prop-rpm-special*)
+ (syntable :changelog nil nil
+ ;; ignore if escaped
+ (syntoken "%%")
+ ;; "warn" if not escaped
+ (syntoken "%" :property *prop-control*)
+ ;; emails
+ (syntoken "<[a-z0-9_-]+@[a-z0-9_-]+\\.\\w+(\\.\\w+)?>"
+ :icase t
+ :property *prop-string*)
+ )
+ ;; comments
+ (syntoken "#" :contained t :nospec t :begin :comment)
+ (syntable :comment *prop-comment* nil
+ ;; some macros are expanded even when inside comments, and may
+ ;; cause surprises, "warn" about it
+ (syntoken "%\\{?\\w+\\}?" :property *prop-rpm-special*)
+ (syntoken "$" :switch -1)
+ )
+ (synaugment :global)
+ )
+
+ ;; may appear anywhere
+ (syntable :global nil nil
+ ;; preprocessor like commands
+ (syntoken "^%(define|if|ifarch|else|endif)\\>"
+ :icase t
+ :property *prop-preprocessor*)
+ ;; variables
+ (syntoken "%\\{.*\\}" :property *prop-constant*)
+ )
+
+ ;; example: "Group: ..." or "Group(pt_BR): ..."
+ (syntoken "^\\w+(\\(\\w+\\))?:" :property *prop-keyword*)
+
+ ;; for sections with shell commands
+ (syntable :shell nil nil
+ (syntoken "\\<(if|then|elif|else|fi|for|do|done|case|esac|while|until)\\>"
+ :property *prop-keyword*)
+ (syntable :strings nil nil
+ (syntoken "\"" :nospec t :begin :string :contained t)
+ (syntable :string *prop-string* nil
+ (syntoken "\\$\\(?\\w+\\)?" :property *prop-constant*)
+ (syntoken "\\\\.")
+ (syntoken "\"" :nospec t :switch -1)
+ )
+ (syntoken "\'" :nospec t :begin :constant :contained t)
+ (syntable :constant *prop-constant* nil
+ (syntoken "\\\\.")
+ (syntoken "\'" :nospec t :switch -1)
+ )
+ (syntoken "\`" :nospec t :begin :escape :contained t)
+ (syntable :escape *prop-rpm-escape* nil
+ (syntoken "\\$\\(?\\w+\\)?" :property *prop-constant*)
+ (syntoken "\\\\.")
+ (syntoken "\`" :nospec t :switch -1)
+ )
+ )
+ (synaugment :strings :sections)
+ )
+ (synaugment :sections)
+)
+|#
+
+
+(defsyntax *rpm-mode* :package nil nil nil
+ ;; commands, macro definitions, etc
+ (syntoken "^\\s*%\\s*\\w+" :property *prop-keyword*)
+
+ ;; rpm "variables"
+ (syntoken "%\\{.*\\}" :property *prop-constant*)
+
+ ;; package info, example: "Group: ...", "Group(pt_BR): ...", etc.
+ (syntoken "^\\w+(\\(\\w+\\))?:" :property *prop-preprocessor*)
+
+ ;; comments
+ (syntoken "#" :contained t :nospec t :begin :comment)
+ (syntable :comment *prop-comment* nil
+ ;; some macros are expanded even when inside comments, and may
+ ;; cause surprises, "warn" about it
+ (syntoken "%define\\>" :property *prop-control*)
+ (syntoken "%\\{?\\w+\\}?" :property *prop-string*)
+ (syntoken "$" :switch -1)
+ )
+
+ ;; emails
+ (syntoken "<?[a-z0-9_-]+@[a-z0-9_-]+\\.\\w+(\\.\\w+)*>?"
+ :icase t
+ :property *prop-string*)
+ ;; links
+ (syntoken "\\<(http|ftp)://\\S+" :property *prop-string*)
+)
diff --git a/lisp/modules/progmodes/sgml.lsp b/lisp/modules/progmodes/sgml.lsp
new file mode 100644
index 0000000..511aae5
--- /dev/null
+++ b/lisp/modules/progmodes/sgml.lsp
@@ -0,0 +1,428 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/sgml.lsp,v 1.2 2002/10/06 17:11:48 paulo Exp $
+;;
+
+(require "syntax")
+(in-package "XEDIT")
+
+;; Default property the text is shown.
+(defsynprop *prop-sgml-default*
+ "default"
+ :font "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "Gray10"
+)
+
+(defsynprop *prop-sgml-default-short*
+ "default-short"
+ :font "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "Gray10"
+ :underline t
+)
+
+;; Large font.
+(defsynprop *prop-sgml-sect*
+ "sect"
+ :font "-*-helvetica-bold-r-*-*-17-*-*-*-*-*-*-1"
+ :foreground "Gray20"
+)
+
+;; Monospaced property.
+(defsynprop *prop-sgml-tt*
+ "tt"
+ :font "-*-courier-medium-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "Black"
+)
+
+;; Italic property.
+(defsynprop *prop-sgml-it*
+ "it"
+ :font "-*-helvetica-medium-o-*-*-12-*-*-*-*-*-*-1"
+ :foreground "Black"
+)
+
+;; Bold font property.
+(defsynprop *prop-sgml-bf*
+ "bf"
+ :font "-*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "Gray10"
+)
+
+;; Looks like a link...
+(defsynprop *prop-sgml-link*
+ "link"
+ :font "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "blue"
+ :underline t
+)
+
+;; Monospaced, also looks like a link...
+(defsynprop *prop-sgml-email*
+ "email"
+ :font "-*-courier-medium-r-*-*-12-*-*-*-*-*-*-1"
+ :foreground "blue"
+ :underline t
+)
+
+;; Another monospaced property,
+(defsynprop *prop-sgml-screen*
+ "screen"
+ :font "-*-fixed-*-*-*-*-*-*-*-*-*-*-*-1"
+ :foreground "Gray10"
+)
+
+(defsynprop *prop-sgml-maybe-entity*
+ "maybe-entity"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "VioletRed4"
+ :background "LightYellow"
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; The macros sgml-syntoken and sgml-syntable allows creating rules for
+;; matching text inside tags in the format:
+;; <tag> or <tag arg=value> or <tag arg1=value ... argn=value>
+;; any-text
+;; </tag>
+;; The generated rules don't allow things like: < tag> or </tag >
+;;
+;; This could also be done as a normal definition, with a starting rule like:
+;; "<(tag1|tag2|tag3)\\>"
+;; and an ending rule like:
+;; "</(tag1|tag2|tag3)>"
+;; But is implemented in way that will fail on purpose for things like:
+;; <tag1>any text</tag3></tag1>
+;;
+;; NOTE: These definitions aren't cheap in the time required to process the
+;; file, and are just adaptations/tests with the syntax-highlight code,
+;; probably it is better to avoid using it in other syntax definitions.
+;; NOTE2: It cannot be defined as a single macro because it is required to
+;; generate 2 entries in the main SGML syntax highlight definition,
+;; or, should generate the entire definition from a macro; you will
+;; need to type the tag name twice, but shouldn't be a problem if
+;; you are using sgml :-)
+;; XXX: Maybe the syntax-highlight code could save the starting match and
+;; apply a regex generated at run-time to check for the ending tag,
+;; but this probably would make the parser too slow, better to have
+;; a specialized parser if that is required...
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro sgml-syntoken (name)
+ `(syntoken (string-concat "<" ,name "\\>")
+ :icase t
+ :contained t
+ :begin (intern (string-concat ,name "$") 'keyword))
+)
+(defmacro sgml-syntable (name property)
+ `(let
+ (
+ (label (intern (string-concat ,name "$") 'keyword))
+ (nested-label (intern (string (gensym)) 'keyword))
+ )
+ (syntable label *prop-preprocessor* nil
+ ;; tag is still open, process any options
+ (synaugment :generic-tag)
+ (syntoken ">"
+ :nospec t
+ :property *prop-preprocessor*
+ :begin nested-label)
+ ;; Generate a nested table that includes everything, and only
+ ;; returns when the closing tag is found.
+ (syntable nested-label ,property nil
+ (syntoken (string-concat "</" ,name ">")
+ :icase t
+ :nospec t
+ :property *prop-preprocessor*
+ :switch -2)
+ (synaugment :main)
+ )
+ )
+ )
+)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Generate tokens for tags that don't require and ending tag.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro sgml-syntable-simple (name property)
+ `(let
+ (
+ (label (intern (string-concat ,name "$") 'keyword))
+ (nested-label (intern (string (gensym)) 'keyword))
+ )
+ (syntable label *prop-preprocessor* nil
+ ;; tag is still open, process any options
+ (synaugment :generic-tag)
+ (syntoken ">"
+ :nospec t
+ :property *prop-preprocessor*
+ :begin nested-label)
+ ;; Generate a nested table that finishes whenever an unmatched
+ ;; start or end tag is found.
+ (syntable nested-label ,property nil
+ (syntoken "</"
+ :icase t
+ :nospec t
+ :contained t
+ :begin :simple-nested-tag)
+ ;; These will take precedence over other rules
+ (syntoken "<"
+ :icase t
+ :nospec t
+ :contained t
+ :begin :simple-nested-tag)
+ (syntoken "<p>"
+ :icase t
+ :nospec t
+ :property *prop-preprocessor*
+ :switch :main)
+ (synaugment :main)
+ )
+ )
+ )
+)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Define some macros to generate tokens for tags in the format:
+;; <tag/ ... /
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro sgml-syntoken-short (name)
+ `(syntoken (string-concat "<" ,name "/")
+ :icase t
+ :property *prop-preprocessor*
+ :begin (intern (string-concat ,name "/") 'keyword))
+)
+(defmacro sgml-syntable-short (name property)
+ `(syntable (intern (string-concat ,name "/") 'keyword) ,property nil
+ (syntoken "/"
+ :nospec t
+ :property *prop-preprocessor*
+ :switch -1)
+ (syntoken "</?\\w+>"
+ :property *prop-control*
+ :switch :main)
+ )
+)
+
+
+;; The main SGML syntax table
+(defsyntax *sgml-mode* :main *prop-sgml-default* nil nil
+ ;; Comments
+ (syntoken "<!--"
+ :nospec t
+ :contained t
+ :begin :comment)
+ (syntable :comment *prop-comment* nil
+ ;; Only one rule, to finish the comment.
+ (syntoken "-->"
+ :nospec t
+ :switch -1)
+ )
+
+ ;; Entities
+ (syntoken "&[a-zA-Z0-9_.-]+;"
+ :property *prop-constant*)
+ ;; Probably an entity, missing ending `;'
+ (syntoken "&[a-zA-Z0-9_.-]+"
+ :property *prop-sgml-maybe-entity*)
+
+ ;; Strings
+ (syntable :string *prop-string* nil
+ ;; Ignore escaped characters.
+ (syntoken "\\\\.")
+ ;; Rule to finish the string.
+ (syntoken "\""
+ :nospec t
+ :switch -1)
+ )
+
+ ;; Links
+ (syntable :link *prop-preprocessor* nil
+ ;; No link string following "url="
+ (syntoken ">"
+ :nospec t
+ :property *prop-control*
+ :switch -1)
+ (syntoken "\""
+ :nospec t
+ :contained t
+ :begin :link-string)
+ (syntable :link-string *prop-sgml-link* nil
+ ;; Ignore escaped characters.
+ (syntoken "\\\\.")
+ ;; Rule to finish the link, note that returns two levels.
+ (syntoken "\""
+ :nospec t
+ :switch -2)
+ )
+ )
+
+ ;; "Special" tag
+ (syntoken "<!"
+ :nospec t
+ :contained t
+ :begin :special-tag)
+ ;; Rules for "special" tags
+ (syntable :special-tag *prop-preprocessor* nil
+ (syntoken "["
+ :nospec t
+ :property *prop-preprocessor*
+ :begin :brackets)
+ ;; Finish the "special" tag
+ (syntoken ">"
+ :nospec t
+ :switch -1)
+ (syntable :brackets *prop-sgml-default* nil
+ (syntoken "]"
+ :nospec t
+ :property *prop-preprocessor*
+ :switch -1)
+ ;; Allow nesting.
+ (syntoken "["
+ :nospec t
+ :property *prop-preprocessor*
+ :begin :brackets)
+ ;; Entities.
+ (syntoken "%[a-zA-Z0-9_.-]+;?"
+ :property *prop-annotation*)
+ ;; Allow everything inside the brackets
+ (synaugment :main)
+ )
+ ;; Don't use generic tag tokens, only create a rule for strings
+ (syntoken "\""
+ :nospec t
+ :begin :string
+ :contained t)
+ ;; Allow everything inside the "special" tag
+ (synaugment :main)
+ )
+
+ ;; Some "short" tags
+ (sgml-syntoken-short "tt")
+ (sgml-syntable-short "tt" *prop-sgml-tt*)
+ (sgml-syntoken-short "it")
+ (sgml-syntable-short "it" *prop-sgml-it*)
+ (sgml-syntoken-short "bf")
+ (sgml-syntable-short "bf" *prop-sgml-bf*)
+ (sgml-syntoken-short "em")
+ (sgml-syntable-short "em" *prop-sgml-bf*)
+
+ ;; Short tag
+ (syntoken "<\\w+/"
+ :property *prop-preprocessor*
+ :begin :short-tag)
+ (syntable :short-tag *prop-sgml-default-short* nil
+ (syntoken "/"
+ :nospec t
+ :property *prop-preprocessor*
+ :switch -1)
+ (syntoken "</?\\w+>"
+ :property *prop-control*
+ :switch -1)
+ )
+
+ ;; Don't allow spaces, this may and may not be the start of a tag,
+ ;; but the syntax-highlight definition is not specialized...
+ (syntoken "<([^/a-zA-Z]|$)"
+ :property *prop-control*)
+
+ ;; Some tags that require an end tag
+ (sgml-syntoken "tt")
+ (sgml-syntable "tt" *prop-sgml-tt*)
+ (sgml-syntoken "code")
+ (sgml-syntable "code" *prop-sgml-tt*)
+ (sgml-syntoken "tag")
+ (sgml-syntable "tag" *prop-sgml-tt*)
+ (sgml-syntoken "verb")
+ (sgml-syntable "verb" *prop-sgml-tt*)
+ (sgml-syntoken "programlisting")
+ (sgml-syntable "programlisting" *prop-sgml-tt*)
+ (sgml-syntoken "it")
+ (sgml-syntable "it" *prop-sgml-it*)
+ (sgml-syntoken "bf")
+ (sgml-syntable "bf" *prop-sgml-bf*)
+ (sgml-syntoken "em")
+ (sgml-syntable "em" *prop-sgml-bf*)
+ (sgml-syntoken "mail")
+ (sgml-syntable "mail" *prop-sgml-email*)
+ (sgml-syntoken "email")
+ (sgml-syntable "email" *prop-sgml-email*)
+ (sgml-syntoken "screen")
+ (sgml-syntable "screen" *prop-sgml-screen*)
+ (sgml-syntoken "tscreen")
+ (sgml-syntable "tscreen" *prop-sgml-screen*)
+
+
+ ;; Helper for tags that don't need an ending one.
+ ;; NOTE: Since the parser is not specialized, if the tag is
+ ;; folowed by one that has a special property defined here,
+ ;; it may not be detected, i.e. put a <p> after the <sect>
+ ;; and it will work.
+ (syntable :simple-nested-tag *prop-preprocessor* nil
+ ;; tag is still open, process any options
+ (synaugment :generic-tag)
+ (syntoken ">"
+ :nospec t
+ :property *prop-preprocessor*
+ :switch -3)
+ )
+ (sgml-syntoken "sect")
+ (sgml-syntable-simple "sect" *prop-sgml-sect*)
+ (sgml-syntoken "sect1")
+ (sgml-syntable-simple "sect1" *prop-sgml-sect*)
+ (sgml-syntoken "sect2")
+ (sgml-syntable-simple "sect2" *prop-sgml-sect*)
+
+ ;; Generic tags
+ (syntoken "<"
+ :nospec t
+ :contained t
+ :begin :tag)
+ ;; Table :generic-tag is defined to be augmented, no rule to finish it.
+ (syntable :generic-tag *prop-preprocessor* nil
+ ;; Start string
+ (syntoken "\""
+ :nospec t
+ :begin :string
+ :contained t)
+ ;; Start url link
+ (syntoken "url="
+ :nospec t
+ :begin :link)
+ ;; Cannot nest
+ (syntoken "<"
+ :nospec t
+ :property *prop-control*)
+ )
+ (syntable :tag *prop-preprocessor* nil
+ ;; Finish the tag
+ (syntoken ">"
+ :nospec t
+ :switch -1)
+ ;; Import generic definitions
+ (synaugment :generic-tag)
+ )
+)
diff --git a/lisp/modules/progmodes/sh.lsp b/lisp/modules/progmodes/sh.lsp
new file mode 100644
index 0000000..79679ed
--- /dev/null
+++ b/lisp/modules/progmodes/sh.lsp
@@ -0,0 +1,113 @@
+;;
+;; Copyright (c) 2003 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/sh.lsp,v 1.1 2003/01/16 03:50:46 paulo Exp $
+;;
+
+(require "syntax")
+(require "indent")
+(in-package "XEDIT")
+
+(defsynprop *prop-escape*
+ "escape"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "Red3")
+
+(defsynprop *prop-variable*
+ "variable"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "Gold4")
+
+(defsynprop *prop-backslash*
+ "backslash"
+ :font "*courier-bold-r*12*"
+ :foreground "green4")
+
+;; XXX it would be interesting if "here-documents" could be parsed
+;; just searching for "<<\s*EOF\\>" and then for "^EOF\\>" should
+;; handle most cases, but would be a hack...
+(defsyntax *sh-mode* :main nil #'default-indent nil
+ ;; keywords and common commands/builtins
+ (syntoken "\\<(if|then|elif|else|fi|case|in|esac|for|do|done|while|until|break|continue|eval|exit|exec|test|echo|cd|shift|local|return)\\>"
+ :property *prop-keyword*)
+
+ ; comments
+ (syntoken "#.*$" :property *prop-comment*)
+
+ ;; punctuation
+ (syntoken "[][;:*?(){}<>&!|$#]+" :property *prop-punctuation*)
+
+ ;; variable declaration
+ (syntoken "\\w+=" :property *prop-preprocessor*)
+
+ ;; numbers
+ (syntoken "\\<\\d+\\>" :property *prop-number*)
+
+ ;; escaped characters at toplevel
+ (syntoken "\\\\." :property *prop-backslash*)
+
+ ;; single quote
+ (syntoken "'" :nospec t :contained t :begin :single)
+ (syntable :single *prop-constant* nil
+ ;; do nothing, escaped characters
+ (syntoken "\\\\.")
+ (syntoken "'" :nospec t :switch -1)
+ )
+
+ ;; double quote
+ (syntoken "\"" :nospec t :contained t :begin :double)
+ (syntable :double *prop-string* #'default-indent
+ ;; escaped characters
+ (syntoken "\\\\." :property *prop-backslash*)
+ (syntoken "\"" :nospec t :switch -1)
+ ;; rule to start escape
+ (syntoken "`" :nospec t :contained t :begin :escape)
+ ;; ignore single quote, required because escape is augmented
+ (syntoken "'" :nospec t)
+ (synaugment :escape :variable)
+ )
+
+ ;; escaped commands
+ (syntoken "`" :nospec t :contained t :begin :escape)
+ (syntable :escape *prop-escape* #'default-indent
+ ;; escaped characters
+ (syntoken "\\\\." :property *prop-backslash*)
+ (syntoken "`" :nospec t :switch -1)
+ ;; rule to start double quote inside escape
+ (syntoken "\"" :nospec t :contained t :begin :double)
+ ;; rule to start single quote
+ (syntoken "'" :nospec t :contained t :begin :single)
+ (synaugment :double :variable)
+ )
+
+ (syntable :variable nil nil
+ (syntoken "\\$\\w+" :property *prop-variable*)
+ )
+ (synaugment :variable)
+)
diff --git a/lisp/modules/progmodes/xconf.lsp b/lisp/modules/progmodes/xconf.lsp
new file mode 100644
index 0000000..dea70a2
--- /dev/null
+++ b/lisp/modules/progmodes/xconf.lsp
@@ -0,0 +1,68 @@
+;;
+;; Copyright (c) 2003 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/xconf.lsp,v 1.1 2003/01/16 03:50:46 paulo Exp $
+;;
+
+(require "syntax")
+(require "indent")
+(in-package "XEDIT")
+
+(defsyntax *xconf-mode* :main nil #'default-indent nil
+ ;; section start
+ (syntoken "\\<(Section|SubSection)\\>"
+ :property *prop-keyword* :icase t :begin :section)
+ ;; just for fun, highlight the section name differently
+ (syntable :section *prop-constant* #'default-indent
+ (syntoken "\"" :nospec t :begin :name)
+ (syntable :name *prop-constant* nil
+ ;; ignore escaped characters
+ (syntoken "\\\\.")
+ (syntoken "\"" :nospec t :switch -2)
+ )
+ )
+
+ ;; section end
+ (syntoken "\\<(EndSection|EndSubSection)\\>"
+ :property *prop-keyword* :icase t)
+
+ ;; numeric options
+ (syntoken "\\<\\d+(\\.\\d+)?\\>" :property *prop-number*)
+
+ ;; comments
+ (syntoken "#.*$" :property *prop-comment*)
+
+ ;; strings
+ (syntoken "\"" :nospec t :begin :string :contained t)
+ (syntable :string *prop-string* #'default-indent
+ ;; ignore escaped characters
+ (syntoken "\\\\.")
+ (syntoken "\"" :nospec t :switch -1)
+ )
+)
diff --git a/lisp/modules/progmodes/xlog.lsp b/lisp/modules/progmodes/xlog.lsp
new file mode 100644
index 0000000..6bc8b57
--- /dev/null
+++ b/lisp/modules/progmodes/xlog.lsp
@@ -0,0 +1,102 @@
+;;
+;; Copyright (c) 2003 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/xlog.lsp,v 1.1 2003/01/16 06:25:51 paulo Exp $
+;;
+
+(require "syntax")
+(in-package "XEDIT")
+
+(defsynprop *prop-xlog-probe*
+ "xlog-probe"
+ :font "*courier-medium-r*12*"
+ :background "rgb:c/f/c")
+
+(defsynprop *prop-xlog-config*
+ "xlog-config"
+ :font "*courier-medium-r*12*"
+ :background "rgb:c/e/f")
+
+(defsynprop *prop-xlog-default*
+ "xlog-default"
+ :font "*courier-medium-r*12*"
+ :background "rgb:e/c/f")
+
+(defsynprop *prop-xlog-warning*
+ "xlog-warning"
+ :font "*courier-bold-r*12*"
+ :foreground "Red4"
+ :background "Yellow1"
+)
+
+(defsynprop *prop-xlog-error*
+ "xlog-error"
+ :font "*courier-bold-r*12*"
+ :foreground "Yellow2"
+ :background "Red3"
+)
+
+(defsyntax *xlog-mode* :main nil nil nil
+ ;; highlight version
+ (syntoken "^XFree86 Version \\S+" :property *prop-annotation*)
+
+ ;; release date
+ (syntoken "^Release Date: " :property *prop-keyword* :begin :note)
+
+ ;; highlight operating system description
+ (syntoken "^Build Operating System: " :property *prop-keyword* :begin :note)
+
+ (syntable :note *prop-annotation* nil (syntoken "$" :switch -1))
+
+ ;; don't highlight info lines
+ (syntoken "^\\(II\\) " :property *prop-keyword*)
+
+ ;; default lines
+ (syntoken "^\\(==\\) " :property *prop-keyword* :begin :default)
+ (syntable :default *prop-xlog-default* nil (syntoken "$" :switch -1))
+
+ ;; probe lines
+ (syntoken "^\\(--\\) " :property *prop-keyword* :begin :probe)
+ (syntable :probe *prop-xlog-probe* nil (syntoken "$" :switch -1))
+
+ ;; config lines
+ (syntoken "^\\(\\*\\*\\) " :property *prop-keyword* :begin :config)
+ (syntable :config *prop-xlog-config* nil (syntoken "$" :switch -1))
+
+ ;; warnings
+ (syntoken "^\\(WW\\) " :property *prop-keyword* :begin :warning)
+ (syntable :warning *prop-xlog-warning* nil (syntoken "$" :switch -1))
+
+ ;; errors
+ (syntoken "^\\(EE\\) " :property *prop-keyword* :begin :error)
+ (syntable :error *prop-xlog-error* nil (syntoken "$" :switch -1))
+
+ ;; command line and "uncommon" messages
+ (syntoken "^\\(..\\) " :property *prop-control* :begin :warning)
+)
diff --git a/lisp/modules/progmodes/xrdb.lsp b/lisp/modules/progmodes/xrdb.lsp
new file mode 100644
index 0000000..c0a099c
--- /dev/null
+++ b/lisp/modules/progmodes/xrdb.lsp
@@ -0,0 +1,115 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/progmodes/xrdb.lsp,v 1.2 2002/10/06 17:11:48 paulo Exp $
+;;
+
+(require "syntax")
+(in-package "XEDIT")
+
+(defsynprop *prop-xrdb-comment*
+ "xrdb-comment"
+ :font "*courier-medium-o*12*"
+ :foreground "sienna"
+)
+
+(defsynprop *prop-xrdb-special*
+ "format"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "RoyalBlue4"
+)
+
+(defsynprop *prop-xrdb-punctuation*
+ "punctuation"
+ :font "-*-courier-bold-r-*-*-14-*-*-*-*-*-*-1"
+ :foreground "OrangeRed4"
+)
+
+(defsyntax *xrdb-mode* :main nil nil nil
+ (syntoken "^\\s*!.*"
+ :property *prop-xrdb-comment*)
+ (syntoken "^\\s*#.*"
+ :property *prop-preprocessor*)
+ (syntoken "\\*|\\.|\\?"
+ :property *prop-xrdb-punctuation*
+ :begin :resource)
+ (syntoken "."
+ :nospec t
+ :begin :resource)
+
+ ;; Extra comments
+ (syntoken "/*" :nospec t :begin :comment :contained t)
+ (syntable :comment *prop-comment* nil
+ (syntoken "/*" :nospec t :property *prop-error*)
+ ;; Rule to finish a comment.
+ (syntoken "*/" :nospec t :switch -1)
+ )
+
+ (syntable :resource nil nil
+ (syntoken "\\*|\\.|\\?" :property *prop-xrdb-punctuation*)
+ (syntoken ":\\s*" :property *prop-xrdb-punctuation* :begin :value)
+ )
+
+ (syntable :value *prop-string* nil
+ (syntoken "\\\\$" :property *prop-constant*)
+
+
+ ;; If the pattern ends at a newline, must switch to the previous state.
+ ;; Not sure yet how to better handle this. The parser does not detect
+ ;; eol because it is a match to the empty string. A possible hack
+ ;; would be to check if the pattern string ends in a "$", but probably
+ ;; better in this case to have a syntoken option, to tell the parser
+ ;; an eol may exist.
+ (syntoken
+ (string-concat
+ "("
+ "\\d+|" ;; numbers
+ "(#\\x+|rgb:\\x+/\\x+/\\x+)|" ;; color spec
+ "#\\w+" ;; translation table
+ ")$")
+ :property *prop-xrdb-special* :switch -2)
+ (syntoken "(\\\\n?|\")$"
+ :property *prop-constant* :switch -2)
+
+ ;; XXX Cut&paste of the above, only without the match to eol
+ (syntoken
+ (string-concat
+ "("
+ "\\d+|"
+ "(#\\x+|rgb:\\x+/\\x+/\\x+)|"
+ "#\\w+"
+ ")")
+ :property *prop-xrdb-special*)
+ (syntoken "(\\\\n?|\")"
+ :property *prop-constant*)
+
+ (syntoken "/*" :nospec t :begin :comment :contained t)
+ (syntoken ".?$" :switch -2)
+ )
+)
diff --git a/lisp/modules/psql.c b/lisp/modules/psql.c
new file mode 100644
index 0000000..6945947
--- /dev/null
+++ b/lisp/modules/psql.c
@@ -0,0 +1,983 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/modules/psql.c,v 1.12 2002/11/23 08:26:52 paulo Exp $ */
+
+#include <stdlib.h>
+#include <libpq-fe.h>
+#undef USE_SSL /* cannot get it to compile... */
+#include <postgres.h>
+#include <utils/geo_decls.h>
+#include "internal.h"
+#include "private.h"
+
+/*
+ * Prototypes
+ */
+int psqlLoadModule(void);
+
+LispObj *Lisp_PQbackendPID(LispBuiltin*);
+LispObj *Lisp_PQclear(LispBuiltin*);
+LispObj *Lisp_PQconsumeInput(LispBuiltin*);
+LispObj *Lisp_PQdb(LispBuiltin*);
+LispObj *Lisp_PQerrorMessage(LispBuiltin*);
+LispObj *Lisp_PQexec(LispBuiltin*);
+LispObj *Lisp_PQfinish(LispBuiltin*);
+LispObj *Lisp_PQfname(LispBuiltin*);
+LispObj *Lisp_PQfnumber(LispBuiltin*);
+LispObj *Lisp_PQfsize(LispBuiltin*);
+LispObj *Lisp_PQftype(LispBuiltin*);
+LispObj *Lisp_PQgetlength(LispBuiltin*);
+LispObj *Lisp_PQgetvalue(LispBuiltin*);
+LispObj *Lisp_PQhost(LispBuiltin*);
+LispObj *Lisp_PQnfields(LispBuiltin*);
+LispObj *Lisp_PQnotifies(LispBuiltin*);
+LispObj *Lisp_PQntuples(LispBuiltin*);
+LispObj *Lisp_PQoptions(LispBuiltin*);
+LispObj *Lisp_PQpass(LispBuiltin*);
+LispObj *Lisp_PQport(LispBuiltin*);
+LispObj *Lisp_PQresultStatus(LispBuiltin*);
+LispObj *Lisp_PQsetdb(LispBuiltin*);
+LispObj *Lisp_PQsetdbLogin(LispBuiltin*);
+LispObj *Lisp_PQsocket(LispBuiltin*);
+LispObj *Lisp_PQstatus(LispBuiltin*);
+LispObj *Lisp_PQtty(LispBuiltin*);
+LispObj *Lisp_PQuser(LispBuiltin*);
+
+/*
+ * Initialization
+ */
+static LispBuiltin lispbuiltins[] = {
+ {LispFunction, Lisp_PQbackendPID, "pq-backend-pid connection"},
+ {LispFunction, Lisp_PQclear, "pq-clear result"},
+ {LispFunction, Lisp_PQconsumeInput, "pq-consume-input connection"},
+ {LispFunction, Lisp_PQdb, "pq-db connection"},
+ {LispFunction, Lisp_PQerrorMessage, "pq-error-message connection"},
+ {LispFunction, Lisp_PQexec, "pq-exec connection query"},
+ {LispFunction, Lisp_PQfinish, "pq-finish connection"},
+ {LispFunction, Lisp_PQfname, "pq-fname result field-number"},
+ {LispFunction, Lisp_PQfnumber, "pq-fnumber result field-name"},
+ {LispFunction, Lisp_PQfsize, "pq-fsize result field-number"},
+ {LispFunction, Lisp_PQftype, "pq-ftype result field-number"},
+ {LispFunction, Lisp_PQgetlength, "pq-getlength result tupple field-number"},
+ {LispFunction, Lisp_PQgetvalue, "pq-getvalue result tupple field-number &optional type"},
+ {LispFunction, Lisp_PQhost, "pq-host connection"},
+ {LispFunction, Lisp_PQnfields, "pq-nfields result"},
+ {LispFunction, Lisp_PQnotifies, "pq-notifies connection"},
+ {LispFunction, Lisp_PQntuples, "pq-ntuples result"},
+ {LispFunction, Lisp_PQoptions, "pq-options connection"},
+ {LispFunction, Lisp_PQpass, "pq-pass connection"},
+ {LispFunction, Lisp_PQport, "pq-port connection"},
+ {LispFunction, Lisp_PQresultStatus, "pq-result-status result"},
+ {LispFunction, Lisp_PQsetdb, "pq-setdb host port options tty dbname"},
+ {LispFunction, Lisp_PQsetdbLogin, "pq-setdb-login host port options tty dbname login password"},
+ {LispFunction, Lisp_PQsocket, "pq-socket connection"},
+ {LispFunction, Lisp_PQstatus, "pq-status connection"},
+ {LispFunction, Lisp_PQtty, "pq-tty connection"},
+ {LispFunction, Lisp_PQuser, "pq-user connection"},
+};
+
+LispModuleData psqlLispModuleData = {
+ LISP_MODULE_VERSION,
+ psqlLoadModule
+};
+
+static int PGconn_t, PGresult_t;
+
+/*
+ * Implementation
+ */
+int
+psqlLoadModule(void)
+{
+ int i;
+ char *fname = "PSQL-LOAD-MODULE";
+
+ PGconn_t = LispRegisterOpaqueType("PGconn*");
+ PGresult_t = LispRegisterOpaqueType("PGresult*");
+
+ GCDisable();
+ /* NOTE: Implemented just enough to make programming examples
+ * (and my needs) work.
+ * Completing this is an exercise to the reader, or may be implemented
+ * when/if required.
+ */
+ LispExecute("(DEFSTRUCT PG-NOTIFY RELNAME BE-PID)\n"
+ "(DEFSTRUCT PG-POINT X Y)\n"
+ "(DEFSTRUCT PG-BOX HIGH LOW)\n"
+ "(DEFSTRUCT PG-POLYGON SIZE NUM-POINTS BOUNDBOX POINTS)\n");
+
+ /* enum ConnStatusType */
+ (void)LispSetVariable(ATOM2("PG-CONNECTION-OK"),
+ REAL(CONNECTION_OK), fname, 0);
+ (void)LispSetVariable(ATOM2("PG-CONNECTION-BAD"),
+ REAL(CONNECTION_BAD), fname, 0);
+ (void)LispSetVariable(ATOM2("PG-CONNECTION-STARTED"),
+ REAL(CONNECTION_STARTED), fname, 0);
+ (void)LispSetVariable(ATOM2("PG-CONNECTION-MADE"),
+ REAL(CONNECTION_MADE), fname, 0);
+ (void)LispSetVariable(ATOM2("PG-CONNECTION-AWAITING-RESPONSE"),
+ REAL(CONNECTION_AWAITING_RESPONSE), fname, 0);
+ (void)LispSetVariable(ATOM2("PG-CONNECTION-AUTH-OK"),
+ REAL(CONNECTION_AUTH_OK), fname, 0);
+ (void)LispSetVariable(ATOM2("PG-CONNECTION-SETENV"),
+ REAL(CONNECTION_SETENV), fname, 0);
+
+
+ /* enum ExecStatusType */
+ (void)LispSetVariable(ATOM2("PGRES-EMPTY-QUERY"),
+ REAL(PGRES_EMPTY_QUERY), fname, 0);
+ (void)LispSetVariable(ATOM2("PGRES-COMMAND-OK"),
+ REAL(PGRES_COMMAND_OK), fname, 0);
+ (void)LispSetVariable(ATOM2("PGRES-TUPLES-OK"),
+ REAL(PGRES_TUPLES_OK), fname, 0);
+ (void)LispSetVariable(ATOM2("PGRES-COPY-OUT"),
+ REAL(PGRES_COPY_OUT), fname, 0);
+ (void)LispSetVariable(ATOM2("PGRES-COPY-IN"),
+ REAL(PGRES_COPY_IN), fname, 0);
+ (void)LispSetVariable(ATOM2("PGRES-BAD-RESPONSE"),
+ REAL(PGRES_BAD_RESPONSE), fname, 0);
+ (void)LispSetVariable(ATOM2("PGRES-NONFATAL-ERROR"),
+ REAL(PGRES_NONFATAL_ERROR), fname, 0);
+ (void)LispSetVariable(ATOM2("PGRES-FATAL-ERROR"),
+ REAL(PGRES_FATAL_ERROR), fname, 0);
+ GCEnable();
+
+ for (i = 0; i < sizeof(lispbuiltins) / sizeof(lispbuiltins[0]); i++)
+ LispAddBuiltinFunction(&lispbuiltins[i]);
+
+ return (1);
+}
+
+LispObj *
+Lisp_PQbackendPID(LispBuiltin *builtin)
+/*
+ pq-backend-pid connection
+ */
+{
+ int pid;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ pid = PQbackendPID(conn);
+
+ return (INTEGER(pid));
+}
+
+LispObj *
+Lisp_PQclear(LispBuiltin *builtin)
+/*
+ pq-clear result
+ */
+{
+ PGresult *res;
+
+ LispObj *result;
+
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ PQclear(res);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_PQconsumeInput(LispBuiltin *builtin)
+/*
+ pq-consume-input connection
+ */
+{
+ int result;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ result = PQconsumeInput(conn);
+
+ return (INTEGER(result));
+}
+
+LispObj *
+Lisp_PQdb(LispBuiltin *builtin)
+/*
+ pq-db connection
+ */
+{
+ char *string;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ string = PQdb(conn);
+
+ return (string ? STRING(string) : NIL);
+}
+
+LispObj *
+Lisp_PQerrorMessage(LispBuiltin *builtin)
+{
+ char *string;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ string = PQerrorMessage(conn);
+
+ return (string ? STRING(string) : NIL);
+}
+
+LispObj *
+Lisp_PQexec(LispBuiltin *builtin)
+/*
+ pq-exec connection query
+ */
+{
+ PGconn *conn;
+ PGresult *res;
+
+ LispObj *connection, *query;
+
+ query = ARGUMENT(1);
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ CHECK_STRING(query);
+ res = PQexec(conn, THESTR(query));
+
+ return (res ? OPAQUE(res, PGresult_t) : NIL);
+}
+
+LispObj *
+Lisp_PQfinish(LispBuiltin *builtin)
+/*
+ pq-finish connection
+ */
+{
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ PQfinish(conn);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_PQfname(LispBuiltin *builtin)
+/*
+ pq-fname result field-number
+ */
+{
+ char *string;
+ int field;
+ PGresult *res;
+
+ LispObj *result, *field_number;
+
+ field_number = ARGUMENT(1);
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ CHECK_INDEX(field_number);
+ field = FIXNUM_VALUE(field_number);
+
+ string = PQfname(res, field);
+
+ return (string ? STRING(string) : NIL);
+}
+
+LispObj *
+Lisp_PQfnumber(LispBuiltin *builtin)
+/*
+ pq-fnumber result field-name
+ */
+{
+ int number;
+ int field;
+ PGresult *res;
+
+ LispObj *result, *field_name;
+
+ field_name = ARGUMENT(1);
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ CHECK_STRING(field_name);
+ number = PQfnumber(res, THESTR(field_name));
+
+ return (INTEGER(number));
+}
+
+LispObj *
+Lisp_PQfsize(LispBuiltin *builtin)
+/*
+ pq-fsize result field-number
+ */
+{
+ int size, field;
+ PGresult *res;
+
+ LispObj *result, *field_number;
+
+ field_number = ARGUMENT(1);
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ CHECK_INDEX(field_number);
+ field = FIXNUM_VALUE(field_number);
+
+ size = PQfsize(res, field);
+
+ return (INTEGER(size));
+}
+
+LispObj *
+Lisp_PQftype(LispBuiltin *builtin)
+{
+ Oid oid;
+ int field;
+ PGresult *res;
+
+ LispObj *result, *field_number;
+
+ field_number = ARGUMENT(1);
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ CHECK_INDEX(field_number);
+ field = FIXNUM_VALUE(field_number);
+
+ oid = PQftype(res, field);
+
+ return (INTEGER(oid));
+}
+
+LispObj *
+Lisp_PQgetlength(LispBuiltin *builtin)
+/*
+ pq-getlength result tupple field-number
+ */
+{
+ PGresult *res;
+ int tuple, field, length;
+
+ LispObj *result, *otupple, *field_number;
+
+ field_number = ARGUMENT(2);
+ otupple = ARGUMENT(1);
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ CHECK_INDEX(otupple);
+ tuple = FIXNUM_VALUE(otupple);
+
+ CHECK_INDEX(field_number);
+ field = FIXNUM_VALUE(field_number);
+
+ length = PQgetlength(res, tuple, field);
+
+ return (INTEGER(length));
+}
+
+LispObj *
+Lisp_PQgetvalue(LispBuiltin *builtin)
+/*
+ pq-getvalue result tuple field &optional type-specifier
+ */
+{
+ char *string;
+ double real = 0.0;
+ PGresult *res;
+ int tuple, field, isint = 0, isreal = 0, integer;
+
+ LispObj *result, *otupple, *field_number, *type;
+
+ type = ARGUMENT(3);
+ field_number = ARGUMENT(2);
+ otupple = ARGUMENT(1);
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ CHECK_INDEX(otupple);
+ tuple = FIXNUM_VALUE(otupple);
+
+ CHECK_INDEX(field_number);
+ field = FIXNUM_VALUE(field_number);
+
+ string = PQgetvalue(res, tuple, field);
+
+ if (type != UNSPEC) {
+ char *typestring;
+
+ CHECK_SYMBOL(type);
+ typestring = ATOMID(type);
+
+ if (strcmp(typestring, "INT16") == 0) {
+ integer = *(short*)string;
+ isint = 1;
+ goto simple_type;
+ }
+ else if (strcmp(typestring, "INT32") == 0) {
+ integer = *(int*)string;
+ isint = 1;
+ goto simple_type;
+ }
+ else if (strcmp(typestring, "FLOAT") == 0) {
+ real = *(float*)string;
+ isreal = 1;
+ goto simple_type;
+ }
+ else if (strcmp(typestring, "REAL") == 0) {
+ real = *(double*)string;
+ isreal = 1;
+ goto simple_type;
+ }
+ else if (strcmp(typestring, "PG-POLYGON") == 0)
+ goto polygon_type;
+ else if (strcmp(typestring, "STRING") != 0)
+ LispDestroy("%s: unknown type %s",
+ STRFUN(builtin), typestring);
+ }
+
+simple_type:
+ return (isint ? INTEGER(integer) : isreal ? DFLOAT(real) :
+ (string ? STRING(string) : NIL));
+
+polygon_type:
+ {
+ LispObj *poly, *box, *p = NIL, *cdr, *obj;
+ POLYGON *polygon;
+ int i, size;
+
+ size = PQgetlength(res, tuple, field);
+ polygon = (POLYGON*)(string - sizeof(int));
+
+ GCDisable();
+ /* get polygon->boundbox */
+ cdr = EVAL(CONS(ATOM("MAKE-PG-POINT"),
+ CONS(KEYWORD("X"),
+ CONS(REAL(polygon->boundbox.high.x),
+ CONS(KEYWORD("Y"),
+ CONS(REAL(polygon->boundbox.high.y), NIL))))));
+ obj = EVAL(CONS(ATOM("MAKE-PG-POINT"),
+ CONS(KEYWORD("X"),
+ CONS(REAL(polygon->boundbox.low.x),
+ CONS(KEYWORD("Y"),
+ CONS(REAL(polygon->boundbox.low.y), NIL))))));
+ box = EVAL(CONS(ATOM("MAKE-PG-BOX"),
+ CONS(KEYWORD("HIGH"),
+ CONS(cdr,
+ CONS(KEYWORD("LOW"),
+ CONS(obj, NIL))))));
+ /* get polygon->p values */
+ for (i = 0; i < polygon->npts; i++) {
+ obj = EVAL(CONS(ATOM("MAKE-PG-POINT"),
+ CONS(KEYWORD("X"),
+ CONS(REAL(polygon->p[i].x),
+ CONS(KEYWORD("Y"),
+ CONS(REAL(polygon->p[i].y), NIL))))));
+ if (i == 0)
+ p = cdr = CONS(obj, NIL);
+ else {
+ RPLACD(cdr, CONS(obj, NIL));
+ cdr = CDR(cdr);
+ }
+ }
+
+ /* make result */
+ poly = EVAL(CONS(ATOM("MAKE-PG-POLYGON"),
+ CONS(KEYWORD("SIZE"),
+ CONS(REAL(size),
+ CONS(KEYWORD("NUM-POINTS"),
+ CONS(REAL(polygon->npts),
+ CONS(KEYWORD("BOUNDBOX"),
+ CONS(box,
+ CONS(KEYWORD("POINTS"),
+ CONS(QUOTE(p), NIL))))))))));
+ GCEnable();
+
+ return (poly);
+ }
+}
+
+LispObj *
+Lisp_PQhost(LispBuiltin *builtin)
+/*
+ pq-host connection
+ */
+{
+ char *string;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ string = PQhost(conn);
+
+ return (string ? STRING(string) : NIL);
+}
+
+LispObj *
+Lisp_PQnfields(LispBuiltin *builtin)
+/*
+ pq-nfields result
+ */
+{
+ int nfields;
+ PGresult *res;
+
+ LispObj *result;
+
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ nfields = PQnfields(res);
+
+ return (INTEGER(nfields));
+}
+
+LispObj *
+Lisp_PQnotifies(LispBuiltin *builtin)
+/*
+ pq-notifies connection
+ */
+{
+ LispObj *result, *code, *cod = COD;
+ PGconn *conn;
+ PGnotify *notifies;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ if ((notifies = PQnotifies(conn)) == NULL)
+ return (NIL);
+
+ GCDisable();
+ code = CONS(ATOM("MAKE-PG-NOTIFY"),
+ CONS(KEYWORD("RELNAME"),
+ CONS(STRING(notifies->relname),
+ CONS(KEYWORD("BE-PID"),
+ CONS(REAL(notifies->be_pid), NIL)))));
+ COD = CONS(code, COD);
+ GCEnable();
+ result = EVAL(code);
+ COD = cod;
+
+ free(notifies);
+
+ return (result);
+}
+
+LispObj *
+Lisp_PQntuples(LispBuiltin *builtin)
+/*
+ pq-ntuples result
+ */
+{
+ int ntuples;
+ PGresult *res;
+
+ LispObj *result;
+
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ ntuples = PQntuples(res);
+
+ return (INTEGER(ntuples));
+}
+
+LispObj *
+Lisp_PQoptions(LispBuiltin *builtin)
+/*
+ pq-options connection
+ */
+{
+ char *string;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ string = PQoptions(conn);
+
+ return (string ? STRING(string) : NIL);
+}
+
+LispObj *
+Lisp_PQpass(LispBuiltin *builtin)
+/*
+ pq-pass connection
+ */
+{
+ char *string;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ string = PQpass(conn);
+
+ return (string ? STRING(string) : NIL);
+}
+
+LispObj *
+Lisp_PQport(LispBuiltin *builtin)
+/*
+ pq-port connection
+ */
+{
+ char *string;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ string = PQport(conn);
+
+ return (string ? STRING(string) : NIL);
+}
+
+LispObj *
+Lisp_PQresultStatus(LispBuiltin *builtin)
+/*
+ pq-result-status result
+ */
+{
+ int status;
+ PGresult *res;
+
+ LispObj *result;
+
+ result = ARGUMENT(0);
+
+ if (!CHECKO(result, PGresult_t))
+ LispDestroy("%s: cannot convert %s to PGresult*",
+ STRFUN(builtin), STROBJ(result));
+ res = (PGresult*)(result->data.opaque.data);
+
+ status = PQresultStatus(res);
+
+ return (INTEGER(status));
+}
+
+LispObj *
+LispPQsetdb(LispBuiltin *builtin, int loginp)
+/*
+ pq-setdb host port options tty dbname
+ pq-setdb-login host port options tty dbname login password
+ */
+{
+ PGconn *conn;
+ char *host, *port, *options, *tty, *dbname, *login, *password;
+
+ LispObj *ohost, *oport, *ooptions, *otty, *odbname, *ologin, *opassword;
+
+ if (loginp) {
+ opassword = ARGUMENT(6);
+ ologin = ARGUMENT(5);
+ }
+ else
+ opassword = ologin = NIL;
+ odbname = ARGUMENT(4);
+ otty = ARGUMENT(3);
+ ooptions = ARGUMENT(2);
+ oport = ARGUMENT(1);
+ ohost = ARGUMENT(0);
+
+ if (ohost != NIL) {
+ CHECK_STRING(ohost);
+ host = THESTR(ohost);
+ }
+ else
+ host = NULL;
+
+ if (oport != NIL) {
+ CHECK_STRING(oport);
+ port = THESTR(oport);
+ }
+ else
+ port = NULL;
+
+ if (ooptions != NIL) {
+ CHECK_STRING(ooptions);
+ options = THESTR(ooptions);
+ }
+ else
+ options = NULL;
+
+ if (otty != NIL) {
+ CHECK_STRING(otty);
+ tty = THESTR(otty);
+ }
+ else
+ tty = NULL;
+
+ if (odbname != NIL) {
+ CHECK_STRING(odbname);
+ dbname = THESTR(odbname);
+ }
+ else
+ dbname = NULL;
+
+ if (ologin != NIL) {
+ CHECK_STRING(ologin);
+ login = THESTR(ologin);
+ }
+ else
+ login = NULL;
+
+ if (opassword != NIL) {
+ CHECK_STRING(opassword);
+ password = THESTR(opassword);
+ }
+ else
+ password = NULL;
+
+ conn = PQsetdbLogin(host, port, options, tty, dbname, login, password);
+
+ return (conn ? OPAQUE(conn, PGconn_t) : NIL);
+}
+
+LispObj *
+Lisp_PQsetdb(LispBuiltin *builtin)
+/*
+ pq-setdb host port options tty dbname
+ */
+{
+ return (LispPQsetdb(builtin, 0));
+}
+
+LispObj *
+Lisp_PQsetdbLogin(LispBuiltin *builtin)
+/*
+ pq-setdb-login host port options tty dbname login password
+ */
+{
+ return (LispPQsetdb(builtin, 1));
+}
+
+LispObj *
+Lisp_PQsocket(LispBuiltin *builtin)
+/*
+ pq-socket connection
+ */
+{
+ int sock;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ sock = PQsocket(conn);
+
+ return (INTEGER(sock));
+}
+
+LispObj *
+Lisp_PQstatus(LispBuiltin *builtin)
+/*
+ pq-status connection
+ */
+{
+ int status;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ status = PQstatus(conn);
+
+ return (INTEGER(status));
+}
+
+LispObj *
+Lisp_PQtty(LispBuiltin *builtin)
+/*
+ pq-tty connection
+ */
+{
+ char *string;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ string = PQtty(conn);
+
+ return (string ? STRING(string) : NIL);
+}
+
+LispObj *
+Lisp_PQuser(LispBuiltin *builtin)
+/*
+ pq-user connection
+ */
+{
+ char *string;
+ PGconn *conn;
+
+ LispObj *connection;
+
+ connection = ARGUMENT(0);
+
+ if (!CHECKO(connection, PGconn_t))
+ LispDestroy("%s: cannot convert %s to PGconn*",
+ STRFUN(builtin), STROBJ(connection));
+ conn = (PGconn*)(connection->data.opaque.data);
+
+ string = PQuser(conn);
+
+ return (string ? STRING(string) : NIL);
+}
diff --git a/lisp/modules/syntax.lsp b/lisp/modules/syntax.lsp
new file mode 100644
index 0000000..c297235
--- /dev/null
+++ b/lisp/modules/syntax.lsp
@@ -0,0 +1,1452 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/syntax.lsp,v 1.11 2003/01/16 03:50:46 paulo Exp $
+;;
+
+(provide "syntax")
+(require "xedit")
+(in-package "XEDIT")
+
+(defvar *syntax-symbols* '(
+ syntax-highlight defsyntax defsynprop synprop-p syntax-p
+ syntable syntoken synaugment
+ *prop-default* *prop-keyword* *prop-number* *prop-string*
+ *prop-constant* *prop-comment* *prop-preprocessor*
+ *prop-punctuation* *prop-error* *prop-annotation*
+))
+(export *syntax-symbols*)
+(in-package "USER")
+(dolist (symbol xedit::*syntax-symbols*)
+ (import symbol)
+)
+(in-package "XEDIT")
+(makunbound '*syntax-symbols*)
+
+#|
+TODO:
+o Add a command to match without increment the offset in the input, this
+ may be useful for example in a case like:
+ some-table
+ match "<"
+ switch -1
+ match "<" <- the table already eated this, so it won't be matched.
+ This must be carefully checked at compile time, such instruction should
+ be in a token that returns or starts a new one, and even then, may need
+ runtime check to make sure it won't enter an infinite loop.
+o Allow combining properties, this is supported in Xaw, and could allow some
+ very interesting effects for complex documents.
+o Maybe have an separated function/loop for tables that don't have tokens
+ that start/switch to another table, and/or have the contained attribute set.
+ This could allow running considerably faster.
+o Do a better handling of interactive edition for tokens that start and end
+ with the same pattern, as an example strings, if the user types '"', it
+ will parse up to the end of the file, "inverting" all strings.
+o Allow generic code to be run once a match is found, such code could handle
+ some defined variables and take decisions based on the parser state. This
+ should be detected at compile time, to maybe run a different parser for
+ such syntax tables, due to the extra time building the environment to
+ call the code. This would be useful to "really" parse documents with
+ complex syntax, for example, a man page source file.
+o Add command to change current default property without initializing a new
+ state.
+o Fix problems matching EOL. Since EOL is an empty string match, if there
+ is a rule to match only EOL, but some other rule matches up to the end
+ of the input, the match to EOL will not be recognized. Currently the only
+ way to handle this is to have a nested table that always returns once a
+ match is found, so that it will restart the match loop code even if the
+ input is at EOL.
+ One possible solution would be to add the ending newline to the input,
+ and then instead of matching "$", should match "\\n".
+o XXX Usage of the variable newline-property must be reviewed in function
+ syntax-highlight, if the text property has a background attribute,
+ visual effect will look "strange", will paint a square with the
+ background attribute at the end of every line in the matched text.
+|#
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Some annotations to later write documentation for the module...
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+#|
+ The current interface logic should be easy to understand for people
+that have written lex scanners before. It has some extended semantics,
+that could be translated to stacked BEGIN() statements in lex, but
+currently does not have rules for matches in the format RE/TRAILING, as
+well as code attached to rules (the biggest difference) and/or things
+like REJECT and unput(). Also, at least currently, it is *really* quite
+slower than lex.
+
+ MATCHING RULES
+ --------------
+ When two tokens are matched at the same input offset, the longest
+token is used, if the length is the same, the first definition is
+used. For example:
+ token1 => int
+ token2 => [A-Za-z]+
+ input => integer
+ Token1 matches "int" and token2 matches "integer", but since token2 is
+longer, it is used. But in the case:
+ token1 => int
+ token2 => [A-Za-z]+
+ input => int
+ Both, token1 and token2 match "int", since token1 is defined first, it
+is used.
+|#
+
+
+;; Initialize some default properties that may be shared in syntax
+;; highlight definitions. Use of these default properties is encouraged,
+;; so that "tokens" will be shown identically when editing program
+;; sources in different programming languages.
+(defsynprop *prop-default*
+ "default"
+ :font "*courier-medium-r*12*"
+ :foreground "black")
+
+(defsynprop *prop-keyword*
+ "keyword"
+ :font "*courier-bold-r*12*"
+ :foreground "gray12")
+
+(defsynprop *prop-number*
+ "number"
+ :font "*courier-bold-r*12*"
+ :foreground "OrangeRed3")
+
+(defsynprop *prop-string*
+ "string"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "RoyalBlue2")
+
+(defsynprop *prop-constant*
+ "constant"
+ :font "*lucidatypewriter-medium-r*12*"
+ :foreground "VioletRed3")
+
+(defsynprop *prop-comment*
+ "comment"
+ :font "*courier-medium-o*12*"
+ :foreground "SlateBlue3")
+
+(defsynprop *prop-preprocessor*
+ "preprocessor"
+ :font "*courier-medium-r*12*"
+ :foreground "green4")
+
+(defsynprop *prop-punctuation*
+ "punctuation"
+ :font "*courier-bold-r*12*"
+ :foreground "gray12")
+
+;; Control characters, not always errors...
+(defsynprop *prop-control*
+ "control"
+ :font "*courier-bold-r*12*"
+ :foreground "yellow2"
+ :background "red3")
+
+(defsynprop *prop-error*
+ "error"
+ :font "*new century schoolbook-bold*24*"
+ :foreground "yellow"
+ :background "red")
+
+(defsynprop *prop-annotation*
+ "annotation"
+ :font "*courier-medium-r*12*"
+ :foreground "black"
+ :background "PaleGreen")
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; The "main" definition of the syntax highlight coding interface.
+;; Creates a "special" variable with the given name, associating to
+;; it an already compiled syntax table.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro defsyntax (variable label property indent options &rest lists)
+ `(if (boundp ',variable)
+ ,variable
+ (progn
+ (proclaim '(special ,variable))
+ (setq ,variable
+ (compile-syntax-table
+ (string ',variable) ,options
+ (syntable ,label ,property ,indent ,@lists)
+ )
+ )
+ )
+ )
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Just a wrapper to create a hash-table and bound it to a symbol.
+;; Example of call:
+;; (defsynoptions *my-syntax-options*
+;; (:indent . 8)
+;; (:indent-option-1 . 1)
+;; (:indent-option-2 . 2)
+;; )
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro defsynoptions (variable &rest options)
+ `(if (boundp ',variable)
+ ,variable
+ (progn
+ (proclaim '(special ,variable))
+ (setq ,variable (make-hash-table :initial-contents ',options))
+ )
+ )
+)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; These definitions should be "private".
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defstruct syntoken
+ regex ;; A compiled regexp.
+ property ;; NIL for default, or a synprop structure.
+ contained ;; Only used when switch/begin is not NIL. Values:
+ ;; NIL -> just switch to or begin new
+ ;; syntax table.
+ ;; (not NIL) -> apply syntoken property
+ ;; (or default one) to matched
+ ;; text *after* switching to or
+ ;; beginning a new syntax table.
+ switch ;; Values for switch are:
+ ;; NIL -> do nothing
+ ;; A keyword -> switch to the syntax table
+ ;; identified by the keyword.
+ ;; A negative integer -> Pop the stack
+ ;; -<swich-value> times.
+ ;; A common value is -1,
+ ;; to switch to the previous
+ ;; state, but some times
+ ;; it is desired to return
+ ;; two or more times in
+ ;; in the stack.
+ ;; NOTE: This is actually a jump, the stack is
+ ;; popped until the named syntax table is found,
+ ;; if the stack becomes empty, a new state is
+ ;; implicitly created.
+ begin ;; NIL or a keyword (like switch), but instead of
+ ;; popping the stack, it pushes the current syntax
+ ;; table to the stack and sets a new current one.
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Just a wrapper to make-syntoken.
+;; TODO: Add support for structure constructors.
+;; XXX: Note that the NOSUB only works with the xedit regex, it
+;; will still return the match offsets, but will ignore subexpressions,
+;; that is, parenthesis are used only for grouping.
+;; TODO: Create a new version of the re-exec call that returns
+;; offsets in the format (<from> . <to>) and not
+;; ((<from0> . <to0>) ... (<fromN> . <toN>)). Only the global result
+;; is expected/used, so there is no reason to allocate more than one
+;; cons cell per call.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun syntoken (pattern
+ &key icase nospec property contained switch begin (nosub t)
+ &aux
+ (regex
+ (re-comp pattern :icase icase :nospec nospec :nosub nosub)
+ )
+ check)
+
+ ;; Don't allow a regex that matches the null string enter the
+ ;; syntax table list.
+ (if (consp (setq check (re-exec regex "" :noteol t :notbol t)))
+#+xedit (error "SYNTOKEN: regex matches empty string ~S" regex)
+#-xedit ()
+ )
+
+ (make-syntoken
+ :regex regex
+ :property property
+ :contained contained
+ :switch switch
+ :begin begin
+ )
+)
+
+
+;; This structure is defined only to do some type checking, it just
+;; holds a list of keywords.
+(defstruct synaugment
+ labels ;; List of keywords labeling syntax tables.
+)
+
+(defstruct syntable
+ label ;; A keyword naming this syntax table.
+ property ;; NIL or a default synprop structure.
+ indent ;; Indentation function for the syntax table.
+ tokens ;; A list of syntoken structures.
+ tables ;; A list of syntable structures.
+ augments ;; A list of synaugment structures, used only
+ ;; at "compile time", so that a table can be
+ ;; used before it's definition.
+ bol ;; One of the tokens match the empty string at
+ ;; the start of a line (loop optimization hint).
+ ;; Field filled at "link" time.
+ eol ;; Same comments as bol, but in this case, for
+ ;; the empty string at the end of a line.
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Just call make-syntable, but sorts the elements by type, allowing
+;; a cleaner code when defining the syntax highlight rules.
+;; XXX Same comments as for syntoken about the use of a constructor for
+;; structures. TODO: when/if clos is implemented in the interpreter.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun syntable (label default-property indent &rest definitions)
+
+ ;; Check for possible errors in the arguments.
+ (unless (keywordp label)
+ (error "SYNTABLE: ~A is not a keyword" label)
+ )
+ (unless
+ (or
+ (null default-property)
+ (synprop-p default-property)
+ )
+ (error "SYNTABLE: ~A is an invalid text property"
+ default-property
+ )
+ )
+
+ ;; Don't allow unknown data in the definition list.
+ ;; XXX typecase should be added to the interpreter, and since
+ ;; the code is traversing the entire list, it could build
+ ;; now the arguments to make-syntable.
+ (dolist (item definitions)
+ (unless
+ (or
+
+ ;; Allow NIL in the definition list, so that one
+ ;; can put conditionals in the syntax definition,
+ ;; and if the conditional is false, fill the slot
+ ;; with a NIL value.
+ (atom item)
+ (syntoken-p item)
+ (syntable-p item)
+ (synaugment-p item)
+ )
+ (error "SYNTABLE: invalid syntax table argument ~A" item)
+ )
+ )
+
+ ;; Build the syntax table.
+ (make-syntable
+ :label label
+ :property default-property
+ :indent indent
+ :tokens (remove-if-not #'syntoken-p definitions)
+ :tables (remove-if-not #'syntable-p definitions)
+ :augments (remove-if-not #'synaugment-p definitions)
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Just to do a "preliminary" error checking, every element must be a
+;; a keyword, and also check for reserved names.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun synaugment (&rest keywords)
+ (dolist (keyword keywords)
+ (unless (keywordp keyword)
+ (error "SYNAUGMENT: bad syntax table label ~A" keyword)
+ )
+ )
+ (make-synaugment :labels keywords)
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Recursive compile utility function.
+;; Returns a cons in the format:
+;; car => List of all syntoken structures
+;; (including child tables).
+;; cdr => List of all child syntable structures.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun list-syntable-elements (table &aux result sub-result)
+ (setq
+ result
+ (cons
+ (syntable-tokens table)
+ (syntable-tables table))
+ )
+
+ ;; For every child syntax table.
+ (dolist (child (syntable-tables table))
+
+ ;; Recursively call list-syntable-elements.
+ (setq sub-result (list-syntable-elements child))
+
+ (rplaca result (append (car result) (car sub-result)))
+ (rplacd result (append (cdr result) (cdr sub-result)))
+ )
+
+ ;; Return the pair of nested tokens and tables.
+ result
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Append tokens of the augment list to the tokens of the specified
+;; syntax table.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun compile-syntax-augment-list (table table-list
+ &aux labels augment tokens)
+
+ ;; Create a list of all augment tables.
+ (dolist (augment (syntable-augments table))
+ (setq labels (append labels (synaugment-labels augment)))
+ )
+
+ ;; Remove duplicates and references to "itself",
+ ;; without warnings?
+ (setq
+ labels
+ (remove
+ (syntable-label table)
+ (remove-duplicates labels :from-end t)
+ )
+ )
+
+ ;; Check if the specified syntax tables exists!
+ (dolist (label labels)
+ (unless
+ (setq
+ augment
+ (car (member label table-list :key #'syntable-label))
+ )
+ (error "COMPILE-SYNTAX-AUGMENT-LIST: Cannot augment ~A in ~A"
+ label
+ (syntable-label table)
+ )
+ )
+
+ ;; Increase list of tokens.
+ (setq tokens (append tokens (syntable-tokens augment)))
+ )
+
+ ;; Store the tokens in the augment list. They will be added
+ ;; to the syntax table in the second pass.
+ (setf (syntable-augments table) tokens)
+
+ ;; Recurse on every child table.
+ (dolist (child (syntable-tables table))
+ (compile-syntax-augment-list child table-list)
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Just add the augmented tokens to the token list, recursing on
+;; every child syntax table.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun link-syntax-augment-table (table)
+ (setf
+ (syntable-tokens table)
+ ;; When augmenting a table, duplicated tokens or different tokens
+ ;; that use the same regex pattern should be common.
+ (remove-duplicates
+ (nconc (syntable-tokens table) (syntable-augments table))
+ :key #'syntoken-regex
+ :test #'equal
+ :from-end t
+ )
+
+ ;; Don't need to keep this list anymore.
+ (syntable-augments table)
+ ()
+ )
+
+ ;; Check if one of the tokens match the empty string at the
+ ;; start or end of a text line. XXX The fields bol and eol
+ ;; are expected to be initialized to NIL.
+ (dolist (token (syntable-tokens table))
+ (when (consp (re-exec (syntoken-regex token) "" :noteol t))
+ (setf (syntable-bol table) t)
+ (return)
+ )
+ )
+ (dolist (token (syntable-tokens table))
+ (when (consp (re-exec (syntoken-regex token) "" :notbol t))
+ (setf (syntable-eol table) t)
+ (return)
+ )
+ )
+
+ (dolist (child (syntable-tables table))
+ (link-syntax-augment-table child)
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; "Compile" the main structure of the syntax highlight code.
+;; Variables "switches" and "begins" are used only for error checking.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun compile-syntax-table (name options main-table &aux syntax elements
+ switches begins tables properties)
+ (unless (stringp name)
+ (error "COMPILE-SYNTAX-TABLE: ~A is not a string" name)
+ )
+
+ (setq
+ elements
+ (list-syntable-elements main-table)
+
+ switches
+ (remove-if
+ #'null
+ (car elements)
+ :key #'syntoken-switch
+ )
+
+ begins
+ (remove-if-not
+ #'keywordp
+ (car elements)
+ :key #'syntoken-begin
+ )
+
+ ;; The "main-table" isn't in the list, because
+ ;; list-syntable-elements includes only the child tables;
+ ;; this is done to avoid the need of removing duplicates here.
+ tables
+ (cons main-table (cdr elements))
+ )
+
+ ;; Check for typos in the keywords, or for not defined syntax tables.
+ (dolist (item (mapcar #'syntoken-switch switches))
+ (unless
+ (or
+ (and
+ (integerp item)
+ (minusp item)
+ )
+ (member item tables :key #'syntable-label)
+ )
+ (error "COMPILE-SYNTAX-TABLE: SWITCH ~A cannot be matched"
+ item
+ )
+ )
+ )
+ (dolist (item (mapcar #'syntoken-begin begins))
+ (unless (member item tables :key #'syntable-label)
+ (error "COMPILE-SYNTAX-TABLE: BEGIN ~A cannot be matched"
+ item
+ )
+ )
+ )
+
+ ;; Create a list of all properties used by the syntax.
+ (setq
+ properties
+ (delete-duplicates
+
+ ;; Remove explicitly set to "default" properties.
+ (remove nil
+
+ (append
+
+ ;; List all properties in the syntoken list.
+ (mapcar
+ #'syntoken-property
+ (car elements)
+ )
+
+ ;; List all properties in the syntable list.
+ (mapcar
+ #'syntable-property
+ tables
+ )
+ )
+ )
+ :test #'string=
+ :key #'synprop-name
+ )
+ )
+
+ ;; Provide a default property if none specified.
+ (unless
+ (member
+ "default"
+ properties
+ :test #'string=
+ :key #'synprop-name
+ )
+ (setq properties (append (list *prop-default*) properties))
+ )
+
+
+ ;; Now that a list of all nested syntax tables is known, compile the
+ ;; augment list. Note that even the main-table can be augmented to
+ ;; include tokens of one of it's children.
+
+ ;; Adding the tokens of the augment tables must be done in
+ ;; two passes, or it may cause surprises due to "inherited"
+ ;; tokens, as the augment table was processed first, and
+ ;; increased it's token list.
+ (compile-syntax-augment-list main-table tables)
+
+ ;; Now just append the augmented tokens to the table's token list.
+ (link-syntax-augment-table main-table)
+
+ ;; Change all syntoken switch and begin fields to point to the
+ ;; syntable.
+ (dolist (item switches)
+ (if (keywordp (syntoken-switch item))
+ ;; A switch may be relative, check if a keyword
+ ;; was specified.
+ (setf
+ (syntoken-switch item)
+ (car
+ (member
+ (syntoken-switch item)
+ tables
+ :key #'syntable-label
+ )
+ )
+ )
+ )
+ )
+ (dolist (item begins)
+ (setf
+ (syntoken-begin item)
+ (car
+ (member
+ (syntoken-begin item)
+ tables
+ :key #'syntable-label
+ )
+ )
+ )
+ )
+
+ ;; Don't need to add a entity for default properties
+ (dolist (item (car elements))
+ (and
+ (syntoken-property item)
+ (string= (synprop-name (syntoken-property item)) "default")
+ (setf (syntoken-property item) ())
+ )
+ )
+ (dolist (item tables)
+ (and
+ (syntable-property item)
+ (string= (synprop-name (syntable-property item)) "default")
+ (setf (syntable-property item) ())
+ )
+ )
+
+ (setq syntax
+ (make-syntax
+ :name name
+ :options options
+ :labels tables
+ :quark
+ (compile-syntax-property-list
+ name
+ properties
+ )
+ :token-count
+ (length (car elements))
+ )
+ )
+
+ ;; Ready to run!
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Loop applying the specifed syntax table to the text.
+;; XXX This function needs a review. Should compile the regex patterns
+;; with newline sensitive match (and scan the entire file), and keep a
+;; cache of matched tokens (that may be at a very longer offset), and,
+;; when the match is removed from the cache, readd the token to the
+;; token-list; if the token does not match, it will not be in the cache,
+;; but should be removed from the token-list. If properly implemented, it
+;; should be somewhat like 4 times faster, but I would not be surprised
+;; if it becames even faster.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun syntax-highlight (*syntax*
+ &optional
+ (*from* (point-min))
+ (*to* (point-max))
+ interactive
+ &aux
+#+debug (*line-number* 0)
+ stream
+ indent-table
+ )
+
+ ;; Make sure the property list is in use.
+ ;; The interactive flag is only set after loading the file.
+ (or interactive
+ (property-list (syntax-quark *syntax*))
+ )
+
+#+debug
+ (setq *from* 0 *to* 0)
+
+#-debug
+ (and (>= *from* *to*) (return-from syntax-highlight (values *from* nil)))
+
+ ;; Remove any existing properties from the text.
+ (clear-entities *from* (1+ *to*))
+
+ (setq stream
+#-debug (make-string-input-stream (read-text *from* (- *to* *from*)))
+#+debug *standard-input*
+ )
+
+ (prog*
+ (
+ ;; Used to check if end of file found but syntax stack did
+ ;; not finish.
+ (point-max (point-max))
+
+ ;; Used in interactive mode, to return the syntax table
+ ;; where the cursor is located.
+ (point (point))
+
+ ;; The current stack of states.
+ stack
+
+ ;; The current syntable.
+ (syntax-table (car (syntax-labels *syntax*)))
+
+ ;; The current syntable's default property.
+ (default-property (syntable-property syntax-table))
+
+ ;; Add this property to newlines as a hint to the interactive
+ ;; callback, so that it knows from where to restart parsing.
+ newline-property
+
+ ;; The tokens in the current syntax table that may match,
+ ;; i.e. the items in this list are not in nomatch.
+ token-list
+
+ ;; A pointer to the syntable token list, if token-list is
+ ;; eq to this value, cannot change it inplace.
+ current-token-list
+
+ ;; Help to avoid allocating too many new object cells, and
+ ;; optmizes a bit time in [n]?set-difference.
+ ;; This optimizes only the processing of one line of text
+ ;; as nomatch must be rebuilt when reading a new line of text.
+ token-list-stack
+
+ ;; Matches for the current list of tokens.
+ matches
+
+ ;; Line of text.
+ line
+
+ ;; Length of the text line.
+ length
+
+ ;; A inverse cache, don't call re-exec when the regex is
+ ;; already known to not match.
+ nomatch
+
+ ;; Use cache as a list of matches to avoid repetitive
+ ;; unnecessary calls to re-exec.
+ ;; cache is a list in which every element has the format:
+ ;; (token . (start . end))
+ ;; Line of text.
+ cache
+
+ ;; Used just to avoid a function call at every re-exec call.
+ notbol
+
+ match
+
+ start
+ left
+ right
+ result
+ property
+
+ ;; Beginig a new syntax table?
+ begin
+
+ ;; Switching to another syntax table?
+ switch
+
+ ;; Property flag when changing the current syntax table.
+ contained
+
+ ;; Flag to know if syntax table has changed.
+ change
+
+ ;; Variables used when removing invalid elements from the
+ ;; the cache.
+ item
+ from
+ to
+ )
+
+;-----------------------------------------------------------------------
+:read
+#+debug-verbose
+ (format t "** Entering :READ stack length is ~D~%" (length stack))
+#+debug (format t "~%[~D]> " (incf *line-number*))
+
+ ;; If input has finished, return.
+ (unless (setq line (read-line stream nil nil))
+ (when
+ (and
+ ;; If a nested syntax table wasn't finished
+ (consp stack)
+ (<
+ (setq *to* (scan *from* :eol :right))
+ point-max
+ )
+ )
+ (setq line (read-text *from* (- *to* *from*)))
+ (clear-entities *from* (1+ *to*))
+ (go :again)
+ )
+#-debug (close stream)
+ (return)
+ )
+
+;------------------------------------------------------------------------
+:again
+ (setq
+ start 0
+ length (length line)
+ token-list (syntable-tokens syntax-table)
+ current-token-list token-list
+ token-list-stack ()
+ nomatch ()
+ cache ()
+ )
+
+
+ ;; If empty line, and current table does not have matches for
+ ;; the empty string at start or end of a text line.
+ (when
+ (and
+ (= length 0)
+ (not (syntable-eol syntax-table))
+ (not (syntable-bol syntax-table)))
+#+debug-verbose
+ (format t "Empty line and table has no match to bol or eol~%")
+
+ (and newline-property
+ (add-entity *from* 1 (synprop-quark newline-property)))
+ (go :update)
+ )
+
+;------------------------------------------------------------------------
+:loop
+#+debug-verbose
+ (format t "** Entering :LOOP at offset ~D in table ~A, cache has ~D items~%"
+ start
+ (syntable-label syntax-table)
+ (length cache))
+
+ (setq notbol (> start 0))
+
+ ;; For every token that may match.
+ (dolist
+ (token
+ (setq
+ token-list
+ (if (eq token-list current-token-list)
+ (set-difference token-list nomatch :test #'eq)
+ (nset-difference token-list nomatch :test #'eq)
+ )
+ )
+ )
+
+ ;; Try to fetch match from cache.
+ (if (setq match (member token cache :test #'eq :key #'car))
+ ;; Match is in the cache.
+
+ (progn
+ ;; Match must be moved to the beginning of the
+ ;; matches list, as a match from another syntax
+ ;; table may be also in the cache, but before
+ ;; the match for the current token.
+#+debug-verbose (format t "cached: {~A:~S} ~A~%"
+ (cdar match)
+ (subseq line (cadar match) (cddar match))
+ (syntoken-regex token))
+
+ ;; Remove the match from the cache.
+ (if (eq match cache)
+
+ ;; This could be changed to only set "matches"
+ ;; if it is not the first element of cache,
+ ;; but is unsafe, because other tokens may
+ ;; be added to "matches", and will end up
+ ;; before when joining "matches" and "cache".
+ (progn
+ (setq cache (cdr cache))
+ (rplacd match matches)
+ (setq matches match))
+
+ (progn
+ (if (= (length match) 1)
+ (progn
+ (rplacd (last cache 2) nil)
+ (rplacd match matches)
+ (setq matches match))
+ (progn
+ (setq matches (cons (car match) matches))
+ (rplaca match (cadr match))
+ (rplacd match (cddr match)))
+ )
+ )
+ )
+
+ ;; Exit loop if the all the remaining
+ ;; input was matched.
+ (when
+ (and
+ (= start (cadar match))
+ (= length (cddar match))
+ )
+#+debug-verbose (format t "Rest of line match~%")
+ (return)
+ )
+ )
+
+ ;; Not in the cache, call re-exec.
+ (if
+ (consp
+ (setq
+ match
+ (re-exec
+ (syntoken-regex token)
+ line
+ :start start
+ :notbol notbol)))
+
+ ;; Match found.
+ (progn
+#+debug-verbose (format t "Adding to cache: {~A:~S} ~A~%"
+ (car match)
+ (subseq line (caar match) (cdar match))
+ (syntoken-regex token))
+
+ ;; Only the first pair is used.
+ (setq match (car match))
+
+ (cond
+ (
+ (or
+ (null matches)
+ ;; No overlap and after most
+ ;; recent match.
+ (>= (car match) (cddar matches))
+ ;; No overlap and before most
+ ;; recent match.
+ (<= (cdr match) (cadar matches))
+ )
+ (setq
+ matches
+ (cons (cons token match) matches)
+ )
+ )
+ (
+ (or
+ ;; Overlap, but start before most
+ ;; recent match.
+ (< (car match) (cadar matches))
+ (and
+ ;; Same offset as most recent
+ ;; match, but is longer.
+ (= (car match) (cadar matches))
+ (> (cdr match) (cddar matches))
+ )
+ )
+ (rplaca (car matches) token)
+ (rplacd (car matches) match)
+#+debug-verbose (format t "Replaced most recent match~%")
+ )
+ (t
+#+debug-verbose (format t "Ignored~%")
+ ;; XXX The interpreter does not yet implement
+ ;; implicit tagbody in dolist, just comment
+ ;; the go call in that case. (Will just do
+ ;; an unecessary test...)
+ (go :ignored)
+ )
+ )
+
+ ;; Exit loop if the all the remaining
+ ;; input was matched.
+ (when
+ (and
+ (= start (car match))
+ (= length (cdr match)))
+#+debug-verbose (format t "Rest of line match~%")
+ (return))
+ )
+
+ ;; Match not found.
+ (progn
+#+debug-verbose (format t "Adding to nomatch: ~A~%"
+ (syntoken-regex token))
+ (setq nomatch (cons token nomatch)))
+ )
+ )
+:ignored
+ )
+
+ ;; Add matches to the beginning of the cache list.
+ (setq
+ ;; Put matches with smaller offset first.
+ cache
+ (stable-sort (nconc (nreverse matches) cache) #'< :key #'cadr)
+
+ ;; Make sure that when the match loop is reentered, this
+ ;; variable is NIL.
+ matches
+ ()
+ )
+
+ ;; While the first entry in the cache is not from the current table.
+ (until (or (null cache) (member (caar cache) token-list :test #'eq))
+
+#+debug-verbose
+ (format t "Not in the current table, removing {~A:~S} ~A~%"
+ (cdar cache)
+ (subseq line (cadar cache) (cddar cache))
+ (syntoken-regex (caar cache)))
+
+ (setq cache (cdr cache))
+ )
+
+
+ ;; If nothing was matched in the entire/remaining line.
+ (unless cache
+ (when default-property
+ (if
+ (or
+ (null result)
+ (> start (cadar result))
+ (not (eq (cddar result) default-property)))
+ (setq
+ result
+ (cons
+ (cons start (cons length default-property))
+ result
+ )
+ )
+ (rplaca (cdar result) length)
+ )
+ )
+
+#+debug-verbose
+ (format t "No match until end of line~%")
+
+ ;; Result already known, and there is no syntax table
+ ;; change, bypass :PARSE.
+ (and interactive
+ (null indent-table)
+ (<= 0 (- point *from*) length)
+ (setq indent-table syntax-table))
+ (go :process)
+ )
+
+#+debug-verbose
+ (format t "Removing first candidate from cache {~A:~S} ~A~%"
+ (cdar cache)
+ (subseq line (cadar cache) (cddar cache))
+ (syntoken-regex (caar cache))
+ )
+
+ ;; Prepare to choose best match.
+ (setq
+ match (car cache)
+ left (cadr match)
+ right (cddr match)
+ cache (cdr cache)
+ )
+
+ ;; First element can be safely removed now.
+ ;; If there is only one, skip loop below.
+ (or cache (go :parse))
+
+ ;; Remove elements of cache that must be discarded.
+ (setq
+ item (car cache)
+ from (cadr item)
+ to (cddr item)
+ )
+
+ (loop
+ (if
+ (or
+
+ ;; If everything removed from the cache.
+ (null item)
+
+ ;; Or next item is at a longer offset than the
+ ;; end of current match.
+ (>= from right)
+ )
+ (return)
+ )
+
+ (and
+ ;; If another match at the same offset.
+ (= left from)
+
+ ;; And if this match is longer than the current one.
+ (> to right)
+
+ (member (car item) token-list :test #'eq)
+
+ (setq
+ match item
+ right to
+ )
+ )
+
+#+debug-verbose
+ (format t "Removing from cache {~A:~S} ~A~%"
+ (cdar cache)
+ (subseq line from to)
+ (syntoken-regex (caar cache)))
+
+ (setq
+ cache (cdr cache)
+ item (car cache)
+ from (cadr item)
+ to (cddr item)
+ )
+ )
+
+
+;-----------------------------------------------------------------------
+:parse
+#+debug-verbose
+ (format t "** Entering :PARSE~%")
+
+ (setq
+
+ ;; Change match value to the syntoken.
+ match (car match)
+
+ begin (syntoken-begin match)
+ switch (syntoken-switch match)
+ contained (syntoken-contained match)
+ change (or begin switch)
+ )
+
+ ;; Check for unmatched leading text.
+ (when (and default-property (> left start))
+#+debug-verbose (format t "No match in {(~D . ~D):~S}~%"
+ start
+ left
+ (subseq line start left)
+ )
+ (if
+ (or
+ (null result)
+ (> start (cadar result))
+ (not (eq (cddar result) default-property)))
+ (setq
+ result
+ (cons
+ (cons start (cons left default-property))
+ result
+ )
+ )
+ (rplaca (cdar result) left)
+ )
+ )
+
+ ;; If the syntax table is not changed,
+ ;; or if the new table requires that the
+ ;; current default property be used.
+ (unless (and change contained)
+
+ (and
+ (> right left)
+ (setq
+ property
+ (or
+ ;; If token specifies the property.
+ (syntoken-property match)
+ default-property
+ )
+ )
+
+ ;; Add matched text.
+ (if
+ (or
+ (null result)
+ (> left (cadar result))
+ (not (eq (cddar result) property))
+ )
+ (setq
+ result
+ (cons
+ (cons left (cons right property))
+ result
+ )
+ )
+ (rplaca (cdar result) right)
+ )
+ )
+
+#+debug-verbose
+ (format t "(0)Match found for {(~D . ~D):~S}~%"
+ left
+ right
+ (subseq line left right)
+ )
+ )
+
+
+ ;; Update start offset in the input now!
+ (and interactive
+ (null indent-table)
+ (<= start (- point *from*) right)
+ (setq indent-table syntax-table))
+ (setq start right)
+
+
+ ;; When changing the current syntax table.
+ (when change
+ (when switch
+ (if (numberp switch)
+
+ ;; If returning to a previous state.
+ ;; Don't generate an error if the stack
+ ;; becomes empty?
+ (while
+ (< switch 0)
+
+ (setq
+ syntax-table (pop stack)
+ token-list (pop token-list-stack)
+ switch (1+ switch)
+ )
+ )
+
+ ;; Else, not to a previous state, but
+ ;; returning to a named syntax table,
+ ;; search for it in the stack.
+ (while
+ (and
+
+ (setq
+ token-list (pop token-list-stack)
+ syntax-table (pop stack)
+ )
+
+ (not (eq switch syntax-table))
+ )
+ ;; Empty loop.
+ )
+ )
+
+ ;; If no match found while popping
+ ;; the stack.
+ (unless syntax-table
+
+ ;; Return to the topmost syntax table.
+ (setq
+ syntax-table
+ (car (syntax-labels *syntax*))
+ )
+ )
+
+#+debug-verbose (format t "switching to ~A offset: ~D~%"
+ (syntable-label syntax-table)
+ start
+ )
+
+ (if (null token-list)
+ (setq token-list (syntable-tokens syntax-table))
+ )
+ )
+
+ (when begin
+ ;; Save state for a possible
+ ;; :SWITCH later.
+ (setq
+ stack (cons syntax-table stack)
+ token-list-stack (cons token-list token-list-stack)
+ token-list (syntable-tokens begin)
+ syntax-table begin
+ )
+#+debug-verbose (format t "begining ~A offset: ~D~%"
+ (syntable-label syntax-table)
+ start
+ )
+ )
+
+ ;; Change current syntax table.
+ (setq
+ default-property (syntable-property syntax-table)
+ current-token-list (syntable-tokens syntax-table)
+ )
+
+ ;; Set newline property, to help interactive callback
+ ;; Only need to have a defined value, for now don't care
+ ;; about wich value is being used, neither if there is
+ ;; a value to be set.
+ (if (null stack)
+ (setq newline-property nil)
+ (or newline-property
+ (setq newline-property default-property)
+ (setq newline-property (syntoken-property match))
+ )
+ )
+
+ ;; If processing of text was deferred.
+ (when contained
+
+ (and
+ (> right left)
+ (setq
+ property
+ (or
+ (syntoken-property match)
+ default-property
+ )
+ )
+ ;; Add matched text with the updated property.
+ (if
+ (or
+ (null result)
+ (> left (cadar result))
+ (not (eq (cddar result) property))
+ )
+ (setq
+ result
+ (cons
+ (cons left (cons right property))
+ result
+ )
+ )
+ (rplaca (cdar result) right)
+ )
+ )
+
+#+debug-verbose (format t "(1)Match found for {(~D . ~D):~S}~%"
+ left
+ right
+ (subseq line left right)
+ )
+ )
+
+ (go :loop)
+ )
+
+
+;-----------------------------------------------------------------------
+ ;; Wait for the end of the line to process, so that
+ ;; it is possible to join sequential matches with the
+ ;; same text property.
+ (and (or cache (< start length)) (go :loop))
+:process
+
+#+debug-verbose
+ (format t "** Entering :PROCESS~%")
+
+ (if result
+ (progn
+ ;; If the last property was at the end of the line,
+ ;; there are nested syntax tables, and there is a
+ ;; default property, include the newline in the property,
+ ;; as a hint to the interactive callback.
+ (and
+ newline-property
+ (if
+ (and
+ (eq (cddar result) newline-property)
+ (= length (cadar result))
+ )
+ (rplaca (cdar result) (1+ length))
+ (setq
+ result
+ (cons
+ (cons length (cons (1+ length) newline-property))
+ result
+ )
+ )
+ )
+ )
+
+ ;; Result was created in reversed order.
+ (nreverse result)
+ (dolist (item result)
+ (setq
+ left (car item)
+ right (cadr item)
+ property (cddr item))
+
+ ;; Use the information.
+ (add-entity
+ (+ *from* left)
+ (- right left)
+ (synprop-quark property))
+ )
+ )
+
+ (and newline-property
+ (add-entity
+ (+ *from* length)
+ 1
+ (synprop-quark newline-property))
+ )
+ )
+
+;------------------------------------------------------------------------
+:update
+ ;; Prepare for new matches.
+ (setq
+ result nil
+
+ ;; Update offset to read text.
+ ;; Add 1 for the skipped newline.
+ *from* (+ *from* length 1)
+ )
+
+ (go :read)
+ )
+
+#+debug (terpri)
+ (values *to* indent-table)
+)
+
+(compile 'syntax-highlight)
diff --git a/lisp/modules/x11.c b/lisp/modules/x11.c
new file mode 100644
index 0000000..3cdb0bc
--- /dev/null
+++ b/lisp/modules/x11.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/modules/x11.c,v 1.10 2002/11/23 08:26:52 paulo Exp $ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "internal.h"
+#include "private.h"
+#include <X11/Xlib.h>
+
+/*
+ * Prototypes
+ */
+int x11LoadModule(void);
+
+LispObj *Lisp_XOpenDisplay(LispBuiltin *builtin);
+LispObj *Lisp_XCloseDisplay(LispBuiltin *builtin);
+LispObj *Lisp_XDefaultRootWindow(LispBuiltin *builtin);
+LispObj *Lisp_XDefaultScreen(LispBuiltin *builtin);
+LispObj *Lisp_XDefaultScreenOfDisplay(LispBuiltin *builtin);
+LispObj *Lisp_XBlackPixel(LispBuiltin *builtin);
+LispObj *Lisp_XBlackPixelOfScreen(LispBuiltin *builtin);
+LispObj *Lisp_XWidthOfScreen(LispBuiltin *builtin);
+LispObj *Lisp_XHeightOfScreen(LispBuiltin *builtin);
+LispObj *Lisp_XWhitePixel(LispBuiltin *builtin);
+LispObj *Lisp_XWhitePixelOfScreen(LispBuiltin *builtin);
+LispObj *Lisp_XDefaultGC(LispBuiltin *builtin);
+LispObj *Lisp_XDefaultGCOfScreen(LispBuiltin *builtin);
+LispObj *Lisp_XCreateSimpleWindow(LispBuiltin *builtin);
+LispObj *Lisp_XMapWindow(LispBuiltin *builtin);
+LispObj *Lisp_XDestroyWindow(LispBuiltin *builtin);
+LispObj *Lisp_XFlush(LispBuiltin *builtin);
+LispObj *Lisp_XRaiseWindow(LispBuiltin *builtin);
+LispObj *Lisp_XBell(LispBuiltin *builtin);
+
+LispObj *Lisp_XDrawLine(LispBuiltin *builtin);
+
+/*
+ * Initialization
+ */
+static LispBuiltin lispbuiltins[] = {
+ {LispFunction, Lisp_XOpenDisplay, "x-open-display &optional display-name"},
+ {LispFunction, Lisp_XCloseDisplay, "x-close-display display"},
+ {LispFunction, Lisp_XDefaultRootWindow, "x-default-root-window display"},
+ {LispFunction, Lisp_XDefaultScreen, "x-default-screen display"},
+ {LispFunction, Lisp_XDefaultScreenOfDisplay, "x-default-screen-of-display display"},
+ {LispFunction, Lisp_XBlackPixel, "x-black-pixel display &optional screen"},
+ {LispFunction, Lisp_XBlackPixelOfScreen, "x-black-pixel-of-screen screen"},
+ {LispFunction, Lisp_XWhitePixel, "x-white-pixel display &optional screen"},
+ {LispFunction, Lisp_XWhitePixelOfScreen, "x-white-pixel-of-screen screen"},
+ {LispFunction, Lisp_XDefaultGC, "x-default-gc display &optional screen"},
+ {LispFunction, Lisp_XDefaultGCOfScreen, "x-default-gc-of-screen screen"},
+ {LispFunction, Lisp_XCreateSimpleWindow, "x-create-simple-window display parent x y width height &optional border-width border background"},
+ {LispFunction, Lisp_XMapWindow, "x-map-window display window"},
+ {LispFunction, Lisp_XDestroyWindow, "X-DESTROY-WINDOW"},
+ {LispFunction, Lisp_XFlush, "x-flush display"},
+ {LispFunction, Lisp_XDrawLine, "x-draw-line display drawable gc x1 y1 x2 y2"},
+ {LispFunction, Lisp_XBell, "x-bell display &optional percent"},
+ {LispFunction, Lisp_XRaiseWindow, "x-raise-window display window"},
+ {LispFunction, Lisp_XWidthOfScreen, "x-width-of-screen screen"},
+ {LispFunction, Lisp_XHeightOfScreen, "x-height-of-screen screen"},
+};
+
+LispModuleData x11LispModuleData = {
+ LISP_MODULE_VERSION,
+ x11LoadModule
+};
+
+static int x11Display_t, x11Screen_t, x11Window_t, x11GC_t;
+
+/*
+ * Implementation
+ */
+int
+x11LoadModule(void)
+{
+ int i;
+
+ x11Display_t = LispRegisterOpaqueType("Display*");
+ x11Screen_t = LispRegisterOpaqueType("Screen*");
+ x11Window_t = LispRegisterOpaqueType("Window");
+ x11GC_t = LispRegisterOpaqueType("GC");
+
+ for (i = 0; i < sizeof(lispbuiltins) / sizeof(lispbuiltins[0]); i++)
+ LispAddBuiltinFunction(&lispbuiltins[i]);
+
+ return (1);
+}
+
+LispObj *
+Lisp_XOpenDisplay(LispBuiltin *builtin)
+/*
+x-open-display &optional display-name
+ */
+{
+ LispObj *display_name;
+ char *dname;
+
+ display_name = ARGUMENT(0);
+
+ if (display_name == UNSPEC)
+ dname = NULL;
+ else {
+ CHECK_STRING(display_name);
+ dname = THESTR(display_name);
+ }
+
+ return (OPAQUE(XOpenDisplay(dname), x11Display_t));
+}
+
+LispObj *
+Lisp_XCloseDisplay(LispBuiltin *builtin)
+/*
+ x-close-display display
+ */
+{
+ LispObj *display;
+
+ display = ARGUMENT(0);
+
+ if (!CHECKO(display, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(display));
+
+ XCloseDisplay((Display*)(display->data.opaque.data));
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_XDefaultRootWindow(LispBuiltin *builtin)
+/*
+ x-default-root-window display
+ */
+{
+ LispObj *display;
+
+ display = ARGUMENT(0);
+
+ if (!CHECKO(display, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(display));
+
+ return (OPAQUE(DefaultRootWindow((Display*)(display->data.opaque.data)),
+ x11Window_t));
+}
+
+LispObj *
+Lisp_XDefaultScreen(LispBuiltin *builtin)
+/*
+ x-default-screen display
+ */
+{
+ LispObj *display;
+
+ display = ARGUMENT(0);
+
+ if (!CHECKO(display, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(display));
+
+ return (INTEGER(DefaultScreen((Display*)(display->data.opaque.data))));
+}
+
+LispObj *
+Lisp_XDefaultScreenOfDisplay(LispBuiltin *builtin)
+/*
+ x-default-screen-of-display display
+ */
+{
+ LispObj *display;
+
+ display = ARGUMENT(0);
+
+ if (!CHECKO(display, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(display));
+
+ return (OPAQUE(DefaultScreenOfDisplay((Display*)(display->data.opaque.data)),
+ x11Screen_t));
+}
+
+LispObj *
+Lisp_XBlackPixel(LispBuiltin *builtin)
+/*
+ x-black-pixel display &optional screen
+ */
+{
+ Display *display;
+ int screen;
+
+ LispObj *odisplay, *oscreen;
+
+ oscreen = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ if (oscreen == UNSPEC)
+ screen = DefaultScreen(display);
+ else
+ CHECK_INDEX(oscreen);
+ else
+ screen = FIXNUM_VALUE(oscreen);
+
+ if (screen >= ScreenCount(display))
+ LispDestroy("%s: screen index %d too large, %d screens available",
+ STRFUN(builtin), screen, ScreenCount(display));
+
+ return (INTEGER(BlackPixel(display, screen)));
+}
+
+LispObj *
+Lisp_XBlackPixelOfScreen(LispBuiltin *builtin)
+/*
+ x-black-pixel-of-screen screen
+ */
+{
+ LispObj *screen;
+
+ screen = ARGUMENT(0);
+
+ if (!CHECKO(screen, x11Screen_t))
+ LispDestroy("%s: cannot convert %s to Screen*",
+ STRFUN(builtin), STROBJ(screen));
+
+ return (INTEGER(XBlackPixelOfScreen((Screen*)(screen->data.opaque.data))));
+}
+
+LispObj *
+Lisp_XWhitePixel(LispBuiltin *builtin)
+/*
+ x-white-pixel display &optional screen
+ */
+{
+ Display *display;
+ int screen;
+
+ LispObj *odisplay, *oscreen;
+
+ oscreen = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ if (oscreen == UNSPEC)
+ screen = DefaultScreen(display);
+ else
+ CHECK_FIXNUM(oscreen);
+ else
+ screen = FIXNUM_VALUE(oscreen);
+
+ if (screen >= ScreenCount(display))
+ LispDestroy("%s: screen index %d too large, %d screens available",
+ STRFUN(builtin), screen, ScreenCount(display));
+
+ return (INTEGER(WhitePixel(display, screen)));
+}
+
+LispObj *
+Lisp_XWhitePixelOfScreen(LispBuiltin *builtin)
+/*
+ x-white-pixel-of-screen screen
+ */
+{
+ LispObj *screen;
+
+ screen = ARGUMENT(0);
+
+ if (!CHECKO(screen, x11Screen_t))
+ LispDestroy("%s: cannot convert %s to Screen*",
+ STRFUN(builtin), STROBJ(screen));
+
+ return (INTEGER(WhitePixelOfScreen((Screen*)(screen->data.opaque.data))));
+}
+
+LispObj *
+Lisp_XDefaultGC(LispBuiltin *builtin)
+/*
+ x-default-gc display &optional screen
+ */
+{
+ Display *display;
+ int screen;
+
+ LispObj *odisplay, *oscreen;
+
+ oscreen = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ if (oscreen == UNSPEC)
+ screen = DefaultScreen(display);
+ else
+ CHECK_FIXNUM(oscreen);
+ else
+ screen = FIXNUM_VALUE(oscreen);
+
+ if (screen >= ScreenCount(display))
+ LispDestroy("%s: screen index %d too large, %d screens available",
+ STRFUN(builtin), screen, ScreenCount(display));
+
+ return (OPAQUE(DefaultGC(display, screen), x11GC_t));
+}
+
+LispObj *
+Lisp_XDefaultGCOfScreen(LispBuiltin *builtin)
+/*
+ x-default-gc-of-screen screen
+ */
+{
+ LispObj *screen;
+
+ screen = ARGUMENT(0);
+
+ if (!CHECKO(screen, x11Screen_t))
+ LispDestroy("%s: cannot convert %s to Screen*",
+ STRFUN(builtin), STROBJ(screen));
+
+ return (OPAQUE(DefaultGCOfScreen((Screen*)(screen->data.opaque.data)),
+ x11GC_t));
+}
+
+LispObj *
+Lisp_XCreateSimpleWindow(LispBuiltin *builtin)
+/*
+ x-create-simple-window display parent x y width height &optional border-width border background
+ */
+{
+ Display *display;
+ Window parent;
+ int x, y;
+ unsigned int width, height, border_width;
+ unsigned long border, background;
+
+ LispObj *odisplay, *oparent, *ox, *oy, *owidth, *oheight,
+ *oborder_width, *oborder, *obackground;
+
+ obackground = ARGUMENT(8);
+ oborder = ARGUMENT(7);
+ oborder_width = ARGUMENT(6);
+ oheight = ARGUMENT(5);
+ owidth = ARGUMENT(4);
+ oy = ARGUMENT(3);
+ ox = ARGUMENT(2);
+ oparent = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ if (!CHECKO(oparent, x11Window_t))
+ LispDestroy("%s: cannot convert %s to Window",
+ STRFUN(builtin), STROBJ(oparent));
+ parent = (Window)(oparent->data.opaque.data);
+
+ CHECK_FIXNUM(ox);
+ x = FIXNUM_VALUE(ox);
+
+ CHECK_FIXNUM(oy);
+ y = FIXNUM_VALUE(oy);
+
+ CHECK_INDEX(owidth);
+ width = FIXNUM_VALUE(owidth);
+
+ CHECK_INDEX(oheight);
+ height = FIXNUM_VALUE(oheight);
+
+ /* check &OPTIONAL parameters */
+ if (oborder_width == UNSPEC)
+ border_width = 1;
+ else
+ CHECK_INDEX(oborder_width);
+ else
+ border_width = FIXNUM_VALUE(oborder_width);
+
+ if (oborder == UNSPEC)
+ border = BlackPixel(display, DefaultScreen(display));
+ else
+ CHECK_LONGINT(oborder);
+ else
+ border = LONGINT_VALUE(oborder);
+
+ if (obackground == UNSPEC)
+ background = WhitePixel(display, DefaultScreen(display));
+ else
+ CHECK_LONGINT(obackground);
+ else
+ background = LONGINT_VALUE(obackground);
+
+ return (OPAQUE(
+ XCreateSimpleWindow(display, parent, x, y, width, height,
+ border_width, border, background),
+ x11Window_t));
+}
+
+LispObj *
+Lisp_XMapWindow(LispBuiltin *builtin)
+/*
+ x-map-window display window
+ */
+{
+ Display *display;
+ Window window;
+
+ LispObj *odisplay, *owindow;
+
+ owindow = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ if (!CHECKO(owindow, x11Window_t))
+ LispDestroy("%s: cannot convert %s to Window",
+ STRFUN(builtin), STROBJ(owindow));
+ window = (Window)(owindow->data.opaque.data);
+
+ XMapWindow(display, window);
+
+ return (owindow);
+}
+
+LispObj *
+Lisp_XDestroyWindow(LispBuiltin *builtin)
+/*
+ x-destroy-window display window
+ */
+{
+ Display *display;
+ Window window;
+
+ LispObj *odisplay, *owindow;
+
+ owindow = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ if (!CHECKO(owindow, x11Window_t))
+ LispDestroy("%s: cannot convert %s to Window",
+ STRFUN(builtin), STROBJ(owindow));
+ window = (Window)(owindow->data.opaque.data);
+
+ XDestroyWindow(display, window);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_XFlush(LispBuiltin *builtin)
+/*
+ x-flush display
+ */
+{
+ Display *display;
+
+ LispObj *odisplay;
+
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ XFlush(display);
+
+ return (odisplay);
+}
+
+LispObj *
+Lisp_XDrawLine(LispBuiltin *builtin)
+/*
+ x-draw-line display drawable gc x1 y1 x2 y2
+ */
+{
+ Display *display;
+ Drawable drawable;
+ GC gc;
+ int x1, y1, x2, y2;
+
+ LispObj *odisplay, *odrawable, *ogc, *ox1, *oy1, *ox2, *oy2;
+
+ oy2 = ARGUMENT(6);
+ ox2 = ARGUMENT(5);
+ oy1 = ARGUMENT(4);
+ ox1 = ARGUMENT(3);
+ ogc = ARGUMENT(2);
+ odrawable = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ /* XXX correct check when drawing to pixmaps implemented */
+ if (!CHECKO(odrawable, x11Window_t))
+ LispDestroy("%s: cannot convert %s to Drawable",
+ STRFUN(builtin), STROBJ(odrawable));
+ drawable = (Drawable)(odrawable->data.opaque.data);
+
+ if (!CHECKO(ogc, x11GC_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(ogc));
+ gc = (GC)(ogc->data.opaque.data);
+
+ CHECK_FIXNUM(ox1);
+ x1 = FIXNUM_VALUE(ox1);
+
+ CHECK_FIXNUM(oy1);
+ y1 = FIXNUM_VALUE(oy1);
+
+ CHECK_FIXNUM(ox2);
+ x2 = FIXNUM_VALUE(ox2);
+
+ CHECK_FIXNUM(oy2);
+ y2 = FIXNUM_VALUE(oy2);
+
+ XDrawLine(display, drawable, gc, x1, y1, x2, y2);
+
+ return (odrawable);
+}
+
+LispObj *
+Lisp_XBell(LispBuiltin *builtin)
+/*
+ x-bell &optional percent
+ */
+{
+ Display *display;
+ int percent;
+
+ LispObj *odisplay, *opercent;
+
+ opercent = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ if (opercent == UNSPEC)
+ percent = 0;
+ else
+ CHECK_FIXNUM(opercent);
+ else
+ percent = FIXNUM_VALUE(opercent);
+
+ if (percent < -100 || percent > 100)
+ LispDestroy("%s: percent value %d out of range -100 to 100",
+ STRFUN(builtin), percent);
+
+ XBell(display, percent);
+
+ return (odisplay);
+}
+
+LispObj *
+Lisp_XRaiseWindow(LispBuiltin *builtin)
+/*
+ x-raise-window display window
+ */
+{
+ Display *display;
+ Window window;
+
+ LispObj *odisplay, *owindow;
+
+ owindow = ARGUMENT(1);
+ odisplay = ARGUMENT(0);
+
+ if (!CHECKO(odisplay, x11Display_t))
+ LispDestroy("%s: cannot convert %s to Display*",
+ STRFUN(builtin), STROBJ(odisplay));
+ display = (Display*)(odisplay->data.opaque.data);
+
+ if (!CHECKO(owindow, x11Window_t))
+ LispDestroy("%s: cannot convert %s to Window",
+ STRFUN(builtin), STROBJ(owindow));
+ window = (Window)(owindow->data.opaque.data);
+
+ XRaiseWindow(display, window);
+
+ return (owindow);
+}
+
+LispObj *
+Lisp_XWidthOfScreen(LispBuiltin *builtin)
+/*
+ x-width-of-screen screen
+ */
+{
+ LispObj *screen;
+
+ screen = ARGUMENT(0);
+
+ if (!CHECKO(screen, x11Screen_t))
+ LispDestroy("%s: cannot convert %s to Screen*",
+ STRFUN(builtin), STROBJ(screen));
+
+ return (FIXNUM(WidthOfScreen((Screen*)(screen->data.opaque.data))));
+}
+
+LispObj *
+Lisp_XHeightOfScreen(LispBuiltin *builtin)
+/*
+ x-height-of-screen screen
+ */
+{
+ LispObj *screen;
+
+ screen = ARGUMENT(0);
+
+ if (!CHECKO(screen, x11Screen_t))
+ LispDestroy("%s: cannot convert %s to Screen*",
+ STRFUN(builtin), STROBJ(screen));
+
+ return (FIXNUM(HeightOfScreen((Screen*)(screen->data.opaque.data))));
+}
diff --git a/lisp/modules/xaw.c b/lisp/modules/xaw.c
new file mode 100644
index 0000000..c2b372b
--- /dev/null
+++ b/lisp/modules/xaw.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/modules/xaw.c,v 1.14 2002/11/23 08:26:52 paulo Exp $ */
+
+#include <stdlib.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Xaw/AsciiSink.h>
+#include <X11/Xaw/AsciiSrc.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Grip.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/MultiSink.h>
+#include <X11/Xaw/MultiSrc.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/Panner.h>
+#include <X11/Xaw/Porthole.h>
+#include <X11/Xaw/Repeater.h>
+#include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/Simple.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/Sme.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/StripChart.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/TextSink.h>
+#include <X11/Xaw/TextSrc.h>
+#include <X11/Xaw/Tip.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/Tree.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Vendor.h>
+#include "internal.h"
+#include "private.h"
+
+/*
+ * Types
+ */
+typedef struct {
+ LispObj *object;
+ void *data;
+} WidgetData;
+
+/*
+ * Prototypes
+ */
+int xawLoadModule(void);
+void LispXawCleanupCallback(Widget, XtPointer, XtPointer);
+
+/* until a better/smarter interface be written... */
+LispObj *Lisp_XawCoerceToListReturnStruct(LispBuiltin*);
+LispObj *Lisp_XawScrollbarCoerceToReal(LispBuiltin*);
+
+LispObj *Lisp_XawFormDoLayout(LispBuiltin*);
+LispObj *Lisp_XawListChange(LispBuiltin*);
+LispObj *Lisp_XawListHighlight(LispBuiltin*);
+LispObj *Lisp_XawListUnhighlight(LispBuiltin*);
+LispObj *Lisp_XawTextGetSource(LispBuiltin*);
+LispObj *Lisp_XawTextLastPosition(LispBuiltin*);
+LispObj *Lisp_XawTextReplace(LispBuiltin*);
+LispObj *Lisp_XawTextSearch(LispBuiltin*);
+LispObj *Lisp_XawTextGetInsertionPoint(LispBuiltin*);
+LispObj *Lisp_XawTextSetInsertionPoint(LispBuiltin*);
+LispObj *Lisp_XawScrollbarSetThumb(LispBuiltin*);
+
+/*
+ * Initialization
+ */
+
+static LispBuiltin lispbuiltins[] = {
+ {LispFunction, Lisp_XawCoerceToListReturnStruct, "xaw-coerce-to-list-return-struct opaque"},
+ {LispFunction, Lisp_XawScrollbarCoerceToReal, "xaw-scrollbar-coerce-to-real opaque"},
+
+ {LispFunction, Lisp_XawScrollbarSetThumb, "xaw-scrollbar-set-thumb widget top &optional shown"},
+ {LispFunction, Lisp_XawFormDoLayout, "xaw-form-do-layout widget force"},
+ {LispFunction, Lisp_XawListChange, "xaw-list-change widget list &optional longest resize"},
+ {LispFunction, Lisp_XawListHighlight, "xaw-list-highlight widget index"},
+ {LispFunction, Lisp_XawListUnhighlight, "xaw-list-unhighlight widget"},
+ {LispFunction, Lisp_XawTextGetSource, "xaw-text-get-source widget"},
+ {LispFunction, Lisp_XawTextLastPosition, "xaw-text-last-position widget"},
+ {LispFunction, Lisp_XawTextReplace, "xaw-text-replace widget left right text"},
+ {LispFunction, Lisp_XawTextSearch, "xaw-text-search widget direction text"},
+ {LispFunction, Lisp_XawTextGetInsertionPoint, "xaw-text-get-insertion-point widget"},
+ {LispFunction, Lisp_XawTextSetInsertionPoint, "xaw-text-set-insertion-point widget position"},
+};
+
+LispModuleData xawLispModuleData = {
+ LISP_MODULE_VERSION,
+ xawLoadModule
+};
+
+static int xawWidget_t, xawWidgetClass_t, xawListReturnStruct_t, xawFloatp_t;
+static WidgetData **list_data;
+static int num_list_data;
+
+/*
+ * Implementation
+ */
+int
+xawLoadModule(void)
+{
+ int i;
+ char *fname = "XAW-LOAD-MODULE";
+
+ xawWidget_t = LispRegisterOpaqueType("Widget");
+ xawWidgetClass_t = LispRegisterOpaqueType("WidgetClass");
+ xawListReturnStruct_t = LispRegisterOpaqueType("XawListReturnStruct");
+ xawFloatp_t = LispRegisterOpaqueType("float*");
+
+ LispExecute("(DEFSTRUCT XAW-LIST-RETURN-STRUCT STRING INDEX)\n");
+
+ GCDisable();
+ (void)LispSetVariable(ATOM2("ASCII-SINK-OBJECT-CLASS"),
+ OPAQUE(asciiSinkObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("ASCII-SRC-OBJECT-CLASS"),
+ OPAQUE(asciiSinkObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("ASCII-TEXT-WIDGET-CLASS"),
+ OPAQUE(asciiTextWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("BOX-WIDGET-CLASS"),
+ OPAQUE(boxWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("COMMAND-WIDGET-CLASS"),
+ OPAQUE(commandWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("DIALOG-WIDGET-CLASS"),
+ OPAQUE(dialogWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("FORM-WIDGET-CLASS"),
+ OPAQUE(formWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("GRIP-WIDGET-CLASS"),
+ OPAQUE(gripWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("LABEL-WIDGET-CLASS"),
+ OPAQUE(labelWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("LIST-WIDGET-CLASS"),
+ OPAQUE(listWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("MENU-BUTTON-WIDGET-CLASS"),
+ OPAQUE(menuButtonWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("MULTI-SINK-OBJEC-TCLASS"),
+ OPAQUE(multiSinkObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("MULTI-SRC-OBJECT-CLASS"),
+ OPAQUE(multiSrcObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("PANED-WIDGET-CLASS"),
+ OPAQUE(panedWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("PANNER-WIDGET-CLASS"),
+ OPAQUE(pannerWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("PORTHOLE-WIDGET-CLASS"),
+ OPAQUE(portholeWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("REPEATER-WIDGET-CLASS"),
+ OPAQUE(repeaterWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("SCROLLBAR-WIDGET-CLASS"),
+ OPAQUE(scrollbarWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("SIMPLE-MENU-WIDGET-CLASS"),
+ OPAQUE(simpleMenuWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("SIMPLE-WIDGET-CLASS"),
+ OPAQUE(simpleWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("SME-BSB-OBJECT-CLASS"),
+ OPAQUE(smeBSBObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("SME-LINE-OBJECT-CLASS"),
+ OPAQUE(smeLineObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("SME-OBJECT-CLASS"),
+ OPAQUE(smeObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("STRIP-CHART-WIDGET-CLASS"),
+ OPAQUE(stripChartWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("TEXT-WIDGET-CLASS"),
+ OPAQUE(textWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("TEXT-SINKOBJECT-CLASS"),
+ OPAQUE(textSinkObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("TEXT-SRC-OBJECT-CLASS"),
+ OPAQUE(textSrcObjectClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("TIP-WIDGET-CLASS"),
+ OPAQUE(tipWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("TOGGLE-WIDGET-CLASS"),
+ OPAQUE(toggleWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("TREE-WIDGET-CLASS"),
+ OPAQUE(treeWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("VIEWPORT-WIDGET-CLASS"),
+ OPAQUE(viewportWidgetClass, xawWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("VENDOR-SHELL-WIDGET-CLASS"),
+ OPAQUE(vendorShellWidgetClass, xawWidgetClass_t),
+ fname, 0);
+
+ /* return codes of XawTextReplace */
+ (void)LispSetVariable(ATOM2("XAW-REPLACE-ERROR"),
+ INTEGER(XawReplaceError), fname, 0);
+ (void)LispSetVariable(ATOM2("XAW-EDIT-DONE"),
+ INTEGER(XawEditDone), fname, 0);
+ (void)LispSetVariable(ATOM2("XAW-EDIT-ERROR"),
+ INTEGER(XawEditError), fname, 0);
+ (void)LispSetVariable(ATOM2("XAW-POSITION-ERROR"),
+ INTEGER(XawPositionError), fname, 0);
+
+ /* return code of XawTextSearch */
+ (void)LispSetVariable(ATOM2("XAW-TEXT-SEARCH-ERROR"),
+ INTEGER(XawTextSearchError), fname, 0);
+
+ /* enum XawTextScanDirection */
+ (void)LispSetVariable(ATOM2("XAWSD-LEFT"),
+ INTEGER(XawsdLeft), fname, 0);
+ (void)LispSetVariable(ATOM2("XAWSD-RIGHT"),
+ INTEGER(XawsdRight), fname, 0);
+ GCEnable();
+
+ for (i = 0; i < sizeof(lispbuiltins) / sizeof(lispbuiltins[0]); i++)
+ LispAddBuiltinFunction(&lispbuiltins[i]);
+
+ return (1);
+}
+
+void
+LispXawCleanupCallback(Widget w, XtPointer user_data, XtPointer call_data)
+{
+ WidgetData *data = (WidgetData*)user_data;
+
+ UPROTECT(CAR(data->object), data->object);
+ XtFree((XtPointer)data->data);
+ XtFree((XtPointer)data);
+}
+
+LispObj *
+Lisp_XawCoerceToListReturnStruct(LispBuiltin *builtin)
+/*
+ xaw-coerce-to-list-return-struct opaque
+ */
+{
+ LispObj *result, *code, *ocod = COD;
+ XawListReturnStruct *retlist;
+
+ LispObj *opaque;
+
+ opaque = ARGUMENT(0);
+
+ if (!CHECKO(opaque, xawListReturnStruct_t))
+ LispDestroy("%s: cannot convert %s to XawListReturnStruct",
+ STRFUN(builtin), STROBJ(opaque));
+
+ retlist = (XawListReturnStruct*)(opaque->data.opaque.data);
+
+ GCDisable();
+ code = CONS(ATOM("MAKE-XAW-LIST-RETURN-STRUCT"),
+ CONS(KEYWORD("STRING"),
+ CONS(STRING(retlist->string),
+ CONS(KEYWORD("INDEX"),
+ CONS(INTEGER(retlist->list_index), NIL)))));
+ COD = CONS(code, COD);
+ GCEnable();
+
+ result = EVAL(code);
+ COD = ocod;
+
+ return (result);
+}
+
+LispObj *
+Lisp_XawScrollbarCoerceToReal(LispBuiltin *builtin)
+/*
+ xaw-scrollbar-coerce-to-real opaque
+ */
+{
+ LispObj *result;
+ float *floatp;
+ double real;
+
+ LispObj *opaque;
+
+ opaque = ARGUMENT(0);
+
+ if (!CHECKO(opaque, xawFloatp_t))
+ LispDestroy("%s: cannot convert %s to float*",
+ STRFUN(builtin), STROBJ(opaque));
+
+ floatp = (float*)(opaque->data.opaque.data);
+ real = *floatp;
+
+ return (DFLOAT(real));
+}
+
+LispObj *
+Lisp_XawFormDoLayout(LispBuiltin *builtin)
+/*
+ xaw-form-do-layout widget force
+ */
+{
+ int force;
+
+ LispObj *owidget, *oforce;
+
+ oforce = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+
+ force = oforce != NIL;
+ XawFormDoLayout((Widget)(owidget->data.opaque.data), force);
+
+ return (oforce);
+}
+
+LispObj *
+Lisp_XawTextGetSource(LispBuiltin *builtin)
+/*
+ xaw-text-get-source widget
+ */
+{
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+
+ return (OPAQUE(XawTextGetSource((Widget)(owidget->data.opaque.data)),
+ xawWidget_t));
+}
+
+LispObj *
+Lisp_XawTextLastPosition(LispBuiltin *builtin)
+/*
+ xaw-text-last-position widget
+ */
+{
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+
+ return (FIXNUM(XawTextLastPosition((Widget)(owidget->data.opaque.data))));
+}
+
+LispObj *
+Lisp_XawTextGetInsertionPoint(LispBuiltin *builtin)
+/*
+ xaw-text-get-insertion-point widget
+ */
+{
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+
+ return (FIXNUM(XawTextGetInsertionPoint((Widget)(owidget->data.opaque.data))));
+}
+
+LispObj *
+Lisp_XawTextSetInsertionPoint(LispBuiltin *builtin)
+/*
+ xaw-text-set-insertion-point widget position
+ */
+{
+ Widget widget;
+ XawTextPosition position;
+
+ LispObj *owidget, *oposition;
+
+ oposition = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+
+ CHECK_INDEX(oposition);
+ position = (XawTextPosition)FIXNUM_VALUE(oposition);
+
+ XawTextSetInsertionPoint(widget, position);
+
+ return (oposition);
+}
+
+LispObj *
+Lisp_XawTextReplace(LispBuiltin *builtin)
+/*
+ xaw-text-replace widget left right text
+ */
+{
+ Widget widget;
+ XawTextPosition left, right;
+ XawTextBlock block;
+
+ LispObj *owidget, *oleft, *oright, *otext;
+
+ otext = ARGUMENT(3);
+ oright = ARGUMENT(2);
+ oleft = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+
+ CHECK_INDEX(oleft);
+ left = (XawTextPosition)FIXNUM_VALUE(oleft);
+
+ CHECK_INDEX(oright);
+ right = (XawTextPosition)FIXNUM_VALUE(oright);
+
+ CHECK_STRING(otext);
+ block.firstPos = 0;
+ block.ptr = THESTR(otext);
+ block.length = strlen(block.ptr);
+ block.format = FMT8BIT;
+
+ return (FIXNUM(XawTextReplace(widget, left, right, &block)));
+}
+
+LispObj *
+Lisp_XawTextSearch(LispBuiltin *builtin)
+/*
+ xaw-text-search widget direction text
+ */
+{
+ Widget widget;
+ XawTextScanDirection direction;
+ XawTextBlock block;
+
+ LispObj *owidget, *odirection, *otext;
+
+ otext = ARGUMENT(2);
+ odirection = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+
+ CHECK_INDEX(odirection);
+ direction = (XawTextPosition)FIXNUM_VALUE(odirection);
+ if (direction != XawsdLeft && direction != XawsdRight)
+ LispDestroy("%s: %d does not fit in XawTextScanDirection",
+ STRFUN(builtin), direction);
+
+ CHECK_STRING(otext);
+ block.firstPos = 0;
+ block.ptr = THESTR(otext);
+ block.length = strlen(block.ptr);
+ block.format = FMT8BIT;
+
+ return (FIXNUM(XawTextSearch(widget, direction, &block)));
+}
+
+LispObj *
+Lisp_XawListChange(LispBuiltin *builtin)
+/*
+ xaw-list-change widget list &optional longest resize
+ */
+{
+ Widget widget;
+ String *list;
+ int i, nitems;
+ int longest;
+ Boolean resize;
+ LispObj *object;
+ WidgetData *data;
+
+ LispObj *owidget, *olist, *olongest, *oresize;
+
+ oresize = ARGUMENT(3);
+ olongest = ARGUMENT(2);
+ olist = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+
+ CHECK_LIST(olist);
+ for (nitems = 0, object = olist; CONSP(object);
+ ++nitems, object = CDR(object))
+ CHECK_STRING(CAR(object));
+
+ if (olongest != UNSPEC) {
+ CHECK_INDEX(olongest);
+ longest = FIXNUM_VALUE(olongest);
+ }
+ else
+ XtVaGetValues(widget, XtNlongest, &longest, NULL, 0);
+ resize = oresize != UNSPEC && oresize != NIL;
+
+ /* No errors in arguments, build string list */
+ list = (String*)XtMalloc(sizeof(String) * nitems);
+ for (i = 0, object = olist; CONSP(object); i++, object = CDR(object))
+ list[i] = THESTR(CAR(object));
+
+ /* Check if xaw-list-change was already called
+ * for this widget and free previous data */
+ for (i = 0; i < num_list_data; i++)
+ if ((Widget)CAR(list_data[i]->object)->data.opaque.data == widget) {
+ XtRemoveCallback(widget, XtNdestroyCallback,
+ LispXawCleanupCallback, list_data[i]);
+ LispXawCleanupCallback(widget, list_data[i], NULL);
+ break;
+ }
+
+ if (i >= num_list_data) {
+ ++num_list_data;
+ list_data = (WidgetData**)XtRealloc((XtPointer)list_data,
+ sizeof(WidgetData*) * num_list_data);
+ }
+
+ data = (WidgetData*)XtMalloc(sizeof(WidgetData));
+ data->data = list;
+ list_data[i] = data;
+ data->object = CONS(owidget, olist);
+ PROTECT(owidget, data->object);
+ XtAddCallback(widget, XtNdestroyCallback, LispXawCleanupCallback, data);
+
+ XawListChange(widget, list, nitems, longest, resize);
+
+ return (olist);
+}
+
+LispObj *
+Lisp_XawListHighlight(LispBuiltin *builtin)
+/*
+ xaw-list-highlight widget index
+ */
+{
+ Widget widget;
+ int position;
+
+ LispObj *owidget, *oindex;
+
+ oindex = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+
+ CHECK_INDEX(oindex);
+ position = FIXNUM_VALUE(oindex);
+
+ XawListHighlight(widget, position);
+
+ return (oindex);
+}
+
+LispObj *
+Lisp_XawListUnhighlight(LispBuiltin *builtin)
+/*
+ xaw-list-unhighlight widget
+ */
+{
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+
+ XawListUnhighlight((Widget)(owidget->data.opaque.data));
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_XawScrollbarSetThumb(LispBuiltin *builtin)
+/*
+ xaw-scrollbar-set-thumb widget top &optional shown
+ */
+{
+ Widget widget;
+ double top, shown;
+
+ LispObj *owidget, *otop, *oshown;
+
+ oshown = ARGUMENT(2);
+ otop = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xawWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+
+ CHECK_DFLOAT(otop);
+ top = DFLOAT_VALUE(otop);
+
+ if (oshown == UNSPEC)
+ shown = 1.0;
+ else {
+ CHECK_DFLOAT(oshown);
+ shown = DFLOAT_VALUE(oshown);
+ }
+
+ XawScrollbarSetThumb(widget, top, shown);
+
+ return (oshown == UNSPEC ? DFLOAT(shown) : oshown);
+}
diff --git a/lisp/modules/xedit.lsp b/lisp/modules/xedit.lsp
new file mode 100644
index 0000000..87a85c7
--- /dev/null
+++ b/lisp/modules/xedit.lsp
@@ -0,0 +1,560 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/modules/xedit.lsp,v 1.10 2003/01/16 06:25:50 paulo Exp $
+;;
+
+(provide "xedit")
+
+#+debug (make-package "XEDIT" :use '("LISP" "EXT"))
+(in-package "XEDIT")
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; TODO The user should be able to define *auto-modes* prior to the
+;; initialization here in a configuration file, since defvar only binds
+;; the variable if it is unbound or doesn't have a value defined.
+;; *auto-modes* is a list of conses where every car is compiled
+;; to a regexp to match the name of the file being loaded. The caddr is
+;; either a string, a pathname, or a syntax-p.
+;; When loading a file, if the regexp in the car matches, it will check
+;; the caddr value, and if it is a:
+;; string: executes (load "progmodes/<the-string>.lsp")
+;; pathname: executes (load <the-pathhame>)
+;; syntax-p: does nothing, already loaded
+;;
+;; If it fails to load the file, or the returned value is not a
+;; syntax-p, the entry is removed.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defvar *auto-modes* '(
+ ("\\.(c|cc|C|cxx|h|bm|xbm|xpm|l|y)$"
+ "C/C++" "c" . *c-mode*)
+ ("\\.(li?sp|scm)$"
+ "Lisp/Scheme" "lisp" . *lisp-mode*)
+ ("Imakefile|(\\.(cf|rules|tmpl|def|cpp)$)"
+ "X imake" "imake" . *imake-mode*)
+ ("[Mm]akefile.*|\\.mk$"
+ "Makefile" "make" . *make-mode*)
+ ("\\.sh$"
+ "Unix shell" "sh" . *sh-mode*)
+ ("\\.sgml?$"
+ "SGML" "sgml" . *sgml-mode*)
+ ("\\.html?$"
+ "HTML" "html" . *html-mode*)
+ ("\\.(man|\\d)$"
+ "Man page" "man" . *man-mode*)
+ ("app-defaults/\\w+|\\u[A-Za-z0-9_-]+\\.ad"
+ "X resource" "xrdb" . *xrdb-mode*)
+ ("\\<XF86Config[^/]*"
+ "XF86Config" "xconf" . *xconf-mode*)
+ ("\\.spec$"
+ "RPM spec" "rpm" . *rpm-mode*)
+ ("\\<XFree86\\.\\d+\\.log$"
+ "XFree86 log" "xlog" . *xlog-mode*)
+))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Compile the regexps in the *auto-modes* list.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(dolist (mode *auto-modes*)
+ (rplaca mode (re-comp (car mode) :nosub t))
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Find the progmode associated with the given filename.
+;; Returns nil if nothing matches.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun auto-mode (filename &optional symbol &aux syntax)
+ (if (and symbol (symbolp symbol))
+ (if (boundp symbol)
+ (return-from auto-mode (symbol-value symbol))
+ (setq syntax (cddr (find symbol *auto-modes* :key #'cdddr)))
+ )
+ ;; symbol optional argument is not a symbol
+ (do*
+ (
+ (mode *auto-modes* (cdr mode))
+ (regex (caar mode) (caar mode))
+ )
+ ((endp mode))
+
+ ;; only wants to know if the regex match.
+ (when (listp (re-exec regex filename :count 0))
+ (setq syntax (cddar mode) symbol (cdr syntax))
+ (return)
+ )
+ )
+ )
+
+ ;; if file was already loaded
+ (if (and symbol (boundp symbol))
+ (return-from auto-mode (symbol-value symbol))
+ )
+
+ (when (consp syntax)
+ ;; point to the syntax file specification
+ (setq syntax (car syntax))
+
+ ;; try to load the syntax definition file
+ (if (stringp syntax)
+ (load
+ (string-concat
+ (namestring *default-pathname-defaults*)
+ "progmodes/"
+ syntax
+ ".lsp"
+ )
+ )
+ (load syntax)
+ )
+
+ (and symbol (boundp symbol) (symbol-value symbol))
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Data types.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; The main syntax structure, normally, only one should exist per
+;; syntax highlight module.
+;; The structure is defined here so it is not required to load all
+;; the extra data associated with syntax-highlight at initialization
+;; time, and will never be loaded if no syntax-highlight mode is
+;; defined to the files being edited.
+(defstruct syntax
+ name ;; A unique string to identify the syntax mode.
+ ;; Should be the name of the language/file type.
+ options ;; A hash table of options specified for the
+ ;; language.
+
+ ;; Field(s) defined at "compile time"
+ labels ;; Not exactly a list of labels, but all syntax
+ ;; tables for the module.
+ quark ;; A XrmQuark associated with the XawTextPropertyList
+ ;; used by this syntax mode.
+ token-count ;; Number of distinct syntoken structures in
+ ;; the syntax table.
+)
+
+;; Xlfd description, used when combining properties.
+;; Field names are self descriptive.
+;; XXX Fields should be initialized as strings, but fields
+;; that have an integer value should be allowed to
+;; be initialized as such.
+;; Combining properties in supported in Xaw, but not yet in the
+;; syntax highlight code interface. Combining properties allow easier
+;; implementation for markup languages, for example:
+;; <b>bold<i>italic</i></b>
+;; would render "bold" using a bold version of the default font,
+;; and "italic" using a bold and italic version of the default font
+(defstruct xlfd
+ foundry
+ family
+ weight
+ slant
+ setwidth
+ addstyle
+ pixel-size
+ point-size
+ res-x
+ res-y
+ spacing
+ avgwidth
+ registry
+ encoding
+)
+
+
+;; At some time this structure should also hold information for at least:
+;; o fontset
+;; o foreground pixmap
+;; o background pixmap
+;; XXX This is also a TODO in Xaw.
+(defstruct synprop
+ quark ;; XrmQuark identifier of the XawTextProperty
+ ;; structure. This field is filled when "compiling"
+ ;; the syntax-table.
+
+ name ;; String name of property, must be unique per
+ ;; property list.
+ font ;; Optional font string name of property.
+ foreground ;; Optional string representation of foreground color.
+ background ;; Optional string representation of background color.
+ xlfd ;; Optional xlfd structure, when combining properties.
+ ;; Currently combining properties logic not implemented,
+ ;; but fonts may be specified using the xlfd definition.
+
+ ;; Boolean properties.
+ underline ;; Draw a line below the text.
+ overstrike ;; Draw a line over the text.
+
+ ;; XXX Are these working in Xaw?
+ subscript ;; Align text to the bottom of the line.
+ superscript ;; Align text to the top of the line.
+ ;; Note: subscript and superscript only have effect when the text
+ ;; line has different height fonts displayed.
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Utility macro, to create a "special" variable holding
+;; a synprop structure.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defmacro defsynprop (variable name
+ &key font foreground background xlfd underline
+ overstrike subscript superscript)
+ `(progn
+ (proclaim '(special ,variable))
+ (setq ,variable
+ (make-synprop
+ :name ,name
+ :font ,font
+ :foreground ,foreground
+ :background ,background
+ :xlfd ,xlfd
+ :underline ,underline
+ :overstrike ,overstrike
+ :subscript ,subscript
+ :superscript ,superscript
+ )
+ )
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Convert a synprop structure to a string in the format
+;; expected by Xaw.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun synprop-to-string (synprop &aux values booleans xlfd)
+ (if (setq xlfd (synprop-xlfd synprop))
+ (dolist
+ (element
+ `(
+ ("foundry" ,(xlfd-foundry xlfd))
+ ("family" ,(xlfd-family xlfd))
+ ("weight" ,(xlfd-weight xlfd))
+ ("slant" ,(xlfd-slant xlfd))
+ ("setwidth" ,(xlfd-setwidth xlfd))
+ ("addstyle" ,(xlfd-addstyle xlfd))
+ ("pixelsize" ,(xlfd-pixel-size xlfd))
+ ("pointsize" ,(xlfd-point-size xlfd))
+ ("resx" ,(xlfd-res-x xlfd))
+ ("resy" ,(xlfd-res-y xlfd))
+ ("spacing" ,(xlfd-spacing xlfd))
+ ("avgwidth" ,(xlfd-avgwidth xlfd))
+ ("registry" ,(xlfd-registry xlfd))
+ ("encoding" ,(xlfd-encoding xlfd))
+ )
+ )
+ (if (cadr element)
+ (setq values (append values element))
+ )
+ )
+ )
+ (dolist
+ (element
+ `(
+ ("font" ,(synprop-font synprop))
+ ("foreground" ,(synprop-foreground synprop))
+ ("background" ,(synprop-background synprop))
+ )
+ )
+ (if (cadr element)
+ (setq values (append values element))
+ )
+ )
+
+ ;; Boolean attributes. These can be specified in the format
+ ;; <name>=<anything>, but do a nicer output as the format
+ ;; <name> is accepted.
+ (dolist
+ (element
+ `(
+ ("underline" ,(synprop-underline synprop))
+ ("overstrike" ,(synprop-overstrike synprop))
+ ("subscript" ,(synprop-subscript synprop))
+ ("superscript" ,(synprop-superscript synprop))
+ )
+ )
+ (if (cadr element)
+ (setq booleans (append booleans element))
+ )
+ )
+
+ ;; Play with format conditionals, list iteration, and goto, to
+ ;; make resulting string.
+ (format
+ nil
+ "~A~:[~;?~]~:[~3*~;~A=~A~{&~A=~A~}~]~:[~;&~]~:[~2*~;~A~{&~A~*~}~]"
+
+ (synprop-name synprop) ;; ~A
+ (or values booleans) ;; ~:[~;?~]
+ values ;; ~:[
+ (car values) (cadr values) (cddr values) ;; ~A=~A~{&~A=~A~}
+ (and values booleans) ;; ~:[~;&~]
+ booleans ;; ~:[
+ (car booleans) (cddr booleans) ;; ~A~{&~A~*~}
+ )
+)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Use xedit protocol to create a XawTextPropertyList with the
+;; given arguments.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun compile-syntax-property-list (name properties
+ &aux string-properties quark)
+
+ ;; Create a string representation of the properties.
+ (dolist (property properties)
+ (setq
+ string-properties
+ (append
+ string-properties
+ (list (synprop-to-string property))
+ )
+ )
+ )
+
+ (setq
+ string-properties
+ (case (length string-properties)
+ (0 "")
+ (1 (car string-properties))
+ (t (format nil "~A~{,~A~}"
+ (car string-properties)
+ (cdr string-properties)
+ )
+ )
+ )
+ )
+
+#+debug
+ (format *output* "~Cconvert-property-list ~S ~S~%"
+ *escape*
+ name
+ string-properties
+ )
+ (setq quark #-debug (convert-property-list name string-properties)
+ #+debug 0)
+
+ ;; Store the quark for properties not yet "initialized".
+ ;; XXX This is just a call to Xrm{Perm,}StringToQuark, and should
+ ;; be made available if there were a wrapper/interface to
+ ;; that Xlib function.
+ (dolist (property properties)
+ (unless (integerp (synprop-quark property))
+#+debug
+ (format *output* "~Cxrm-string-to-quark ~S~%"
+ *escape*
+ (synprop-name property)
+ )
+ (setf
+ (synprop-quark property)
+#-debug (xrm-string-to-quark (synprop-name property))
+#+debug 0
+ )
+ )
+ )
+
+ quark
+)
+
+
+
+
+#+debug
+(progn
+ (defconstant *escape* #\$)
+
+ (defconstant *output* *standard-output*)
+
+ ;; Recognized identifiers for wrap mode.
+ (defconstant *wrap-modes* '(:never :line :word))
+
+ ;; Recognized identifiers for justification.
+ (defconstant *justifications* '(:left :right :center :full))
+
+ ;; XawTextScanType
+ (defconstant *scan-type*
+ '(:positions :white-space :eol :paragraph :all :alpha-numeric))
+
+ ;; XawTextScanDirection
+ (defconstant *scan-direction* '(:left :right))
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Debugging version of xedit functions.
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (defun clear-entities (left right)
+ (format *output* "~Cclear-entities ~D ~D~%"
+ *escape* left right))
+
+ (defun add-entity (offset length identifier)
+ (format *output* "~Cadd-entity ~D ~D ~D~%"
+ *escape* offset length identifier))
+
+ (defun background (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-background ~S~%" *escape* value)
+ (format *output* "~Cget-background~%" *escape*)))
+
+ (defun foreground (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-foreground ~S~%" *escape* value)
+ (format *output* "~Cget-foreground~%" *escape*)))
+
+ (defun font (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-font ~S~%" *escape* value)
+ (format *output* "~Cget-font~%" *escape*)))
+
+ (defun point (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-point ~D~%" *escape* value)
+ (format *output* "~Cget-point~%" *escape*)))
+
+ (defun point-min ()
+ (format *output* "~Cpoint-min~%" *escape*))
+
+ (defun point-max ()
+ (format *output* "~Cpoint-max~%" *escape*))
+
+ (defun property-list (&optional (quark nil specified))
+ (format *output* "~property-list ~D~%" *escape* quark))
+
+ (defun insert (string)
+ (format *output* "~Cinsert ~S~%" *escape* string))
+
+ (defun read-text (offset length)
+ (format *output* "~Cread-text ~D ~D~%"
+ *escape* offset length))
+
+ (defun replace-text (left right string)
+ (format *output* "~Creplace-text ~D ~D ~S~%"
+ *escape* left right string))
+
+ (defun scan (offset type direction &key (count 1) include)
+ (unless (setq type (position type *scan-type*))
+ (error "SCAN: type must be one of ~A, not ~A"
+ *scan-type* type))
+ (unless (setq direction (position direction *scan-direction*))
+ (error "SCAN: direction must be one of ~A, not ~A"
+ *scan-direction* direction))
+ (format *output* "~Cscan ~D ~D ~D ~D ~D~%"
+ *escape* offset type direction count (if include 1 0)))
+
+ (defun search-forward (string &optional case-sensitive)
+ (format *output* "~Csearch-forward ~S ~D~%"
+ *escape* string (if case-sensitive 1 0)))
+
+ (defun search-backward (string &optional case-sensitive)
+ (format *output* "~Csearch-backward ~S ~D~%"
+ *escape* string (if case-sensitive 1 0)))
+
+ (defun wrap-mode (&optional (value nil specified))
+ (if specified
+ (progn
+ (unless (member value *wrap-modes*)
+ (error "WRAP-MODE: argument must be one of ~A, not ~A"
+ *wrap-modes* value))
+ (format *output* "~Cset-wrap-mode ~S~%"
+ *escape* (string value)))
+ (format *output* "~Cget-wrap-mode~%" *escape*)))
+
+ (defun auto-fill (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-auto-fill ~S~%"
+ *escape* (if value "true" "false"))
+ (format *output* "~Cget-auto-fill~%" *escape*)))
+
+ (defun justification (&optional (value nil specified))
+ (if specified
+ (progn
+ (unless (member value *justifications*)
+ (error "JUSTIFICATION: argument must be one of ~A, not ~A"
+ *justifications* value))
+ (format *output* "~Cset-justification ~S~%"
+ *escape* (string value)))
+ (format *output* "~Cget-justification~%" *escape*)))
+
+ (defun left-column (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-left-column ~D~%" *escape* value)
+ (format *output* "~Cget-left-column~%" *escape*)))
+
+ (defun right-column (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-right-column ~D~%" *escape* value)
+ (format *output* "~Cget-right-column~%" *escape*)))
+
+ (defun vertical-scrollbar (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-vert-scrollbar ~S~%"
+ *escape* (if value "always" "never"))
+ (format *output* "~Cget-vert-scrollbar~%" *escape*)))
+
+ (defun horizontal-scrollbar (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-horiz-scrollbar ~S~%"
+ *escape* (if value "always" "never"))
+ (format *output* "~Cget-horiz-scrollbar~%" *escape*)))
+
+ #|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+ (defun create-buffer (name)
+ (format *output* "~Ccreate-buffer ~S~%" *escape* name))
+
+ (defun remove-buffer (name)
+ (format *output* "~Cremove-buffer ~S~%" *escape* name))
+
+ (defun buffer-name (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-buffer-name ~S~%" *escape* value)
+ (format *output* "~Cget-buffer-name~%" *escape*)))
+
+ (defun buffer-filename (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-buffer-filename ~S~%"
+ *escape* (namestring value))
+ (format *output* "~Cget-buffer-filename~%" *escape*)))
+
+ (defun current-buffer (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-current-buffer ~S~%" *escape* value)
+ (format *output* "~Cget-current-buffer~%" *escape*)))
+
+ (defun other-buffer (&optional (value nil specified))
+ (if specified
+ (format *output* "~Cset-other-buffer ~S~%" *escape* value)
+ (format *output* "~Cget-other-buffer~%" *escape*)))
+ |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||#
+)
diff --git a/lisp/modules/xt.c b/lisp/modules/xt.c
new file mode 100644
index 0000000..13c7ae7
--- /dev/null
+++ b/lisp/modules/xt.c
@@ -0,0 +1,1797 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/modules/xt.c,v 1.19 2002/11/23 08:26:52 paulo Exp $ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include "internal.h"
+#include "private.h"
+
+/*
+ * Types
+ */
+typedef struct {
+ XrmQuark qname;
+ XrmQuark qtype;
+ Cardinal size;
+} ResourceInfo;
+
+typedef struct {
+ WidgetClass widget_class;
+ ResourceInfo **resources;
+ Cardinal num_resources;
+ Cardinal num_cons_resources;
+} ResourceList;
+
+typedef struct {
+ Arg *args;
+ Cardinal num_args;
+} Resources;
+
+typedef struct {
+ LispObj *data;
+ /* data is => (list* widget callback argument) */
+} CallbackArgs;
+
+/*
+ * Prototypes
+ */
+int xtLoadModule(LispMac*);
+void LispXtCleanupCallback(Widget, XtPointer, XtPointer);
+
+void LispXtCallback(Widget, XtPointer, XtPointer);
+void LispXtInputCallback(XtPointer, int*, XtInputId*);
+
+/* a hack... */
+LispObj *Lisp_XtCoerceToWidgetList(LispBuiltin*);
+
+LispObj *Lisp_XtAddCallback(LispBuiltin*);
+LispObj *Lisp_XtAppInitialize(LispBuiltin*);
+LispObj *Lisp_XtAppMainLoop(LispBuiltin*);
+LispObj *Lisp_XtAppAddInput(LispBuiltin*);
+LispObj *Lisp_XtAppPending(LispBuiltin*);
+LispObj *Lisp_XtAppProcessEvent(LispBuiltin*);
+LispObj *Lisp_XtCreateWidget(LispBuiltin*);
+LispObj *Lisp_XtCreateManagedWidget(LispBuiltin*);
+LispObj *Lisp_XtCreatePopupShell(LispBuiltin*);
+LispObj *Lisp_XtDestroyWidget(LispBuiltin*);
+LispObj *Lisp_XtGetKeyboardFocusWidget(LispBuiltin*);
+LispObj *Lisp_XtGetValues(LispBuiltin*);
+LispObj *Lisp_XtManageChild(LispBuiltin*);
+LispObj *Lisp_XtUnmanageChild(LispBuiltin*);
+LispObj *Lisp_XtSetMappedWhenManaged(LispBuiltin*);
+LispObj *Lisp_XtMapWidget(LispBuiltin*);
+LispObj *Lisp_XtName(LispBuiltin*);
+LispObj *Lisp_XtParent(LispBuiltin*);
+LispObj *Lisp_XtUnmapWidget(LispBuiltin*);
+LispObj *Lisp_XtPopup(LispBuiltin*);
+LispObj *Lisp_XtPopdown(LispBuiltin*);
+LispObj *Lisp_XtIsRealized(LispBuiltin*);
+LispObj *Lisp_XtRealizeWidget(LispBuiltin*);
+LispObj *Lisp_XtUnrealizeWidget(LispBuiltin*);
+LispObj *Lisp_XtRemoveInput(LispBuiltin*);
+LispObj *Lisp_XtSetSensitive(LispBuiltin*);
+LispObj *Lisp_XtSetValues(LispBuiltin*);
+LispObj *Lisp_XtWidgetToApplicationContext(LispBuiltin*);
+LispObj *Lisp_XtDisplay(LispBuiltin*);
+LispObj *Lisp_XtDisplayOfObject(LispBuiltin*);
+LispObj *Lisp_XtScreen(LispBuiltin*);
+LispObj *Lisp_XtScreenOfObject(LispBuiltin*);
+LispObj *Lisp_XtSetKeyboardFocus(LispBuiltin*);
+LispObj *Lisp_XtWindow(LispBuiltin*);
+LispObj *Lisp_XtWindowOfObject(LispBuiltin*);
+LispObj *Lisp_XtAddGrab(LispBuiltin*);
+LispObj *Lisp_XtRemoveGrab(LispBuiltin*);
+LispObj *Lisp_XtAppGetExitFlag(LispBuiltin*);
+LispObj *Lisp_XtAppSetExitFlag(LispBuiltin*);
+
+LispObj *LispXtCreateWidget(LispBuiltin*, int);
+
+static Resources *LispConvertResources(LispObj*, Widget,
+ ResourceList*, ResourceList*);
+static void LispFreeResources(Resources*);
+
+static int bcmp_action_resource(_Xconst void*, _Xconst void*);
+static ResourceInfo *GetResourceInfo(char*, ResourceList*, ResourceList*);
+static ResourceList *GetResourceList(WidgetClass);
+static int bcmp_action_resource_list(_Xconst void*, _Xconst void*);
+static ResourceList *FindResourceList(WidgetClass);
+static int qcmp_action_resource_list(_Xconst void*, _Xconst void*);
+static ResourceList *CreateResourceList(WidgetClass);
+static int qcmp_action_resource(_Xconst void*, _Xconst void*);
+static void BindResourceList(ResourceList*);
+
+static void PopdownAction(Widget, XEvent*, String*, Cardinal*);
+static void QuitAction(Widget, XEvent*, String*, Cardinal*);
+
+/*
+ * Initialization
+ */
+static LispBuiltin lispbuiltins[] = {
+ {LispFunction, Lisp_XtCoerceToWidgetList, "xt-coerce-to-widget-list number opaque"},
+
+ {LispFunction, Lisp_XtAddGrab, "xt-add-grab widget exclusive spring-loaded"},
+ {LispFunction, Lisp_XtAddCallback, "xt-add-callback widget callback-name callback &optional client-data"},
+ {LispFunction, Lisp_XtAppAddInput, "xt-app-add-input app-context fileno condition function &optional client-data"},
+ {LispFunction, Lisp_XtAppInitialize, "xt-app-initialize app-context-return application-class &optional options fallback-resources"},
+ {LispFunction, Lisp_XtAppPending, "xt-app-pending app-context"},
+ {LispFunction, Lisp_XtAppMainLoop, "xt-app-main-loop app-context"},
+ {LispFunction, Lisp_XtAppProcessEvent, "xt-app-process-event app-context &optional mask"},
+ {LispFunction, Lisp_XtAppGetExitFlag, "xt-app-get-exit-flag app-context"},
+ {LispFunction, Lisp_XtAppSetExitFlag, "xt-app-set-exit-flag app-context"},
+ {LispFunction, Lisp_XtCreateManagedWidget, "xt-create-managed-widget name widget-class parent &optional arguments"},
+ {LispFunction, Lisp_XtCreateWidget, "xt-create-widget name widget-class parent &optional arguments"},
+ {LispFunction, Lisp_XtCreatePopupShell, "xt-create-popup-shell name widget-class parent &optional arguments"},
+ {LispFunction, Lisp_XtDestroyWidget, "xt-destroy-widget widget"},
+ {LispFunction, Lisp_XtGetKeyboardFocusWidget, "xt-get-keyboard-focus-widget widget"},
+ {LispFunction, Lisp_XtGetValues, "xt-get-values widget arguments"},
+ {LispFunction, Lisp_XtManageChild, "xt-manage-child widget"},
+ {LispFunction, Lisp_XtName, "xt-name widget"},
+ {LispFunction, Lisp_XtUnmanageChild, "xt-unmanage-child widget"},
+ {LispFunction, Lisp_XtMapWidget, "xt-map-widget widget"},
+ {LispFunction, Lisp_XtUnmapWidget, "xt-unmap-widget widget"},
+ {LispFunction, Lisp_XtSetMappedWhenManaged, "xt-set-mapped-when-managed widget map-when-managed"},
+ {LispFunction, Lisp_XtParent, "xt-parent widget"},
+ {LispFunction, Lisp_XtPopup, "xt-popup widget grab-kind"},
+ {LispFunction, Lisp_XtPopdown, "xt-popdown widget"},
+ {LispFunction, Lisp_XtIsRealized, "xt-is-realized widget"},
+ {LispFunction, Lisp_XtRealizeWidget, "xt-realize-widget widget"},
+ {LispFunction, Lisp_XtUnrealizeWidget, "xt-unrealize-widget widget"},
+ {LispFunction, Lisp_XtRemoveInput, "xt-remove-input input"},
+ {LispFunction, Lisp_XtRemoveGrab, "xt-remove-grab widget"},
+ {LispFunction, Lisp_XtSetKeyboardFocus, "xt-set-keyboard-focus widget descendant"},
+ {LispFunction, Lisp_XtSetSensitive, "xt-set-sensitive widget sensitive"},
+ {LispFunction, Lisp_XtSetValues, "xt-set-values widget arguments"},
+ {LispFunction, Lisp_XtWidgetToApplicationContext, "xt-widget-to-application-context widget"},
+ {LispFunction, Lisp_XtDisplay, "xt-display widget"},
+ {LispFunction, Lisp_XtDisplayOfObject, "xt-display-of-object object"},
+ {LispFunction, Lisp_XtScreen, "xt-screen widget"},
+ {LispFunction, Lisp_XtScreenOfObject, "xt-screen-of-object object"},
+ {LispFunction, Lisp_XtWindow, "xt-window widget"},
+ {LispFunction, Lisp_XtWindowOfObject, "xt-window-of-object object"},
+};
+
+LispModuleData xtLispModuleData = {
+ LISP_MODULE_VERSION,
+ xtLoadModule,
+};
+
+static ResourceList **resource_list;
+static Cardinal num_resource_list;
+
+static Atom delete_window;
+static int xtAppContext_t, xtWidget_t, xtWidgetClass_t, xtWidgetList_t,
+ xtInputId_t, xtDisplay_t, xtScreen_t, xtWindow_t;
+
+static XtActionsRec actions[] = {
+ {"xt-popdown", PopdownAction},
+ {"xt-quit", QuitAction},
+};
+
+static XrmQuark qCardinal, qInt, qString, qWidget, qFloat;
+
+static CallbackArgs **input_list;
+static Cardinal num_input_list, size_input_list;
+
+/*
+ * Implementation
+ */
+int
+xtLoadModule(void)
+{
+ int i;
+ char *fname = "XT-LOAD-MODULE";
+
+ xtAppContext_t = LispRegisterOpaqueType("XtAppContext");
+ xtWidget_t = LispRegisterOpaqueType("Widget");
+ xtWidgetClass_t = LispRegisterOpaqueType("WidgetClass");
+ xtWidgetList_t = LispRegisterOpaqueType("WidgetList");
+ xtInputId_t = LispRegisterOpaqueType("XtInputId");
+ xtDisplay_t = LispRegisterOpaqueType("Display*");
+ xtScreen_t = LispRegisterOpaqueType("Screen*");
+ xtWindow_t = LispRegisterOpaqueType("Window");
+
+ LispExecute("(DEFSTRUCT XT-WIDGET-LIST NUM-CHILDREN CHILDREN)\n");
+
+ GCDisable();
+ (void)LispSetVariable(ATOM2("CORE-WIDGET-CLASS"),
+ OPAQUE(coreWidgetClass, xtWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("COMPOSITE-WIDGET-CLASS"),
+ OPAQUE(compositeWidgetClass, xtWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("CONSTRAINT-WIDGET-CLASS"),
+ OPAQUE(constraintWidgetClass, xtWidgetClass_t),
+ fname, 0);
+ (void)LispSetVariable(ATOM2("TRANSIENT-SHELL-WIDGET-CLASS"),
+ OPAQUE(transientShellWidgetClass, xtWidgetClass_t),
+ fname, 0);
+
+ /* parameters for XtPopup */
+ (void)LispSetVariable(ATOM2("XT-GRAB-EXCLUSIVE"),
+ INTEGER(XtGrabExclusive), fname, 0);
+ (void)LispSetVariable(ATOM2("XT-GRAB-NONE"),
+ INTEGER(XtGrabNone), fname, 0);
+ (void)LispSetVariable(ATOM2("XT-GRAB-NONE-EXCLUSIVE"),
+ INTEGER(XtGrabNonexclusive), fname, 0);
+
+ /* parameters for XtAppProcessEvent */
+ (void)LispSetVariable(ATOM2("XT-IM-XEVENT"),
+ INTEGER(XtIMXEvent), fname, 0);
+ (void)LispSetVariable(ATOM2("XT-IM-TIMER"),
+ INTEGER(XtIMTimer), fname, 0);
+ (void)LispSetVariable(ATOM2("XT-IM-ALTERNATE-INPUT"),
+ INTEGER(XtIMAlternateInput), fname, 0);
+ (void)LispSetVariable(ATOM2("XT-IM-SIGNAL"),
+ INTEGER(XtIMSignal), fname, 0);
+ (void)LispSetVariable(ATOM2("XT-IM-ALL"),
+ INTEGER(XtIMAll), fname, 0);
+
+ /* parameters for XtAppAddInput */
+ (void)LispSetVariable(ATOM2("XT-INPUT-READ-MASK"),
+ INTEGER(XtInputReadMask), fname, 0);
+ (void)LispSetVariable(ATOM2("XT-INPUT-WRITE-MASK"),
+ INTEGER(XtInputWriteMask), fname, 0);
+ (void)LispSetVariable(ATOM2("XT-INPUT-EXCEPT-MASK"),
+ INTEGER(XtInputExceptMask), fname, 0);
+ GCEnable();
+
+ qCardinal = XrmPermStringToQuark(XtRCardinal);
+ qInt = XrmPermStringToQuark(XtRInt);
+ qString = XrmPermStringToQuark(XtRString);
+ qWidget = XrmPermStringToQuark(XtRWidget);
+ qFloat = XrmPermStringToQuark(XtRFloat);
+
+ for (i = 0; i < sizeof(lispbuiltins) / sizeof(lispbuiltins[0]); i++)
+ LispAddBuiltinFunction(&lispbuiltins[i]);
+
+ return (1);
+}
+
+void
+LispXtCallback(Widget w, XtPointer user_data, XtPointer call_data)
+{
+ CallbackArgs *args = (CallbackArgs*)user_data;
+ LispObj *code, *ocod = COD;
+
+ GCDisable();
+ /* callback name */ /* reall caller */
+ code = CONS(CDR(CDR(args->data)), CONS(OPAQUE(w, xtWidget_t),
+ CONS(CAR(CDR(args->data)), CONS(OPAQUE(call_data, 0), NIL))));
+ /* user arguments */
+ COD = CONS(code, COD);
+ GCEnable();
+
+ (void)EVAL(code);
+ COD = ocod;
+}
+
+
+void
+LispXtCleanupCallback(Widget w, XtPointer user_data, XtPointer call_data)
+{
+ CallbackArgs *args = (CallbackArgs*)user_data;
+
+ UPROTECT(CAR(args->data), args->data);
+ XtFree((XtPointer)args);
+}
+
+void
+LispXtInputCallback(XtPointer closure, int *source, XtInputId *id)
+{
+ CallbackArgs *args = (CallbackArgs*)closure;
+ LispObj *code, *ocod = COD;
+
+ GCDisable();
+ /* callback name */ /* user arguments */
+ code = CONS(CDR(CDR(args->data)), CONS(CAR(CDR(args->data)),
+ CONS(INTEGER(*source), CONS(CAR(args->data), NIL))));
+ /* input source */ /* input id */
+ COD = CONS(code, COD);
+ GCEnable();
+
+ (void)EVAL(code);
+ COD = ocod;
+}
+
+LispObj *
+Lisp_XtCoerceToWidgetList(LispBuiltin *builtin)
+/*
+ xt-coerce-to-widget-list number opaque
+ */
+{
+ int i;
+ WidgetList children;
+ Cardinal num_children;
+ LispObj *cons, *widget_list, *result;
+
+ LispObj *onumber, *opaque;
+
+ opaque = ARGUMENT(1);
+ onumber = ARGUMENT(0);
+
+ CHECK_INDEX(onumber);
+ num_children = FIXNUM_VALUE(onumber);
+
+ if (!CHECKO(opaque, xtWidgetList_t))
+ LispDestroy("%s: cannot convert %s to WidgetList",
+ STRFUN(builtin), STROBJ(opaque));
+ children = (WidgetList)(opaque->data.opaque.data);
+
+ GCDisable();
+ widget_list = cons = NIL;
+ for (i = 0; i < num_children; i++) {
+ result = CONS(OPAQUE(children[i], xtWidget_t), NIL);
+ if (widget_list == NIL)
+ widget_list = cons = result;
+ else {
+ RPLACD(cons, result);
+ cons = CDR(cons);
+ }
+ }
+
+ result = APPLY(ATOM("MAKE-XT-WIDGET-LIST"),
+ CONS(KEYWORD("NUM-CHILDREN"),
+ CONS(INTEGER(num_children),
+ CONS(KEYWORD("CHILDREN"),
+ CONS(widget_list, NIL)))));
+ GCEnable();
+
+ return (result);
+}
+
+LispObj *
+Lisp_XtAddCallback(LispBuiltin *builtin)
+/*
+ xt-add-callback widget callback-name callback &optional client-data
+ */
+{
+ CallbackArgs *arguments;
+ LispObj *data;
+
+ LispObj *widget, *callback_name, *callback, *client_data;
+
+ client_data = ARGUMENT(3);
+ callback = ARGUMENT(2);
+ callback_name = ARGUMENT(1);
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+
+ CHECK_STRING(callback_name);
+ if (!SYMBOLP(callback) && callback->type != LispLambda_t)
+ LispDestroy("%s: %s cannot be used as a callback",
+ STRFUN(builtin), STROBJ(callback));
+
+ if (client_data == UNSPEC)
+ client_data = NIL;
+
+ data = CONS(widget, CONS(client_data, callback));
+ PROTECT(widget, data);
+
+ arguments = XtNew(CallbackArgs);
+ arguments->data = data;
+
+ XtAddCallback((Widget)(widget->data.opaque.data), THESTR(callback_name),
+ LispXtCallback, (XtPointer)arguments);
+ XtAddCallback((Widget)(widget->data.opaque.data), XtNdestroyCallback,
+ LispXtCleanupCallback, (XtPointer)arguments);
+
+ return (client_data);
+}
+
+LispObj *
+Lisp_XtAppAddInput(LispBuiltin *builtin)
+/*
+ xt-app-add-input app-context fileno condition function &optional client-data
+ */
+{
+ LispObj *data, *input;
+ XtAppContext appcon;
+ int source, condition;
+ CallbackArgs *arguments;
+ XtInputId id;
+
+ LispObj *app_context, *fileno, *ocondition, *function, *client_data;
+
+ client_data = ARGUMENT(4);
+ function = ARGUMENT(3);
+ ocondition = ARGUMENT(2);
+ fileno = ARGUMENT(1);
+ app_context = ARGUMENT(0);
+
+ if (!CHECKO(app_context, xtAppContext_t))
+ LispDestroy("%s: cannot convert %s to XtAppContext",
+ STRFUN(builtin), STROBJ(app_context));
+ appcon = (XtAppContext)(app_context->data.opaque.data);
+
+ CHECK_LONGINT(fileno);
+ source = LONGINT_VALUE(fileno);
+
+ CHECK_FIXNUM(ocondition);
+ condition = FIXNUM_VALUE(ocondition);
+
+ if (!SYMBOLP(function) && function->type != LispLambda_t)
+ LispDestroy("%s: %s cannot be used as a callback",
+ STRFUN(builtin), STROBJ(function));
+
+ /* client data optional */
+ if (client_data == UNSPEC)
+ client_data = NIL;
+
+ data = CONS(NIL, CONS(client_data, function));
+
+ arguments = XtNew(CallbackArgs);
+ arguments->data = data;
+
+ id = XtAppAddInput(appcon, source, (XtPointer)condition,
+ LispXtInputCallback, (XtPointer)arguments);
+ GCDisable();
+ input = OPAQUE(id, xtInputId_t);
+ GCEnable();
+ RPLACA(data, input);
+ PROTECT(input, data);
+
+ if (num_input_list + 1 >= size_input_list) {
+ ++size_input_list;
+ input_list = (CallbackArgs**)
+ XtRealloc((XtPointer)input_list,
+ sizeof(CallbackArgs*) * size_input_list);
+ }
+ input_list[num_input_list++] = arguments;
+
+ return (input);
+}
+
+LispObj *
+Lisp_XtRemoveInput(LispBuiltin *builtin)
+/*
+ xt-remove-input input
+ */
+{
+ int i;
+ XtInputId id;
+ CallbackArgs *args;
+
+ LispObj *input;
+
+ input = ARGUMENT(0);
+
+ if (!CHECKO(input, xtInputId_t))
+ LispDestroy("%s: cannot convert %s to XtInputId",
+ STRFUN(builtin), STROBJ(input));
+
+ id = (XtInputId)(input->data.opaque.data);
+ for (i = 0; i < num_input_list; i++) {
+ args = input_list[i];
+ if (id == (XtInputId)(CAR(args->data)->data.opaque.data)) {
+ UPROTECT(CAR(args->data), args->data);
+ XtFree((XtPointer)args);
+
+ if (i + 1 < num_input_list)
+ memmove(input_list + i, input_list + i + 1,
+ sizeof(CallbackArgs*) * (num_input_list - i - 1));
+ --num_input_list;
+
+ XtRemoveInput(id);
+
+ return (T);
+ }
+ }
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_XtAppInitialize(LispBuiltin *builtin)
+/*
+ xt-app-initialize app-context-return application-class &optional options fallback-resources
+ */
+{
+ XtAppContext appcon;
+ Widget shell;
+ int zero = 0;
+ Resources *resources = NULL;
+ String *fallback = NULL;
+
+ LispObj *app_context_return, *application_class,
+ *options, *fallback_resources;
+
+ fallback_resources = ARGUMENT(3);
+ options = ARGUMENT(2);
+ application_class = ARGUMENT(1);
+ app_context_return = ARGUMENT(0);
+
+ CHECK_SYMBOL(app_context_return);
+ CHECK_STRING(application_class);
+ CHECK_LIST(options);
+
+ /* check fallback resources, if given */
+ if (fallback_resources != UNSPEC) {
+ LispObj *string;
+ int count;
+
+ CHECK_CONS(fallback_resources);
+ for (string = fallback_resources, count = 0; CONS_P(string);
+ string = CDR(string), count++)
+ CHECK_STRING(CAR(string));
+
+ /* fallback resources was correctly specified */
+ fallback = LispMalloc(sizeof(String) * (count + 1));
+ for (string = fallback_resources, count = 0; CONS_P(string);
+ string = CDR(string), count++)
+ fallback[count] = THESTR(CAR(string));
+ fallback[count] = NULL;
+ }
+
+ shell = XtAppInitialize(&appcon, THESTR(application_class), NULL,
+ 0, &zero, NULL, fallback, NULL, 0);
+ if (fallback)
+ LispFree(fallback);
+ (void)LispSetVariable(app_context_return,
+ OPAQUE(appcon, xtAppContext_t),
+ STRFUN(builtin), 0);
+
+ XtAppAddActions(appcon, actions, XtNumber(actions));
+
+ if (options != UNSPEC) {
+ resources = LispConvertResources(options, shell,
+ GetResourceList(XtClass(shell)),
+ NULL);
+ if (resources) {
+ XtSetValues(shell, resources->args, resources->num_args);
+ LispFreeResources(resources);
+ }
+ }
+
+ return (OPAQUE(shell, xtWidget_t));
+}
+
+LispObj *
+Lisp_XtAppMainLoop(LispBuiltin *builtin)
+/*
+ xt-app-main-loop app-context
+ */
+{
+ LispObj *app_context;
+
+ app_context = ARGUMENT(0);
+
+ if (!CHECKO(app_context, xtAppContext_t))
+ LispDestroy("%s: cannot convert %s to XtAppContext",
+ STRFUN(builtin), STROBJ(app_context));
+
+ XtAppMainLoop((XtAppContext)(app_context->data.opaque.data));
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_XtAppPending(LispBuiltin *builtin)
+/*
+ xt-app-pending app-context
+ */
+{
+ LispObj *app_context;
+
+ app_context = ARGUMENT(0);
+
+ if (!CHECKO(app_context, xtAppContext_t))
+ LispDestroy("%s: cannot convert %s to XtAppContext",
+ STRFUN(builtin), STROBJ(app_context));
+
+ return (INTEGER(
+ XtAppPending((XtAppContext)(app_context->data.opaque.data))));
+}
+
+LispObj *
+Lisp_XtAppProcessEvent(LispBuiltin *builtin)
+/*
+ xt-app-process-event app-context &optional mask
+ */
+{
+ XtInputMask mask;
+ XtAppContext appcon;
+
+ LispObj *app_context, *omask;
+
+ omask = ARGUMENT(1);
+ app_context = ARGUMENT(0);
+
+ if (!CHECKO(app_context, xtAppContext_t))
+ LispDestroy("%s: cannot convert %s to XtAppContext",
+ STRFUN(builtin), STROBJ(app_context));
+
+ appcon = (XtAppContext)(app_context->data.opaque.data);
+ if (omask == UNSPEC)
+ mask = XtIMAll;
+ else {
+ CHECK_FIXNUM(omask);
+ mask = FIXNUM_VALUE(omask);
+ }
+
+ if (mask != (mask & XtIMAll))
+ LispDestroy("%s: %d does not fit in XtInputMask %d",
+ STRFUN(builtin), mask);
+
+ if (mask)
+ XtAppProcessEvent(appcon, mask);
+
+ return (omask == NIL ? FIXNUM(mask) : omask);
+}
+
+LispObj *
+Lisp_XtRealizeWidget(LispBuiltin *builtin)
+/*
+ xt-realize-widget widget
+ */
+{
+ Widget widget;
+
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+ XtRealizeWidget(widget);
+
+ if (XtIsSubclass(widget, shellWidgetClass)) {
+ if (!delete_window)
+ delete_window = XInternAtom(XtDisplay(widget),
+ "WM_DELETE_WINDOW", False);
+ (void)XSetWMProtocols(XtDisplay(widget), XtWindow(widget),
+ &delete_window, 1);
+ }
+
+ return (owidget);
+}
+
+LispObj *
+Lisp_XtUnrealizeWidget(LispBuiltin *builtin)
+/*
+ xt-unrealize-widget widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+
+ XtUnrealizeWidget((Widget)(widget->data.opaque.data));
+
+ return (widget);
+}
+
+LispObj *
+Lisp_XtIsRealized(LispBuiltin *builtin)
+/*
+ xt-is-realized widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+
+ return (XtIsRealized((Widget)(widget->data.opaque.data)) ? T : NIL);
+}
+
+LispObj *
+Lisp_XtDestroyWidget(LispBuiltin *builtin)
+/*
+ xt-destroy-widget widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+
+ XtDestroyWidget((Widget)(widget->data.opaque.data));
+
+ return (NIL);
+}
+
+#define UNMANAGED 0
+#define MANAGED 1
+#define SHELL 2
+LispObj *
+Lisp_XtCreateWidget(LispBuiltin *builtin)
+/*
+ xt-create-widget name widget-class parent &optional arguments
+ */
+{
+ return (LispXtCreateWidget(builtin, UNMANAGED));
+}
+
+LispObj *
+Lisp_XtCreateManagedWidget(LispBuiltin *builtin)
+/*
+ xt-create-managed-widget name widget-class parent &optional arguments
+ */
+{
+ return (LispXtCreateWidget(builtin, MANAGED));
+}
+
+LispObj *
+Lisp_XtCreatePopupShell(LispBuiltin *builtin)
+/*
+ xt-create-popup-shell name widget-class parent &optional arguments
+ */
+{
+ return (LispXtCreateWidget(builtin, SHELL));
+}
+
+LispObj *
+LispXtCreateWidget(LispBuiltin *builtin, int options)
+/*
+ xt-create-widget name widget-class parent &optional arguments
+ xt-create-managed-widget name widget-class parent &optional arguments
+ xt-create-popup-shell name widget-class parent &optional arguments
+ */
+{
+ char *name;
+ WidgetClass widget_class;
+ Widget widget, parent;
+ Resources *resources = NULL;
+
+ LispObj *oname, *owidget_class, *oparent, *arguments;
+
+ arguments = ARGUMENT(3);
+ oparent = ARGUMENT(2);
+ owidget_class = ARGUMENT(1);
+ oname = ARGUMENT(0);
+
+ CHECK_STRING(oname);
+ name = THESTR(oname);
+
+ if (!CHECKO(owidget_class, xtWidgetClass_t))
+ LispDestroy("%s: cannot convert %s to WidgetClass",
+ STRFUN(builtin), STROBJ(owidget_class));
+ widget_class = (WidgetClass)(owidget_class->data.opaque.data);
+
+ if (!CHECKO(oparent, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(oparent));
+ parent = (Widget)(oparent->data.opaque.data);
+
+ CHECK_LIST(arguments);
+
+ if (options == SHELL)
+ widget = XtCreatePopupShell(name, widget_class, parent, NULL, 0);
+ else
+ widget = XtCreateWidget(name, widget_class, parent, NULL, 0);
+
+ if (arguments == UNSPEC || arguments == NIL)
+ resources = NULL;
+ else {
+ resources = LispConvertResources(arguments, widget,
+ GetResourceList(widget_class),
+ GetResourceList(XtClass(parent)));
+ XtSetValues(widget, resources->args, resources->num_args);
+ }
+ if (options == MANAGED)
+ XtManageChild(widget);
+ if (resources)
+ LispFreeResources(resources);
+
+ return (OPAQUE(widget, xtWidget_t));
+}
+
+LispObj *
+Lisp_XtGetKeyboardFocusWidget(LispBuiltin *builtin)
+/*
+ xt-get-keyboard-focus-widget widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ return (OPAQUE(XtGetKeyboardFocusWidget((Widget)(widget->data.opaque.data)),
+ xtWidget_t));
+}
+
+LispObj *
+Lisp_XtGetValues(LispBuiltin *builtin)
+/*
+ xt-get-values widget arguments
+ */
+{
+ Arg args[1];
+ Widget widget;
+ ResourceList *rlist, *plist;
+ ResourceInfo *resource;
+ LispObj *list, *object = NIL, *result, *cons = NIL;
+ char c1;
+ short c2;
+ int c4;
+#ifdef LONG64
+ long c8;
+#endif
+
+ LispObj *owidget, *arguments;
+
+ arguments = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (arguments == NIL)
+ return (NIL);
+
+ if (!CHECKO(owidget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+ CHECK_CONS(arguments);
+
+ rlist = GetResourceList(XtClass(widget));
+ plist = XtParent(widget) ?
+ GetResourceList(XtClass(XtParent(widget))) : NULL;
+
+ GCDisable();
+ result = NIL;
+ for (list = arguments; CONS_P(list); list = CDR(list)) {
+ CHECK_STRING(CAR(list));
+ if ((resource = GetResourceInfo(THESTR(CAR(list)), rlist, plist))
+ == NULL) {
+ int i;
+ Widget child;
+
+ for (i = 0; i < rlist->num_resources; i++) {
+ if (rlist->resources[i]->qtype == qWidget) {
+ XtSetArg(args[0],
+ XrmQuarkToString(rlist->resources[i]->qname),
+ &child);
+ XtGetValues(widget, args, 1);
+ if (child && XtParent(child) == widget) {
+ resource =
+ GetResourceInfo(THESTR(CAR(list)),
+ GetResourceList(XtClass(child)),
+ NULL);
+ if (resource)
+ break;
+ }
+ }
+ }
+ if (resource == NULL) {
+ LispMessage("%s: resource %s not available",
+ STRFUN(builtin), THESTR(CAR(list)));
+ continue;
+ }
+ }
+ switch (resource->size) {
+ case 1:
+ XtSetArg(args[0], THESTR(CAR(list)), &c1);
+ break;
+ case 2:
+ XtSetArg(args[0], THESTR(CAR(list)), &c2);
+ break;
+ case 4:
+ XtSetArg(args[0], THESTR(CAR(list)), &c4);
+ break;
+#ifdef LONG64
+ case 1:
+ XtSetArg(args[0], THESTR(CAR(list)), &c8);
+ break;
+#endif
+ }
+ XtGetValues(widget, args, 1);
+
+ /* special resources */
+ if (resource->qtype == qString) {
+#ifdef LONG64
+ object = CONS(CAR(list), STRING(c8));
+#else
+ object = CONS(CAR(list), STRING(c4));
+#endif
+ }
+ else if (resource->qtype == qCardinal || resource->qtype == qInt) {
+#ifdef LONG64
+ if (sizeof(int) == 8)
+ object = CONS(CAR(list), INTEGER(c8));
+ else
+#endif
+ object = CONS(CAR(list), INTEGER(c4));
+ }
+ else {
+ switch (resource->size) {
+ case 1:
+ object = CONS(CAR(list), OPAQUE(c1, 0));
+ break;
+ case 2:
+ object = CONS(CAR(list), OPAQUE(c2, 0));
+ break;
+ case 4:
+ object = CONS(CAR(list), OPAQUE(c4, 0));
+ break;
+#ifdef LONG64
+ case 8:
+ object = CONS(CAR(list), OPAQUE(c8, 0));
+ break;
+#endif
+ }
+ }
+
+ if (result == NIL)
+ result = cons = CONS(object, NIL);
+ else {
+ RPLACD(cons, CONS(object, NIL));
+ cons = CDR(cons);
+ }
+ }
+ GCEnable();
+
+ return (result);
+}
+
+LispObj *
+Lisp_XtManageChild(LispBuiltin *builtin)
+/*
+ xt-manage-child widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ XtManageChild((Widget)(widget->data.opaque.data));
+
+ return (widget);
+}
+
+LispObj *
+Lisp_XtUnmanageChild(LispBuiltin *builtin)
+/*
+ xt-unmanage-child widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ XtUnmanageChild((Widget)(widget->data.opaque.data));
+
+ return (widget);
+}
+
+LispObj *
+Lisp_XtMapWidget(LispBuiltin *builtin)
+/*
+ xt-map-widget widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ XtMapWidget((Widget)(widget->data.opaque.data));
+
+ return (widget);
+}
+
+LispObj *
+Lisp_XtUnmapWidget(LispBuiltin *builtin)
+/*
+ xt-unmap-widget widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ XtUnmapWidget((Widget)(widget->data.opaque.data));
+
+ return (widget);
+}
+
+LispObj *
+Lisp_XtSetMappedWhenManaged(LispBuiltin *builtin)
+/*
+ xt-set-mapped-when-managed widget map-when-managed
+ */
+{
+ LispObj *widget, *map_when_managed;
+
+ map_when_managed = ARGUMENT(1);
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+
+ XtSetMappedWhenManaged((Widget)(widget->data.opaque.data),
+ map_when_managed != NIL);
+
+ return (map_when_managed);
+}
+
+LispObj *
+Lisp_XtPopup(LispBuiltin *builtin)
+/*
+ xt-popup widget grab-kind
+ */
+{
+ XtGrabKind kind;
+
+ LispObj *widget, *grab_kind;
+
+ grab_kind = ARGUMENT(1);
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ CHECK_INDEX(grab_kind);
+ kind = (XtGrabKind)FIXNUM_VALUE(grab_kind);
+ if (kind != XtGrabExclusive && kind != XtGrabNone &&
+ kind != XtGrabNonexclusive)
+ LispDestroy("%s: %d does not fit in XtGrabKind",
+ STRFUN(builtin), kind);
+ XtPopup((Widget)(widget->data.opaque.data), kind);
+
+ return (grab_kind);
+}
+
+LispObj *
+Lisp_XtPopdown(LispBuiltin *builtin)
+/*
+ xt-popdown widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ XtPopdown((Widget)(widget->data.opaque.data));
+
+ return (widget);
+}
+
+LispObj *
+Lisp_XtSetKeyboardFocus(LispBuiltin *builtin)
+/*
+ xt-set-keyboard-focus widget descendant
+ */
+{
+ LispObj *widget, *descendant;
+
+ descendant = ARGUMENT(1);
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ if (!CHECKO(descendant, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(descendant));
+ XtSetKeyboardFocus((Widget)(widget->data.opaque.data),
+ (Widget)(descendant->data.opaque.data));
+
+ return (widget);
+}
+
+LispObj *
+Lisp_XtSetSensitive(LispBuiltin *builtin)
+/*
+ xt-set-sensitive widget sensitive
+ */
+{
+ LispObj *widget, *sensitive;
+
+ sensitive = ARGUMENT(1);
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+ XtSetSensitive((Widget)(widget->data.opaque.data), sensitive != NIL);
+
+ return (sensitive);
+}
+
+LispObj *
+Lisp_XtSetValues(LispBuiltin *builtin)
+/*
+ xt-set-values widget arguments
+ */
+{
+ Widget widget;
+ Resources *resources;
+
+ LispObj *owidget, *arguments;
+
+ arguments = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (arguments == NIL)
+ return (owidget);
+
+ if (!CHECKO(owidget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+ CHECK_CONS(arguments);
+ resources = LispConvertResources(arguments, widget,
+ GetResourceList(XtClass(widget)),
+ XtParent(widget) ?
+ GetResourceList(XtClass(XtParent(widget))) :
+ NULL);
+ XtSetValues(widget, resources->args, resources->num_args);
+ LispFreeResources(resources);
+
+ return (owidget);
+}
+
+LispObj *
+Lisp_XtWidgetToApplicationContext(LispBuiltin *builtin)
+/*
+ xt-widget-to-application-context widget
+ */
+{
+ Widget widget;
+ XtAppContext appcon;
+
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+ appcon = XtWidgetToApplicationContext(widget);
+
+ return (OPAQUE(appcon, xtAppContext_t));
+}
+
+LispObj *
+Lisp_XtDisplay(LispBuiltin *builtin)
+/*
+ xt-display widget
+ */
+{
+ Widget widget;
+ Display *display;
+
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+ display = XtDisplay(widget);
+
+ return (OPAQUE(display, xtDisplay_t));
+}
+
+LispObj *
+Lisp_XtDisplayOfObject(LispBuiltin *builtin)
+/*
+ xt-display-of-object object
+ */
+{
+ Widget widget;
+ Display *display;
+
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ if (!CHECKO(object, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(object));
+ widget = (Widget)(object->data.opaque.data);
+ display = XtDisplayOfObject(widget);
+
+ return (OPAQUE(display, xtDisplay_t));
+}
+
+LispObj *
+Lisp_XtScreen(LispBuiltin *builtin)
+/*
+ xt-screen widget
+ */
+{
+ Widget widget;
+ Screen *screen;
+
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+ screen = XtScreen(widget);
+
+ return (OPAQUE(screen, xtScreen_t));
+}
+
+LispObj *
+Lisp_XtScreenOfObject(LispBuiltin *builtin)
+/*
+ xt-screen-of-object object
+ */
+{
+ Widget widget;
+ Screen *screen;
+
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ if (!CHECKO(object, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(object));
+ widget = (Widget)(object->data.opaque.data);
+ screen = XtScreenOfObject(widget);
+
+ return (OPAQUE(screen, xtScreen_t));
+}
+
+LispObj *
+Lisp_XtWindow(LispBuiltin *builtin)
+/*
+ xt-window widget
+ */
+{
+ Widget widget;
+ Window window;
+
+ LispObj *owidget;
+
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+ window = XtWindow(widget);
+
+ return (OPAQUE(window, xtWindow_t));
+}
+
+LispObj *
+Lisp_XtWindowOfObject(LispBuiltin *builtin)
+/*
+ xt-window-of-object widget
+ */
+{
+ Widget widget;
+ Window window;
+
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ if (!CHECKO(object, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(object));
+ widget = (Widget)(object->data.opaque.data);
+ window = XtWindowOfObject(widget);
+
+ return (OPAQUE(window, xtWindow_t));
+}
+
+LispObj *
+Lisp_XtAddGrab(LispBuiltin *builtin)
+/*
+ xt-add-grab widget exclusive spring-loaded
+ */
+{
+ Widget widget;
+ Bool exclusive, spring_loaded;
+
+ LispObj *owidget, *oexclusive, *ospring_loaded;
+
+ ospring_loaded = ARGUMENT(2);
+ oexclusive = ARGUMENT(1);
+ owidget = ARGUMENT(0);
+
+ if (!CHECKO(owidget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(owidget));
+ widget = (Widget)(owidget->data.opaque.data);
+ exclusive = oexclusive != NIL;
+ spring_loaded = ospring_loaded != NIL;
+
+ XtAddGrab(widget, exclusive, spring_loaded);
+
+ return (T);
+}
+
+LispObj *
+Lisp_XtRemoveGrab(LispBuiltin *builtin)
+/*
+ xt-remove-grab widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+
+ XtRemoveGrab((Widget)(widget->data.opaque.data));
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_XtName(LispBuiltin *builtin)
+/*
+ xt-name widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+
+ return (STRING(XtName((Widget)(widget->data.opaque.data))));
+}
+
+LispObj *
+Lisp_XtParent(LispBuiltin *builtin)
+/*
+ xt-parent widget
+ */
+{
+ LispObj *widget;
+
+ widget = ARGUMENT(0);
+
+ if (!CHECKO(widget, xtWidget_t))
+ LispDestroy("%s: cannot convert %s to Widget",
+ STRFUN(builtin), STROBJ(widget));
+
+ return (OPAQUE(XtParent((Widget)widget->data.opaque.data), xtWidget_t));
+}
+
+LispObj *
+Lisp_XtAppGetExitFlag(LispBuiltin *builtin)
+/*
+ xt-app-get-exit-flag app-context
+ */
+{
+ LispObj *app_context;
+
+ app_context = ARGUMENT(0);
+
+ if (!CHECKO(app_context, xtAppContext_t))
+ LispDestroy("%s: cannot convert %s to XtAppContext",
+ STRFUN(builtin), STROBJ(app_context));
+
+ return (XtAppGetExitFlag((XtAppContext)(app_context->data.opaque.data)) ?
+ T : NIL);
+}
+
+LispObj *
+Lisp_XtAppSetExitFlag(LispBuiltin *builtin)
+/*
+ xt-app-get-exit-flag app-context
+ */
+{
+ LispObj *app_context;
+
+ app_context = ARGUMENT(0);
+
+ if (!CHECKO(app_context, xtAppContext_t))
+ LispDestroy("%s: cannot convert %s to XtAppContext",
+ STRFUN(builtin), STROBJ(app_context));
+
+ XtAppSetExitFlag((XtAppContext)(app_context->data.opaque.data));
+
+ return (T);
+}
+
+static Resources *
+LispConvertResources(LispObj *list, Widget widget,
+ ResourceList *rlist, ResourceList *plist)
+{
+ char c1;
+ short c2;
+ int c4;
+#ifdef LONG64
+ long c8;
+#endif
+ XrmValue from, to;
+ LispObj *arg, *val;
+ ResourceInfo *resource;
+ char *fname = "XT-CONVERT-RESOURCES";
+ Resources *resources = (Resources*)XtCalloc(1, sizeof(Resources));
+
+ for (; CONSP(list); list = CDR(list)) {
+ if (!CONSP(CAR(list))) {
+ XtFree((XtPointer)resources);
+ LispDestroy("%s: %s is not a cons", fname, STROBJ(CAR(list)));
+ }
+ arg = CAR(CAR(list));
+ val = CDR(CAR(list));
+
+ if (!STRINGP(arg)) {
+ XtFree((XtPointer)resources);
+ LispDestroy("%s: %s is not a string", fname, STROBJ(arg));
+ }
+
+ if ((resource = GetResourceInfo(THESTR(arg), rlist, plist)) == NULL) {
+ int i;
+ Arg args[1];
+ Widget child;
+
+ for (i = 0; i < rlist->num_resources; i++) {
+ if (rlist->resources[i]->qtype == qWidget) {
+ XtSetArg(args[0],
+ XrmQuarkToString(rlist->resources[i]->qname),
+ &child);
+ XtGetValues(widget, args, 1);
+ if (child && XtParent(child) == widget) {
+ resource =
+ GetResourceInfo(THESTR(arg),
+ GetResourceList(XtClass(child)),
+ NULL);
+ if (resource)
+ break;
+ }
+ }
+ }
+ if (resource == NULL) {
+ LispMessage("%s: resource %s not available",
+ fname, THESTR(arg));
+ continue;
+ }
+ }
+
+ if (LONGINTP(val) || DFLOATP(val) || OPAQUEP(val)) {
+ resources->args = (Arg*)
+ XtRealloc((XtPointer)resources->args,
+ sizeof(Arg) * (resources->num_args + 1));
+ if (!OPAQUEP(val)) {
+ float fvalue;
+
+ if (DFLOATP(val))
+ fvalue = DFLOAT_VALUE(val);
+ else
+ fvalue = LONGINT_VALUE(val);
+ if (resource->qtype == qFloat) {
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname), fvalue);
+ }
+ else
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname),
+ (int)fvalue);
+ }
+ else
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname), val->data.opaque.data);
+ ++resources->num_args;
+ continue;
+ }
+ else if (val == NIL) {
+ /* XXX assume it is a pointer or a boolean */
+#ifdef DEBUG
+ LispWarning("%s: assuming %s is a pointer or boolean",
+ fname, XrmQuarkToString(resource->qname));
+#endif
+ resources->args = (Arg*)
+ XtRealloc((XtPointer)resources->args,
+ sizeof(Arg) * (resources->num_args + 1));
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname), NULL);
+ ++resources->num_args;
+ continue;
+ }
+ else if (val == T) {
+ /* XXX assume it is a boolean */
+#ifdef DEBUG
+ LispWarning("%s: assuming %s is a boolean",
+ fname, XrmQuarkToString(resource->qname));
+#endif
+ resources->args = (Arg*)
+ XtRealloc((XtPointer)resources->args,
+ sizeof(Arg) * (resources->num_args + 1));
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname), True);
+ ++resources->num_args;
+ continue;
+ }
+ else if (!STRINGP(val)) {
+ XtFree((XtPointer)resources);
+ LispDestroy("%s: resource value must be string, number or opaque, not %s",
+ fname, STROBJ(val));
+ }
+
+ from.size = val == NIL ? 1 : strlen(THESTR(val)) + 1;
+ from.addr = val == NIL ? "" : THESTR(val);
+ switch (to.size = resource->size) {
+ case 1:
+ to.addr = (XtPointer)&c1;
+ break;
+ case 2:
+ to.addr = (XtPointer)&c2;
+ break;
+ case 4:
+ to.addr = (XtPointer)&c4;
+ break;
+#ifdef LONG64
+ case 8:
+ to.addr = (XtPointer)&c8;
+ break;
+#endif
+ default:
+ LispWarning("%s: bad resource size %d for %s",
+ fname, to.size, THESTR(arg));
+ continue;
+ }
+
+ if (qString == resource->qtype)
+#ifdef LONG64
+ c8 = (long)from.addr;
+#else
+ c4 = (long)from.addr;
+#endif
+ else if (!XtConvertAndStore(widget, XtRString, &from,
+ XrmQuarkToString(resource->qtype), &to))
+ /* The type converter already have printed an error message */
+ continue;
+
+ resources->args = (Arg*)
+ XtRealloc((XtPointer)resources->args,
+ sizeof(Arg) * (resources->num_args + 1));
+ switch (to.size) {
+ case 1:
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname), c1);
+ break;
+ case 2:
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname), c2);
+ break;
+ case 4:
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname), c4);
+ break;
+#ifdef LONG64
+ case 8:
+ XtSetArg(resources->args[resources->num_args],
+ XrmQuarkToString(resource->qname), c8);
+ break;
+#endif
+ }
+ ++resources->num_args;
+ }
+
+ return (resources);
+}
+
+static void
+LispFreeResources(Resources *resources)
+{
+ if (resources) {
+ XtFree((XtPointer)resources->args);
+ XtFree((XtPointer)resources);
+ }
+}
+
+static int
+bcmp_action_resource(_Xconst void *string, _Xconst void *resource)
+{
+ return (strcmp((String)string,
+ XrmQuarkToString((*(ResourceInfo**)resource)->qname)));
+}
+
+static ResourceInfo *
+GetResourceInfo(char *name, ResourceList *rlist, ResourceList *plist)
+{
+ ResourceInfo **resource = NULL;
+
+ if (rlist->resources)
+ resource = (ResourceInfo**)
+ bsearch(name, rlist->resources, rlist->num_resources,
+ sizeof(ResourceInfo*), bcmp_action_resource);
+
+ if (resource == NULL && plist) {
+ resource = (ResourceInfo**)
+ bsearch(name, &plist->resources[plist->num_resources],
+ plist->num_cons_resources, sizeof(ResourceInfo*),
+ bcmp_action_resource);
+ }
+
+ return (resource ? *resource : NULL);
+}
+
+static ResourceList *
+GetResourceList(WidgetClass wc)
+{
+ ResourceList *list;
+
+ if ((list = FindResourceList(wc)) == NULL)
+ list = CreateResourceList(wc);
+
+ return (list);
+}
+
+static int
+bcmp_action_resource_list(_Xconst void *wc, _Xconst void *list)
+{
+ return ((char*)wc - (char*)((*(ResourceList**)list)->widget_class));
+}
+
+static ResourceList *
+FindResourceList(WidgetClass wc)
+{
+ ResourceList **list;
+
+ if (!resource_list)
+ return (NULL);
+
+ list = (ResourceList**)
+ bsearch(wc, resource_list, num_resource_list,
+ sizeof(ResourceList*), bcmp_action_resource_list);
+
+ return (list ? *list : NULL);
+}
+
+static int
+qcmp_action_resource_list(_Xconst void *left, _Xconst void *right)
+{
+ return ((char*)((*(ResourceList**)left)->widget_class) -
+ (char*)((*(ResourceList**)right)->widget_class));
+}
+
+static ResourceList *
+CreateResourceList(WidgetClass wc)
+{
+ ResourceList *list;
+
+ list = (ResourceList*)XtMalloc(sizeof(ResourceList));
+ list->widget_class = wc;
+ list->num_resources = list->num_cons_resources = 0;
+ list->resources = NULL;
+
+ resource_list = (ResourceList**)
+ XtRealloc((XtPointer)resource_list, sizeof(ResourceList*) *
+ (num_resource_list + 1));
+ resource_list[num_resource_list++] = list;
+ qsort(resource_list, num_resource_list, sizeof(ResourceList*),
+ qcmp_action_resource_list);
+ BindResourceList(list);
+
+ return (list);
+}
+
+static int
+qcmp_action_resource(_Xconst void *left, _Xconst void *right)
+{
+ return (strcmp(XrmQuarkToString((*(ResourceInfo**)left)->qname),
+ XrmQuarkToString((*(ResourceInfo**)right)->qname)));
+}
+
+static void
+BindResourceList(ResourceList *list)
+{
+ XtResourceList xt_list, cons_list;
+ Cardinal i, num_xt, num_cons;
+
+ XtGetResourceList(list->widget_class, &xt_list, &num_xt);
+ XtGetConstraintResourceList(list->widget_class, &cons_list, &num_cons);
+ list->num_resources = num_xt;
+ list->num_cons_resources = num_cons;
+
+ list->resources = (ResourceInfo**)
+ XtMalloc(sizeof(ResourceInfo*) * (num_xt + num_cons));
+
+ for (i = 0; i < num_xt; i++) {
+ list->resources[i] = (ResourceInfo*)XtMalloc(sizeof(ResourceInfo));
+ list->resources[i]->qname =
+ XrmPermStringToQuark(xt_list[i].resource_name);
+ list->resources[i]->qtype =
+ XrmPermStringToQuark(xt_list[i].resource_type);
+ list->resources[i]->size = xt_list[i].resource_size;
+ }
+
+ for (; i < num_xt + num_cons; i++) {
+ list->resources[i] = (ResourceInfo*)XtMalloc(sizeof(ResourceInfo));
+ list->resources[i]->qname =
+ XrmPermStringToQuark(cons_list[i - num_xt].resource_name);
+ list->resources[i]->qtype =
+ XrmPermStringToQuark(cons_list[i - num_xt].resource_type);
+ list->resources[i]->size = cons_list[i - num_xt].resource_size;
+ }
+
+ XtFree((XtPointer)xt_list);
+ if (cons_list)
+ XtFree((XtPointer)cons_list);
+
+ qsort(list->resources, list->num_resources, sizeof(ResourceInfo*),
+ qcmp_action_resource);
+ if (num_cons)
+ qsort(&list->resources[num_xt], list->num_cons_resources,
+ sizeof(ResourceInfo*), qcmp_action_resource);
+}
+
+/*ARGSUSED*/
+static void
+PopdownAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ XtPopdown(w);
+}
+
+/*ARGSUSED*/
+static void
+QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ XtAppSetExitFlag(XtWidgetToApplicationContext(w));
+}
diff --git a/lisp/mp/mp.c b/lisp/mp/mp.c
new file mode 100644
index 0000000..78b7a0e
--- /dev/null
+++ b/lisp/mp/mp.c
@@ -0,0 +1,822 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/mp/mp.c,v 1.3 2002/11/20 07:44:43 paulo Exp $ */
+
+#include "mp.h"
+
+/*
+ * TODO:
+ * o Optimize squaring
+ * o Write better division code and move from mpi.c to here
+ * o Make multiplication code don't required memory to be zeroed
+ * + The first step is easy, just multiply the low word,
+ * then the high word, that may overlap with the result
+ * of the first multiply (in case of carry), and then
+ * just make sure carry is properly propagated in the
+ * subsequent multiplications.
+ * + Some code needs also to be rewritten because some
+ * intermediate addition code in mp_mul, mp_karatsuba_mul,
+ * and mp_toom_mul is assuming the memory is zeroed.
+ */
+
+/*
+ * Prototypes
+ */
+ /* out of memory handler */
+static void mp_outmem(void);
+
+ /* memory allocation fallback functions */
+static void *_mp_malloc(size_t);
+static void *_mp_calloc(size_t, size_t);
+static void *_mp_realloc(void*, size_t);
+static void _mp_free(void*);
+
+/*
+ * Initialization
+ */
+static mp_malloc_fun __mp_malloc = _mp_malloc;
+static mp_calloc_fun __mp_calloc = _mp_calloc;
+static mp_realloc_fun __mp_realloc = _mp_realloc;
+static mp_free_fun __mp_free = _mp_free;
+
+/*
+ * Implementation
+ */
+static void
+mp_outmem(void)
+{
+ fprintf(stderr, "out of memory in MP library.\n");
+ exit(1);
+}
+
+static void *
+_mp_malloc(size_t size)
+{
+ return (malloc(size));
+}
+
+void *
+mp_malloc(size_t size)
+{
+ void *pointer = (*__mp_malloc)(size);
+
+ if (pointer == NULL)
+ mp_outmem();
+
+ return (pointer);
+}
+
+mp_malloc_fun
+mp_set_malloc(mp_malloc_fun fun)
+{
+ mp_malloc_fun old = __mp_malloc;
+
+ __mp_malloc = fun;
+
+ return (old);
+}
+
+static void *
+_mp_calloc(size_t nmemb, size_t size)
+{
+ return (calloc(nmemb, size));
+}
+
+void *
+mp_calloc(size_t nmemb, size_t size)
+{
+ void *pointer = (*__mp_calloc)(nmemb, size);
+
+ if (pointer == NULL)
+ mp_outmem();
+
+ return (pointer);
+}
+
+mp_calloc_fun
+mp_set_calloc(mp_calloc_fun fun)
+{
+ mp_calloc_fun old = __mp_calloc;
+
+ __mp_calloc = fun;
+
+ return (old);
+}
+
+static void *
+_mp_realloc(void *old, size_t size)
+{
+ return (realloc(old, size));
+}
+
+void *
+mp_realloc(void *old, size_t size)
+{
+ void *pointer = (*__mp_realloc)(old, size);
+
+ if (pointer == NULL)
+ mp_outmem();
+
+ return (pointer);
+}
+
+mp_realloc_fun
+mp_set_realloc(mp_realloc_fun fun)
+{
+ mp_realloc_fun old = __mp_realloc;
+
+ __mp_realloc = fun;
+
+ return (old);
+}
+
+static void
+_mp_free(void *pointer)
+{
+ free(pointer);
+}
+
+void
+mp_free(void *pointer)
+{
+ (*__mp_free)(pointer);
+}
+
+mp_free_fun
+mp_set_free(mp_free_fun fun)
+{
+ mp_free_fun old = __mp_free;
+
+ __mp_free = fun;
+
+ return (old);
+}
+
+long
+mp_add(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2)
+{
+ BNI value; /* intermediate result */
+ BNS carry; /* carry flag */
+ long size; /* result size */
+
+ if (len1 < len2)
+ MP_SWAP(op1, op2, len1, len2);
+
+ /* unroll start of loop */
+ value = op1[0] + op2[0];
+ rop[0] = value;
+ carry = value >> BNSBITS;
+
+ /* add op1 and op2 */
+ for (size = 1; size < len2; size++) {
+ value = op1[size] + op2[size] + carry;
+ rop[size] = value;
+ carry = value >> BNSBITS;
+ }
+ if (rop != op1) {
+ for (; size < len1; size++) {
+ value = op1[size] + carry;
+ rop[size] = value;
+ carry = value >> BNSBITS;
+ }
+ }
+ else {
+ /* if rop == op1, than just adjust carry */
+ for (; carry && size < len1; size++) {
+ value = op1[size] + carry;
+ rop[size] = value;
+ carry = value >> BNSBITS;
+ }
+ size = len1;
+ }
+ if (carry)
+ rop[size++] = carry;
+
+ return (size);
+}
+
+long
+mp_sub(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2)
+{
+ long svalue; /* intermediate result */
+ BNS carry; /* carry flag */
+ long size; /* result size */
+
+ /* special case */
+ if (op1 == op2) {
+ rop[0] = 0;
+
+ return (1);
+ }
+
+ /* unroll start of loop */
+ svalue = op1[0] - op2[0];
+ rop[0] = svalue;
+ carry = svalue < 0;
+
+ /* subtracts op2 from op1 */
+ for (size = 1; size < len2; size++) {
+ svalue = (long)(op1[size]) - op2[size] - carry;
+ rop[size] = svalue;
+ carry = svalue < 0;
+ }
+ if (rop != op1) {
+ for (; size < len1; size++) {
+ svalue = op1[size] - carry;
+ rop[size] = svalue;
+ carry = svalue < 0;
+ }
+ }
+ else {
+ /* if rop == op1, than just adjust carry */
+ for (; carry && size < len1; size++) {
+ svalue = op1[size] - carry;
+ rop[size] = svalue;
+ carry = svalue < 0;
+ }
+ size = len1;
+ }
+
+ /* calculate result size */
+ while (size > 1 && rop[size - 1] == 0)
+ --size;
+
+ return (size);
+}
+
+long
+mp_lshift(BNS *rop, BNS *op, BNI len, long shift)
+{
+ long i, size;
+ BNI words, bits; /* how many word and bit shifts */
+
+ words = shift / BNSBITS;
+ bits = shift % BNSBITS;
+ size = len + words;
+
+ if (bits) {
+ BNS hi, lo;
+ BNI carry;
+ int adj;
+
+ for (i = 1, carry = CARRY >> 1; carry; i++, carry >>= 1)
+ if (op[len - 1] & carry)
+ break;
+ adj = (bits + (BNSBITS - i)) / BNSBITS;
+ size += adj;
+
+ lo = hi = op[0];
+ rop[words] = lo << bits;
+ for (i = 1; i < len; i++) {
+ hi = op[i];
+ rop[words + i] = hi << bits | (lo >> (BNSBITS - bits));
+ lo = hi;
+ }
+ if (adj)
+ rop[size - 1] = hi >> (BNSBITS - bits);
+ }
+ else
+ memmove(rop + size - len, op, sizeof(BNS) * len);
+
+ if (words)
+ memset(rop, '\0', sizeof(BNS) * words);
+
+ return (size);
+}
+
+long
+mp_rshift(BNS *rop, BNS *op, BNI len, long shift)
+{
+ int adj = 0;
+ long i, size;
+ BNI words, bits; /* how many word and bit shifts */
+
+ words = shift / BNSBITS;
+ bits = shift % BNSBITS;
+ size = len - words;
+
+ if (bits) {
+ BNS hi, lo;
+ BNI carry;
+
+ for (i = 0, carry = CARRY >> 1; carry; i++, carry >>= 1)
+ if (op[len - 1] & carry)
+ break;
+ adj = (bits + i) / BNSBITS;
+ if (size - adj == 0) {
+ rop[0] = 0;
+
+ return (1);
+ }
+
+ hi = lo = op[words + size - 1];
+ rop[size - 1] = hi >> bits;
+ for (i = size - 2; i >= 0; i--) {
+ lo = op[words + i];
+ rop[i] = (lo >> bits) | (hi << (BNSBITS - bits));
+ hi = lo;
+ }
+ if (adj)
+ rop[0] |= lo << (BNSBITS - bits);
+ }
+ else
+ memmove(rop, op + len - size, size * sizeof(BNS));
+
+ return (size - adj);
+}
+
+ /* rop must be a pointer to len1 + len2 elements
+ * rop cannot be either op1 or op2
+ * rop must be all zeros */
+long
+mp_base_mul(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2)
+{
+ long i, j; /* counters */
+ BNI value; /* intermediate result */
+ BNS carry; /* carry value */
+ long size = len1 + len2;
+
+ /* simple optimization: first pass does not need to deference rop[i+j] */
+ if (op1[0]) {
+ value = (BNI)(op1[0]) * op2[0];
+ rop[0] = value;
+ carry = (BNS)(value >> BNSBITS);
+ for (j = 1; j < len2; j++) {
+ value = (BNI)(op1[0]) * op2[j] + carry;
+ rop[j] = value;
+ carry = (BNS)(value >> BNSBITS);
+ }
+ rop[j] = carry;
+ }
+
+ /* do the multiplication */
+ for (i = 1; i < len1; i++) {
+ if (op1[i]) {
+ /* unrool loop initialization */
+ value = (BNI)(op1[i]) * op2[0] + rop[i];
+ rop[i] = value;
+ carry = (BNS)(value >> BNSBITS);
+ /* multiply */
+ for (j = 1; j < len2; j++) {
+ value = (BNI)(op1[i]) * op2[j] + rop[i + j] + carry;
+ rop[i + j] = value;
+ carry = (BNS)(value >> BNSBITS);
+ }
+ rop[i + j] = carry;
+ }
+ }
+
+ if (size > 1 && rop[size - 1] == 0)
+ --size;
+
+ return (size);
+}
+
+ /* Karatsuba method
+ * t + ((a0 + a1) (b0 + b1) - t - u) x + ux²
+ * where t = a0b0 and u = a1b1
+ *
+ * Karatsuba method reduces the number of multiplications. Example:
+ * Square a 40 length number
+ * instead of a plain 40*40 = 1600 multiplies/adds, it does:
+ * 20*20+20*20+20*20 = 1200
+ * but since it is recursive, every 20*20=400 is reduced to
+ * 10*10+10*10+10*10=300
+ * and so on.
+ * The multiplication by x and x² is a just a shift, as it is a
+ * power of two, and is implemented below by just writting at the
+ * correct offset */
+long
+mp_karatsuba_mul(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2)
+{
+ BNI x; /* shift count */
+ BNI la0, la1, lb0, lb1; /* length of a0, a1, b0, and b1 */
+ BNS *t; /* temporary memory for t product */
+ BNS *u; /* temporary memory for u product */
+ BNS *r; /* pointer to rop */
+ long xlen, tlen, ulen;
+
+ /* calculate value of x, that is 2^(BNSBITS*x) */
+ if (len1 >= len2)
+ x = (len1 + 1) >> 1;
+ else
+ x = (len2 + 1) >> 1;
+
+ /* calculate length of operands */
+ la0 = x;
+ la1 = len1 - x;
+ lb0 = x;
+ lb1 = len2 - x;
+
+ /* allocate buffer for t and (a0 + a1) */
+ tlen = la0 + lb0;
+ t = mp_malloc(sizeof(BNS) * tlen);
+
+ /* allocate buffer for u and (b0 + b1) */
+ if (la1 + lb1 < lb0 + lb1 + 1)
+ ulen = lb0 + lb1 + 1;
+ else
+ ulen = la1 + lb1;
+ u = mp_malloc(sizeof(BNS) * ulen);
+
+ /* calculate a0 + a1, store result in t */
+ tlen = mp_add(t, op1, op1 + x, la0, la1);
+
+ /* calculate b0 + b1, store result in u */
+ ulen = mp_add(u, op2, op2 + x, lb0, lb1);
+
+ /* store (a0 + a1) * (b0 + b1) in rop */
+
+ r = rop + x; /* multiplied by 2^(BNSBITS*x) */
+ xlen = mp_mul(r, t, u, tlen, ulen);
+
+ /* must zero t and u memory, this is required for mp_mul */
+
+ /* calculate t = a0 * b0 */
+ tlen = la0 + lb0;
+ memset(t, '\0', sizeof(BNS) * tlen);
+ tlen = mp_mul(t, op1, op2, la0, lb0);
+
+ /* calculate u = a1 * b1 */
+ ulen = la1 + lb1;
+ memset(u, '\0', sizeof(BNS) * ulen);
+ ulen = mp_mul(u, op1 + x, op2 + x, la1, lb1);
+
+ /* subtract t from partial result */
+ xlen = mp_sub(r, r, t, xlen, tlen);
+
+ /* subtract u form partial result */
+ xlen = mp_sub(r, r, u, xlen, ulen);
+
+ /* add ux^2 to partial result */
+
+ r = rop + (x << 1); /* multiplied by x^2 = 2^(BNSBITS*x*2) */
+ xlen = len1 + len2;
+ xlen = mp_add(r, r, u, xlen, ulen);
+
+ /* now add t to final result */
+ xlen = mp_add(rop, rop, t, xlen, tlen);
+
+ mp_free(t);
+ mp_free(u);
+
+ if (xlen > 1 && rop[xlen - 1] == 0)
+ --xlen;
+
+ return (xlen);
+}
+
+ /* Toom method (partially based on GMP documentation)
+ * Evaluation at k = [ 0 1/2 1 2 oo ]
+ * U(x) = (U2k + U1)k + U0
+ * V(x) = (V2k + V1)k + V0
+ * W(x) = U(x)V(x)
+ *
+ * Sample:
+ * 123 * 456
+ *
+ * EVALUATION:
+ * U(0) = (1*0+2)*0+3 => 3
+ * U(1) = 1+(2+3*2)*2 => 17
+ * U(2) = 1+2+3 => 6
+ * U(3) = (1*2+2)*2+3 => 11
+ * U(4) = 1+(2+3*0)*0 => 1
+ *
+ * V(0) = (4*0+5)*0+6 => 6
+ * V(1) = 4+(5+6*2)*2 => 38
+ * V(2) = 4+5+6 => 15
+ * V(3) = (4*2+5)*2+6 => 32
+ * V(4) = 4+(5+6*0)*0 => 4
+ *
+ * U = [ 3 17 6 11 1 ]
+ * V = [ 6 38 15 32 4 ]
+ * W = [ 18 646 90 352 4 ]
+ *
+ * After that, we have:
+ * a = 18 (w0 already known)
+ * b = 16w0 + 8w1 + 4w2 + 2w3 + w4
+ * c = w0 + w1 + w2 + w3 + w4
+ * d = w0 + 2w1 + 4w2 + 8w3 + 16w4
+ * e = 4 (w4 already known)
+ *
+ * INTERPOLATION:
+ * b = b -16a - e (354)
+ * c = c - a - e (68)
+ * d = d - a - 16e (270)
+ *
+ * w = (b + d) - 8c = (10w1+8w2+10w3) - (8w1+8w2+8w3) = 2w1+2w3
+ * w = 2c - w (56)
+ * b = b/2 = 4w1+w+w3
+ * b = b-c = 4w1+w+w3 - w1+w2+w3 = 3w1+w2
+ * c = w/2 (w2 = 28)
+ * b = b-c = 3w1+c - c = 3w1
+ * b = b/3 (w1 = 27)
+ * d = d/2
+ * d = d-b-w = b+w+4w3 - b-w = 4w3
+ * d = d/4 (w3 = 13)
+ *
+ * RESULT:
+ * w4*10^4 + w3*10³ + w2*10² + w1*10 + w0
+ * 40000 + 13000 + 2800 + 270 + 18
+ * 10 is the base where the calculation was done
+ *
+ * This sample uses small numbers, so it does not show the
+ * advantage of the method. But for example (in base 10), when squaring
+ * 123456789012345678901234567890
+ * The normal method would do 30*30=900 multiplications
+ * Karatsuba method would do 15*15*3=675 multiplications
+ * Toom method would do 10*10*5=500 multiplications
+ * Toom method has a larger overhead if compared with Karatsuba method,
+ * due to evaluation and interpolation, so it should be used for larger
+ * numbers, so that the computation time of evaluation/interpolation
+ * would be smaller than the time spent using other methods.
+ *
+ * Note that Karatsuba method can be seen as a special case of
+ * Toom method, i.e:
+ * U1U0 * V1V0
+ * with k = [ 0 1 oo ]
+ * U = [ U0 U1+U0 U1 ]
+ * V = [ V0 V1+V0 V1 ]
+ * W = [ U0*V0 (U1+U0)*(V1+V0) (U1+V1) ]
+ *
+ * w0 = U0*V0
+ * w = (U1+U0)*(V1+V0)
+ * w2 = (U1*V1)
+ *
+ * w1 = w - w0 - w2
+ * w2x² + w1x + w0
+ *
+ * See Knuth's Seminumerical Algorithms for a sample implemention
+ * using 4 stacks and k = [ 0 1 2 3 ... ], based on the size of the
+ * input.
+ */
+long
+mp_toom_mul(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2)
+{
+ long size, xsize, i;
+ BNI value; /* used in division */
+ BNS carry;
+ BNI x; /* shift count */
+ BNI l1, l2;
+ BNI al, bl, cl, dl, el, Ul[3], Vl[3];
+ BNS *a, *b, *c, *d, *e, *U[3], *V[3];
+
+ /* x is the base i.e. 2^(BNSBITS*x) */
+ x = (len1 + len2 + 4) / 6;
+ l1 = len1 - (x << 1); /* length of remaining piece of op1 */
+ l2 = len2 - (x << 1); /* length of remaining piece of op2 */
+
+ /* allocate memory for storing U and V */
+ U[0] = mp_malloc(sizeof(BNS) * (x + 2));
+ V[0] = mp_malloc(sizeof(BNS) * (x + 2));
+ U[1] = mp_malloc(sizeof(BNS) * (x + 1));
+ V[1] = mp_malloc(sizeof(BNS) * (x + 1));
+ U[2] = mp_malloc(sizeof(BNS) * (x + 2));
+ V[2] = mp_malloc(sizeof(BNS) * (x + 2));
+
+ /* EVALUATE U AND V */
+
+ /* Numbers are in the format U2x²+U1x+U0 and V2x²+V1x+V0 */
+
+ /* U[0] = U2+U1*2+U0*4 */
+
+ /* store U1*2 in U[1], this value is used twice */
+ Ul[1] = mp_lshift(U[1], op1 + x, x, 1);
+
+ /* store U0*4 in U[0] */
+ Ul[0] = mp_lshift(U[0], op1, x, 2);
+ /* add U1*2 to U[0] */
+ Ul[0] = mp_add(U[0], U[0], U[1], Ul[0], Ul[1]);
+ /* add U2 to U[0] */
+ Ul[0] = mp_add(U[0], U[0], op1 + x + x, Ul[0], l1);
+
+ /* U[2] = U2*4+U1*2+U0 */
+
+ /* store U2*4 in U[2] */
+ Ul[2] = mp_lshift(U[2], op1 + x + x, l1, 2);
+ /* add U1*2 to U[2] */
+ Ul[2] = mp_add(U[2], U[2], U[1], Ul[2], Ul[1]);
+ /* add U0 to U[2] */
+ Ul[2] = mp_add(U[2], U[2], op1, Ul[2], x);
+
+ /* U[1] = U2+U1+U0 */
+
+ Ul[1] = mp_add(U[1], op1, op1 + x, x, x);
+ Ul[1] = mp_add(U[1], U[1], op1 + x + x, Ul[1], l1);
+
+
+ /* Evaluate V[x], same code as U[x] */
+ Vl[1] = mp_lshift(V[1], op2 + x, x, 1);
+ Vl[0] = mp_lshift(V[0], op2, x, 2);
+ Vl[0] = mp_add(V[0], V[0], V[1], Vl[0], Vl[1]);
+ Vl[0] = mp_add(V[0], V[0], op2 + x + x, Vl[0], l2);
+ Vl[2] = mp_lshift(V[2], op2 + x + x, l2, 2);
+ Vl[2] = mp_add(V[2], V[2], V[1], Vl[2], Vl[1]);
+ Vl[2] = mp_add(V[2], V[2], op2, Vl[2], x);
+ Vl[1] = mp_add(V[1], op2, op2 + x, x, x);
+ Vl[1] = mp_add(V[1], V[1], op2 + x + x, Vl[1], l2);
+
+
+ /* MULTIPLY U[] AND V[] */
+
+ /* calculate (U2+U1*2+U0*4) * (V2+V1*2+V0*4) */
+ b = mp_calloc(1, sizeof(BNS) * (Ul[0] * Vl[0]));
+ bl = mp_mul(b, U[0], V[0], Ul[0], Vl[0]);
+ mp_free(U[0]);
+ mp_free(V[0]);
+
+ /* calculate (U2+U1+U0) * (V2+V1+V0) */
+ c = mp_calloc(1, sizeof(BNS) * (Ul[1] * Vl[1]));
+ cl = mp_mul(c, U[1], V[1], Ul[1], Vl[1]);
+ mp_free(U[1]);
+ mp_free(V[1]);
+
+ /* calculate (U2*4+U1*2+U0) * (V2*4+V1*2+V0) */
+ d = mp_calloc(1, sizeof(BNS) * (Ul[2] * Vl[2]));
+ dl = mp_mul(d, U[2], V[2], Ul[2], Vl[2]);
+ mp_free(U[2]);
+ mp_free(V[2]);
+
+ /* calculate U0 * V0 */
+ a = mp_calloc(1, sizeof(BNS) * (x + x));
+ al = mp_mul(a, op1, op2, x, x);
+
+ /* calculate U2 * V2 */
+ e = mp_calloc(1, sizeof(BNS) * (l1 + l2));
+ el = mp_mul(e, op1 + x + x, op2 + x + x, l1, l2);
+
+
+ /* INTERPOLATE COEFFICIENTS */
+
+ /* b = b - 16a - e */
+ size = mp_lshift(rop, a, al, 4);
+ bl = mp_sub(b, b, rop, bl, size);
+ bl = mp_sub(b, b, e, bl, el);
+
+ /* c = c - a - e*/
+ cl = mp_sub(c, c, a, cl, al);
+ cl = mp_sub(c, c, e, cl, el);
+
+ /* d = d - a - 16e */
+ dl = mp_sub(d, d, a, dl, al);
+ size = mp_lshift(rop, e, el, 4);
+ dl = mp_sub(d, d, rop, dl, size);
+
+ /* w = (b + d) - 8c */
+ size = mp_add(rop, b, d, bl, dl);
+ xsize = mp_lshift(rop + size, c, cl, 3); /* rop has enough storage */
+ size = mp_sub(rop, rop, rop + size, size, xsize);
+
+ /* w = 2c - w*/
+ xsize = mp_lshift(rop + size, c, cl, 1);
+ size = mp_sub(rop, rop + size, rop, xsize, size);
+
+ /* b = b/2 */
+ bl = mp_rshift(b, b, bl, 1);
+
+ /* b = b - c */
+ bl = mp_sub(b, b, c, bl, cl);
+
+ /* c = w / 2 */
+ cl = mp_rshift(c, rop, size, 1);
+
+ /* b = b - c */
+ bl = mp_sub(b, b, c, bl, cl);
+
+ /* b = b/3 */
+ /* maybe the most expensive calculation */
+ i = bl - 1;
+ value = b[i];
+ b[i] = value / 3;
+ for (--i; i >= 0; i--) {
+ carry = value % 3;
+ value = ((BNI)carry << BNSBITS) + b[i];
+ b[i] = (BNS)(value / 3);
+ }
+
+ /* d = d/2 */
+ dl = mp_rshift(d, d, dl, 1);
+
+ /* d = d - b - w */
+ dl = mp_sub(d, d, b, dl, bl);
+ dl = mp_sub(d, d, rop, dl, size);
+
+ /* d = d/4 */
+ dl = mp_rshift(d, d, dl, 2);
+
+
+ /* STORE RESULT IN ROP */
+ /* first clear memory used as temporary variable w and 8c */
+ memset(rop, '\0', sizeof(BNS) * (len1 + len2));
+
+ i = x * 4;
+ xsize = (len1 + len2) - i;
+ size = mp_add(rop + i, rop + i, e, xsize, el) + i;
+ i = x * 3;
+ xsize = size - i;
+ size = mp_add(rop + i, rop + i, d, xsize, dl) + i;
+ i = x * 2;
+ xsize = size - i;
+ size = mp_add(rop + i, rop + i, c, xsize, cl) + i;
+ i = x;
+ xsize = size - i;
+ size = mp_add(rop + i, rop + i, b, xsize, bl) + i;
+ size = mp_add(rop, rop, a, size, al);
+
+ mp_free(e);
+ mp_free(d);
+ mp_free(c);
+ mp_free(b);
+ mp_free(a);
+
+ if (size > 1 && rop[size - 1] == 0)
+ --size;
+
+ return (size);
+}
+
+long
+mp_mul(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2)
+{
+ if (len1 < len2)
+ MP_SWAP(op1, op2, len1, len2);
+
+ if (len1 < KARATSUBA || len2 < KARATSUBA)
+ return (mp_base_mul(rop, op1, op2, len1, len2));
+ else if (len1 < TOOM && len2 < TOOM && len2 > ((len1 + 1) >> 1))
+ return (mp_karatsuba_mul(rop, op1, op2, len1, len2));
+ else if (len1 >= TOOM && len2 >= TOOM && (len2 + 2) / 3 == (len1 + 2) / 3)
+ return (mp_toom_mul(rop, op1, op2, len1, len2));
+ else {
+ long xsize, psize, isize;
+ BNS *ptr;
+
+ /* adjust index pointer and estimated size of result */
+ isize = 0;
+ xsize = len1 + len2;
+ mp_mul(rop, op1, op2, len2, len2);
+ /* adjust pointers */
+ len1 -= len2;
+ op1 += len2;
+
+ /* allocate buffer for intermediate multiplications */
+ if (len1 > len2)
+ ptr = mp_calloc(1, sizeof(BNS) * (len2 + len2));
+ else
+ ptr = mp_calloc(1, sizeof(BNS) * (len1 + len2));
+
+ /* loop multiplying len2 size operands at a time */
+ while (len1 >= len2) {
+ isize += len2;
+ psize = mp_mul(ptr, op1, op2, len2, len2);
+ mp_add(rop + isize, rop + isize, ptr, xsize - isize, psize);
+ len1 -= len2;
+ op1 += len2;
+
+ /* multiplication routines require zeroed memory */
+ memset(ptr, '\0', sizeof(BNS) * (MIN(len1, len2) + len2));
+ }
+
+ /* len1 was not a multiple of len2 */
+ if (len1) {
+ isize += len2;
+ psize = mp_mul(ptr, op2, op1, len2, len1);
+ mp_add(rop + isize, rop + isize, ptr, xsize, psize);
+ }
+
+ /* adjust result size */
+ if (rop[xsize - 1] == 0)
+ --xsize;
+
+ mp_free(ptr);
+
+ return (xsize);
+ }
+}
diff --git a/lisp/mp/mp.h b/lisp/mp/mp.h
new file mode 100644
index 0000000..88f1b24
--- /dev/null
+++ b/lisp/mp/mp.h
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/mp/mp.h,v 1.6 2003/01/12 03:55:51 tsi Exp $ */
+
+#include <stdio.h>
+#include <math.h>
+#ifdef sun
+#include <ieeefp.h>
+#endif
+#include <float.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+
+#ifndef __mp_h_
+#define __mp_h_
+
+#ifdef __GNUC__
+#define INLINE __inline__
+#else
+#define INLINE /**/
+#endif
+
+/* this normally is better for multiplication and also
+ * simplify addition loops putting the larger value first */
+#define MP_SWAP(op1, op2, len1, len2) { \
+ BNS *top = op1; \
+ BNI tlen = len1; \
+ \
+ op1 = op2; \
+ len1 = len2; \
+ op2 = top; \
+ len2 = tlen; \
+}
+
+/*
+ * At least this length to use Karatsuba multiplication method
+ */
+#define KARATSUBA 32
+
+/*
+ * At least this length to use Toom multiplication method
+ */
+#define TOOM 128
+
+#if ULONG_MAX > 4294967295UL
+ /* sizeof(long) == 8 and sizeof(int) == 4 */
+# define BNI unsigned long
+# define BNS unsigned int
+# define MINSLONG 0x8000000000000000UL
+# define CARRY 0x100000000
+# define LMASK 0xffffffff00000000UL
+# define SMASK 0x00000000ffffffffUL
+# define BNIBITS 64
+# define BNSBITS 32
+# ifndef LONG64
+# define LONG64
+# endif
+#else
+ /* sizeof(long) == 4 and sizeof(short) == 2 */
+# define BNI unsigned long
+# define BNS unsigned short
+# define MINSLONG 0x80000000UL
+# define CARRY 0x10000
+# define LMASK 0xffff0000UL
+# define SMASK 0x0000ffffUL
+# define BNIBITS 32
+# define BNSBITS 16
+#endif
+
+#ifdef MAX
+#undef MAX
+#endif
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#ifdef MIN
+#undef MIN
+#endif
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Types
+ */
+typedef struct _mpi {
+ unsigned int size : 31;
+ unsigned int sign : 1;
+ BNI alloc;
+ BNS *digs; /* LSF format */
+} mpi;
+
+typedef struct _mpr {
+ mpi num;
+ mpi den;
+} mpr;
+
+typedef void *(*mp_malloc_fun)(size_t);
+typedef void *(*mp_calloc_fun)(size_t, size_t);
+typedef void *(*mp_realloc_fun)(void*, size_t);
+typedef void (*mp_free_fun)(void*);
+
+/*
+ * Prototypes
+ */
+/* GENERIC FUNCTIONS */
+ /* memory allocation wrappers */
+void *mp_malloc(size_t size);
+void *mp_calloc(size_t nmemb, size_t size);
+void *mp_realloc(void *pointer, size_t size);
+void mp_free(void *pointer);
+mp_malloc_fun mp_set_malloc(mp_malloc_fun);
+mp_calloc_fun mp_set_calloc(mp_calloc_fun);
+mp_realloc_fun mp_set_realloc(mp_realloc_fun);
+mp_free_fun mp_set_free(mp_free_fun);
+
+ /* adds op1 and op2, stores result in rop
+ * rop must pointer to at least len1 + len2 + 1 elements
+ * rop can be either op1 or op2 */
+long mp_add(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2);
+
+ /* subtracts op2 from op1, stores result in rop
+ * rop must pointer to at least len1 + len2 elements
+ * op1 must be >= op2
+ * rop can be either op1 or op2 */
+long mp_sub(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2);
+
+ /* shift op to the left shift bits
+ * rop must have enough storage for result
+ * rop can be op */
+long mp_lshift(BNS *rop, BNS *op, BNI len, long shift);
+
+ /* shift op to the right shift bits
+ * shift must be positive
+ * rop can be op */
+long mp_rshift(BNS *rop, BNS *op, BNI len, long shift);
+
+ /* use simple generic multiplication method
+ * rop cannot be the same as op1 or op2
+ * rop must be zeroed
+ * op1 can be op2 */
+long mp_base_mul(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2);
+
+ /* use Karatsuba method
+ * MIN(len1, len2) must be larger than (MAX(len1, len2) + 1) >> 1
+ * MAX(len1, len2) should be at least 2
+ * rop cannot be the same as op1 or op2
+ * rop must be zeroed
+ * op1 can be op2 */
+long mp_karatsuba_mul(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2);
+
+ /* use Toom method
+ * len1 / 3 should be equal to len2 / 3
+ * len1 / 3 should be at least 1
+ * rop cannot be the same as op1 or op2
+ * rop must be zeroed
+ * op1 can be op2 */
+long mp_toom_mul(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2);
+
+ /* chooses the available multiplication methods based on it's input
+ * rop must be a pointer to len1 + len2 elements
+ * rop cannot be the same as op1 or op2
+ * rop must be zeroed
+ * op1 can be op2 */
+long mp_mul(BNS *rop, BNS *op1, BNS *op2, BNI len1, BNI len2);
+
+/* INTEGER FUNCTIONS */
+ /* initialize op and set it to 0 */
+void mpi_init(mpi *op);
+
+ /* clear memory associated to op */
+void mpi_clear(mpi *op);
+
+ /* set rop to the value of op */
+void mpi_set(mpi *rop, mpi *op);
+
+ /* set rop to the value of si */
+void mpi_seti(mpi *rop, long si);
+
+ /* set rop to the floor(fabs(d)) */
+void mpi_setd(mpi *rop, double d);
+
+ /* initialize rop to number representation in str in the given base.
+ * leading zeros are skipped.
+ * if sign present, it is processed.
+ * base must be in the range 2 to 36. */
+void mpi_setstr(mpi *rop, char *str, int base);
+
+ /* adds two mp integers */
+void mpi_add(mpi *rop, mpi *op1, mpi *op2);
+
+ /* adds op1 and op2 */
+void mpi_addi(mpi *rop, mpi *op1, long op2);
+
+ /* subtracts two mp integers */
+void mpi_sub(mpi *rop, mpi *op1, mpi *op2);
+
+ /* subtracts op2 from op1 */
+void mpi_subi(mpi *rop, mpi *op1, long op2);
+
+ /* multiply two mp integers */
+void mpi_mul(mpi *rop, mpi *op1, mpi *op2);
+
+ /* multiply op1 by op2 */
+void mpi_muli(mpi *rop, mpi *op1, long op2);
+
+ /* divides num by den and sets rop to result */
+void mpi_div(mpi *rop, mpi *num, mpi *den);
+
+ /* divides num by den and sets rop to the remainder */
+void mpi_rem(mpi *rop, mpi *num, mpi *den);
+
+ /* divides num by den, sets quotient to qrop and remainder to rrop
+ * qrop is truncated towards zero.
+ * qrop and rrop are optional
+ * qrop and rrop cannot be the same variable */
+void mpi_divqr(mpi *qrop, mpi *rrop, mpi *num, mpi *den);
+
+ /* divides num by then and stores result in rop */
+void mpi_divi(mpi *rop, mpi *num, long den);
+
+ /* divides num by den and returns remainder */
+long mpi_remi(mpi *num, long den);
+
+ /* divides num by den
+ * stores quotient in qrop and returns remainder */
+long mpi_divqri(mpi *qrop, mpi *num, long den);
+
+ /* sets rop to num modulo den */
+void mpi_mod(mpi *rop, mpi *num, mpi *den);
+
+ /* returns num modulo den */
+long mpi_modi(mpi *num, long den);
+
+ /* sets rop to the greatest common divisor of num and den
+ * result is always positive */
+void mpi_gcd(mpi *rop, mpi *num, mpi *den);
+
+ /* sets rop to the least common multiple of num and den
+ * result is always positive */
+void mpi_lcm(mpi *rop, mpi *num, mpi *den);
+
+ /* sets rop to op raised to exp */
+void mpi_pow(mpi *rop, mpi *op, unsigned long exp);
+
+ /* sets rop to the integer part of the nth root of op.
+ * returns 1 if result is exact, 0 otherwise */
+int mpi_root(mpi *rop, mpi *op, unsigned long nth);
+
+ /* sets rop to the integer part of the square root of op.
+ * returns 1 if result is exact, 0 otherwise */
+int mpi_sqrt(mpi *rop, mpi *op);
+
+ /* bit shift, left if shift positive, right if negative
+ * a fast way to multiply and divide by powers of two */
+void mpi_ash(mpi *rop, mpi *op, long shift);
+
+ /* sets rop to op1 logand op2 */
+void mpi_and(mpi *rop, mpi *op1, mpi *op2);
+
+ /* sets rop to op1 logior op2 */
+void mpi_ior(mpi *rop, mpi *op1, mpi *op2);
+
+ /* sets rop to op1 logxor op2 */
+void mpi_xor(mpi *rop, mpi *op1, mpi *op2);
+
+ /* sets rop to one's complement of op */
+void mpi_com(mpi *rop, mpi *op);
+
+ /* sets rop to -op */
+void mpi_neg(mpi *rop, mpi *op);
+
+ /* sets rop to the absolute value of op */
+void mpi_abs(mpi *rop, mpi *op);
+
+ /* compares op1 and op2
+ * returns >0 if op1 > op2, 0 if op1 = op2, and <0 if op1 < op2 */
+int mpi_cmp(mpi *op1, mpi *op2);
+
+ /* mpi_cmp with a long integer operand */
+int mpi_cmpi(mpi *op1, long op2);
+
+ /* compares absolute value of op1 and op2
+ * returns >0 if abs(op1) > abs(op2), 0 if abs(op1) = abs(op2),
+ * and <0 if abs(op1) < abs(op2) */
+int mpi_cmpabs(mpi *op1, mpi *op2);
+
+ /* mpi_cmpabs with a long integer operand */
+int mpi_cmpabsi(mpi *op1, long op2);
+
+ /* returns 1 if op1 > 0, 0 if op1 = 0, and -1 if op1 < 0 */
+int mpi_sgn(mpi *op);
+
+ /* fastly swaps contents of op1 and op2 */
+void mpi_swap(mpi *op1, mpi *op2);
+
+ /* returns 1 if op fits in a signed long int, 0 otherwise */
+int mpi_fiti(mpi *op);
+
+ /* converts mp integer to long int
+ * to know if the value will fit, call mpi_fiti */
+long mpi_geti(mpi *op);
+
+ /* convert mp integer to double */
+double mpi_getd(mpi *op);
+
+ /* returns exact number of characters to represent mp integer
+ * in given base, excluding sign and ending null character.
+ * base must be in the range 2 to 36 */
+unsigned long mpi_getsize(mpi *op, int base);
+
+ /* returns pointer to string with representation of mp integer
+ * if str is not NULL, it must have enough space to store integer
+ * representation, if NULL a newly allocated string is returned.
+ * base must be in the range 2 to 36 */
+char *mpi_getstr(char *str, mpi *op, int base);
+
+/* RATIO FUNCTIONS */
+#define mpr_num(op) (&((op)->num))
+#define mpr_den(op) (&((op)->den))
+
+ /* initialize op and set it to 0/1 */
+void mpr_init(mpr *op);
+
+ /* clear memory associated to op */
+void mpr_clear(mpr *op);
+
+ /* set rop to the value of op */
+void mpr_set(mpr *rop, mpr *op);
+
+ /* set rop to num/den */
+void mpr_seti(mpr *rop, long num, long den);
+
+ /* set rop to the value of d */
+void mpr_setd(mpr *rop, double d);
+
+ /* initialize rop to number representation in str in the given base.
+ * leading zeros are skipped.
+ * if sign present, it is processed.
+ * base must be in the range 2 to 36. */
+void mpr_setstr(mpr *rop, char *str, int base);
+
+ /* remove common factors of op */
+void mpr_canonicalize(mpr *op);
+
+ /* adds two mp rationals */
+void mpr_add(mpr *rop, mpr *op1, mpr *op2);
+
+ /* adds op1 and op2 */
+void mpr_addi(mpr *rop, mpr *op1, long op2);
+
+ /* subtracts two mp rationals */
+void mpr_sub(mpr *rop, mpr *op1, mpr *op2);
+
+ /* subtracts op2 from op1 */
+void mpr_subi(mpr *rop, mpr *op1, long op2);
+
+ /* multiply two mp rationals */
+void mpr_mul(mpr *rop, mpr *op1, mpr *op2);
+
+ /* multiply op1 by op2 */
+void mpr_muli(mpr *rop, mpr *op1, long op2);
+
+ /* divide two mp rationals */
+void mpr_div(mpr *rop, mpr *op1, mpr *op2);
+
+ /* divides op1 by op2 */
+void mpr_divi(mpr *rop, mpr *op1, long op2);
+
+ /* sets rop to 1/op */
+void mpr_inv(mpr *rop, mpr *op);
+
+ /* sets rop to -op */
+void mpr_neg(mpr *rop, mpr *op);
+
+ /* sets rop to the absolute value of op */
+void mpr_abs(mpr *rop, mpr *op);
+
+ /* compares op1 and op2
+ * returns >0 if op1 > op2, 0 if op1 = op2, and <0 if op1 < op2 */
+int mpr_cmp(mpr *op1, mpr *op2);
+
+ /* mpr_cmp with a long integer operand */
+int mpr_cmpi(mpr *op1, long op2);
+
+ /* compares absolute value of op1 and op2
+ * returns >0 if abs(op1) > abs(op2), 0 if abs(op1) = abs(op2),
+ * and <0 if abs(op1) < abs(op2) */
+int mpr_cmpabs(mpr *op1, mpr *op2);
+
+ /* mpr_cmpabs with a long integer operand */
+int mpr_cmpabsi(mpr *op1, long op2);
+
+ /* fastly swaps contents of op1 and op2 */
+void mpr_swap(mpr *op1, mpr *op2);
+
+ /* returns 1 if op fits in a signed long int, 0 otherwise */
+int mpr_fiti(mpr *op);
+
+ /* convert mp rational to double */
+double mpr_getd(mpr *op);
+
+ /* returns pointer to string with representation of mp rational
+ * if str is not NULL, it must have enough space to store rational
+ * representation, if NULL a newly allocated string is returned.
+ * base must be in the range 2 to 36 */
+char *mpr_getstr(char *str, mpr *op, int base);
+
+#endif /* __mp_h_ */
diff --git a/lisp/mp/mpi.c b/lisp/mp/mpi.c
new file mode 100644
index 0000000..506dc7e
--- /dev/null
+++ b/lisp/mp/mpi.c
@@ -0,0 +1,1656 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/mp/mpi.c,v 1.12 2002/11/20 07:44:43 paulo Exp $ */
+
+#include "mp.h"
+
+/*
+ * Prototypes
+ */
+ /* do the hard work of mpi_add and mpi_sub */
+static void mpi_addsub(mpi *rop, mpi *op1, mpi *op2, int sub);
+
+ /* logical functions implementation */
+static INLINE BNS mpi_logic(BNS op1, BNS op2, BNS op);
+static void mpi_log(mpi *rop, mpi *op1, mpi *op2, BNS op);
+
+ /* internal mpi_seti, whithout memory allocation */
+static void _mpi_seti(mpi *rop, long si);
+
+/*
+ * Initialization
+ */
+static BNS onedig[1] = { 1 };
+static mpi mpone = { 1, 1, 0, (BNS*)&onedig };
+
+/*
+ * Implementation
+ */
+void
+mpi_init(mpi *op)
+{
+ op->sign = 0;
+ op->size = op->alloc = 1;
+ op->digs = mp_malloc(sizeof(BNS));
+ op->digs[0] = 0;
+}
+
+void
+mpi_clear(mpi *op)
+{
+ op->sign = 0;
+ op->size = op->alloc = 0;
+ mp_free(op->digs);
+}
+
+void
+mpi_set(mpi *rop, mpi *op)
+{
+ if (rop != op) {
+ if (rop->alloc < op->size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * op->size);
+ rop->alloc = op->size;
+ }
+ rop->size = op->size;
+ memcpy(rop->digs, op->digs, sizeof(BNS) * op->size);
+ rop->sign = op->sign;
+ }
+}
+
+void
+mpi_seti(mpi *rop, long si)
+{
+ unsigned long ui;
+ int sign = si < 0;
+ int size;
+
+ if (si == MINSLONG) {
+ ui = MINSLONG;
+ size = 2;
+ }
+ else {
+ if (sign)
+ ui = -si;
+ else
+ ui = si;
+ if (ui < CARRY)
+ size = 1;
+ else
+ size = 2;
+ }
+
+ if (rop->alloc < size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * size);
+ rop->alloc = size;
+ }
+ rop->size = size;
+
+ /* store data in small mp integer */
+ rop->digs[0] = (BNS)ui;
+ if (size > 1)
+ rop->digs[1] = (BNS)(ui >> BNSBITS);
+ rop->size = size;
+
+ /* adjust result sign */
+ rop->sign = sign;
+}
+
+static void
+_mpi_seti(mpi *rop, long si)
+{
+ unsigned long ui;
+ int sign = si < 0;
+ int size;
+
+ if (si == MINSLONG) {
+ ui = MINSLONG;
+ size = 2;
+ }
+ else {
+ if (sign)
+ ui = -si;
+ else
+ ui = si;
+ if (ui < CARRY)
+ size = 1;
+ else
+ size = 2;
+ }
+
+ rop->digs[0] = (BNS)ui;
+ if (size > 1)
+ rop->digs[1] = (BNS)(ui >> BNSBITS);
+ rop->size = size;
+
+ rop->sign = sign;
+}
+
+void
+mpi_setd(mpi *rop, double d)
+{
+ long i;
+ double mantissa;
+ int shift, exponent;
+ BNI size;
+
+ if (isnan(d))
+ d = 0.0;
+ else if (!finite(d))
+ d = copysign(1.0, d) * DBL_MAX;
+
+ /* check if number is larger than 1 */
+ if (fabs(d) < 1.0) {
+ rop->digs[0] = 0;
+ rop->size = 1;
+ rop->sign = d < 0.0;
+
+ return;
+ }
+
+ mantissa = frexp(d, &exponent);
+ if (mantissa < 0)
+ mantissa = -mantissa;
+
+ size = (exponent + (BNSBITS - 1)) / BNSBITS;
+ shift = BNSBITS - (exponent & (BNSBITS - 1));
+
+ /* adjust amount of memory */
+ if (rop->alloc < size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * size);
+ rop->alloc = size;
+ }
+ rop->size = size;
+
+ /* adjust the exponent */
+ if (shift < BNSBITS)
+ mantissa = ldexp(mantissa, -shift);
+
+ /* convert double */
+ for (i = size - 1; i >= 0 && mantissa != 0.0; i--) {
+ mantissa = ldexp(mantissa, BNSBITS);
+ rop->digs[i] = (BNS)mantissa;
+ mantissa -= rop->digs[i];
+ }
+ for (; i >= 0; i--)
+ rop->digs[i] = 0;
+
+ /* normalize */
+ if (size > 1 && rop->digs[size - 1] == 0)
+ --rop->size;
+
+ rop->sign = d < 0.0;
+}
+
+/* how many BNS in the given base, log(base) / log(CARRY) */
+#ifdef LONG64
+static double str_bases[37] = {
+ 0.0000000000000000, 0.0000000000000000, 0.0312500000000000,
+ 0.0495300781475362, 0.0625000000000000, 0.0725602529652301,
+ 0.0807800781475362, 0.0877298413143002, 0.0937500000000000,
+ 0.0990601562950723, 0.1038102529652301, 0.1081072380824156,
+ 0.1120300781475362, 0.1156387411919092, 0.1189798413143002,
+ 0.1220903311127662, 0.1250000000000000, 0.1277332137890731,
+ 0.1303101562950723, 0.1327477347951120, 0.1350602529652300,
+ 0.1372599194618363, 0.1393572380824156, 0.1413613111267817,
+ 0.1432800781475362, 0.1451205059304602, 0.1468887411919092,
+ 0.1485902344426084, 0.1502298413143002, 0.1518119060977367,
+ 0.1533403311127662, 0.1548186346995899, 0.1562500000000000,
+ 0.1576373162299517, 0.1589832137890731, 0.1602900942795302,
+ 0.1615601562950723,
+};
+#else
+static double str_bases[37] = {
+ 0.0000000000000000, 0.0000000000000000, 0.0625000000000000,
+ 0.0990601562950723, 0.1250000000000000, 0.1451205059304602,
+ 0.1615601562950723, 0.1754596826286003, 0.1875000000000000,
+ 0.1981203125901446, 0.2076205059304602, 0.2162144761648311,
+ 0.2240601562950723, 0.2312774823838183, 0.2379596826286003,
+ 0.2441806622255325, 0.2500000000000000, 0.2554664275781462,
+ 0.2606203125901445, 0.2654954695902241, 0.2701205059304602,
+ 0.2745198389236725, 0.2787144761648311, 0.2827226222535633,
+ 0.2865601562950723, 0.2902410118609203, 0.2937774823838183,
+ 0.2971804688852168, 0.3004596826286003, 0.3036238121954733,
+ 0.3066806622255324, 0.3096372693991797, 0.3125000000000000,
+ 0.3152746324599034, 0.3179664275781462, 0.3205801885590604,
+ 0.3231203125901446,
+};
+#endif
+
+void
+mpi_setstr(mpi *rop, char *str, int base)
+{
+ long i; /* counter */
+ int sign; /* result sign */
+ BNI carry; /* carry value */
+ BNI value; /* temporary value */
+ BNI size; /* size of result */
+ char *ptr; /* end of valid input */
+
+ /* initialization */
+ sign = 0;
+ carry = 0;
+
+ /* skip leading spaces */
+ while (isspace(*str))
+ ++str;
+
+ /* check if sign supplied */
+ if (*str == '-') {
+ sign = 1;
+ ++str;
+ }
+ else if (*str == '+')
+ ++str;
+
+ /* skip leading zeros */
+ while (*str == '0')
+ ++str;
+
+ ptr = str;
+ while (*ptr) {
+ if (*ptr >= '0' && *ptr <= '9') {
+ if (*ptr - '0' >= base)
+ break;
+ }
+ else if (*ptr >= 'A' && *ptr <= 'Z') {
+ if (*ptr - 'A' + 10 >= base)
+ break;
+ }
+ else if (*ptr >= 'a' && *ptr <= 'z') {
+ if (*ptr - 'a' + 10 >= base)
+ break;
+ }
+ else
+ break;
+ ++ptr;
+ }
+
+ /* resulting size */
+ size = (ptr - str) * str_bases[base] + 1;
+
+ /* make sure rop has enough storage */
+ if (rop->alloc < size) {
+ rop->digs = mp_realloc(rop->digs, size * sizeof(BNS));
+ rop->alloc = size;
+ }
+ rop->size = size;
+
+ /* initialize rop to zero */
+ memset(rop->digs, '\0', size * sizeof(BNS));
+
+ /* set result sign */
+ rop->sign = sign;
+
+ /* convert string */
+ for (; str < ptr; str++) {
+ value = *str;
+ if (islower(value))
+ value = toupper(value);
+ value = value > '9' ? value - 'A' + 10 : value - '0';
+ value += rop->digs[0] * base;
+ carry = value >> BNSBITS;
+ rop->digs[0] = (BNS)value;
+ for (i = 1; i < size; i++) {
+ value = (BNI)rop->digs[i] * base + carry;
+ carry = value >> BNSBITS;
+ rop->digs[i] = (BNS)value;
+ }
+ }
+
+ /* normalize */
+ if (rop->size > 1 && rop->digs[rop->size - 1] == 0)
+ --rop->size;
+}
+
+void
+mpi_add(mpi *rop, mpi *op1, mpi *op2)
+{
+ mpi_addsub(rop, op1, op2, 0);
+}
+
+void
+mpi_addi(mpi *rop, mpi *op1, long op2)
+{
+ BNS digs[2];
+ mpi op;
+
+ op.digs = (BNS*)digs;
+ _mpi_seti(&op, op2);
+
+ mpi_addsub(rop, op1, &op, 0);
+}
+
+void
+mpi_sub(mpi *rop, mpi *op1, mpi *op2)
+{
+ mpi_addsub(rop, op1, op2, 1);
+}
+
+void
+mpi_subi(mpi *rop, mpi *op1, long op2)
+{
+ BNS digs[2];
+ mpi op;
+
+ op.digs = (BNS*)digs;
+ _mpi_seti(&op, op2);
+
+ mpi_addsub(rop, op1, &op, 1);
+}
+
+static void
+mpi_addsub(mpi *rop, mpi *op1, mpi *op2, int sub)
+{
+ long xlen; /* maximum result size */
+
+ if (sub ^ (op1->sign == op2->sign)) {
+ /* plus one for possible carry */
+ xlen = MAX(op1->size, op2->size) + 1;
+ if (rop->alloc < xlen) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * xlen);
+ rop->alloc = xlen;
+ }
+ rop->size = mp_add(rop->digs, op1->digs, op2->digs,
+ op1->size, op2->size);
+ rop->sign = op1->sign;
+ }
+ else {
+ long cmp; /* check for larger operator */
+
+ cmp = mpi_cmpabs(op1, op2);
+ if (cmp == 0) {
+ rop->digs[0] = 0;
+ rop->size = 1;
+ rop->sign = 0;
+ }
+ else {
+ xlen = MAX(op1->size, op2->size);
+ if (rop->alloc < xlen) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * xlen);
+ rop->alloc = xlen;
+ }
+ if (cmp > 0) {
+ rop->size = mp_sub(rop->digs, op1->digs, op2->digs,
+ op1->size, op2->size);
+ rop->sign = op1->sign;
+ }
+ else {
+ rop->size = mp_sub(rop->digs, op2->digs, op1->digs,
+ op2->size, op1->size);
+ rop->sign = sub ^ op2->sign;
+ }
+ }
+ }
+}
+
+void
+mpi_mul(mpi *rop, mpi *op1, mpi *op2)
+{
+ int sign; /* sign flag */
+ BNS *digs; /* result data */
+ long xsize; /* result size */
+
+ /* get result sign */
+ sign = op1->sign ^ op2->sign;
+
+ /* check for special cases */
+ if (op1->size == 1) {
+ if (*op1->digs == 0) {
+ /* multiply by 0 */
+ mpi_seti(rop, 0);
+ return;
+ }
+ else if (*op1->digs == 1) {
+ /* multiply by +-1 */
+ if (rop->alloc < op2->size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * op2->size);
+ rop->alloc = op2->size;
+ }
+ rop->size = op2->size;
+ memmove(rop->digs, op2->digs, sizeof(BNS) * op2->size);
+ rop->sign = op2->size > 1 || *op2->digs ? sign : 0;
+
+ return;
+ }
+ }
+ else if (op2->size == 1) {
+ if (*op2->digs == 0) {
+ /* multiply by 0 */
+ mpi_seti(rop, 0);
+ return;
+ }
+ else if (*op2->digs == 1) {
+ /* multiply by +-1 */
+ if (rop->alloc < op1->size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * op1->size);
+ rop->alloc = op1->size;
+ }
+ rop->size = op1->size;
+ memmove(rop->digs, op1->digs, sizeof(BNS) * op1->size);
+ rop->sign = op1->size > 1 || *op1->digs ? sign : 0;
+
+ return;
+ }
+ }
+
+ /* allocate result data and set it to zero */
+ xsize = op1->size + op2->size;
+ if (rop->digs == op1->digs || rop->digs == op2->digs)
+ /* rop is also an operand */
+ digs = mp_calloc(1, sizeof(BNS) * xsize);
+ else {
+ if (rop->alloc < xsize) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * xsize);
+ rop->alloc = xsize;
+ }
+ digs = rop->digs;
+ memset(digs, '\0', sizeof(BNS) * xsize);
+ }
+
+ /* multiply operands */
+ xsize = mp_mul(digs, op1->digs, op2->digs, op1->size, op2->size);
+
+ /* store result in rop */
+ if (digs != rop->digs) {
+ /* if rop was an operand, free old data */
+ mp_free(rop->digs);
+ rop->digs = digs;
+ }
+ rop->size = xsize;
+
+ /* set result sign */
+ rop->sign = sign;
+}
+
+void
+mpi_muli(mpi *rop, mpi *op1, long op2)
+{
+ BNS digs[2];
+ mpi op;
+
+ op.digs = (BNS*)digs;
+ _mpi_seti(&op, op2);
+
+ mpi_mul(rop, op1, &op);
+}
+
+void
+mpi_div(mpi *rop, mpi *num, mpi *den)
+{
+ mpi_divqr(rop, NULL, num, den);
+}
+
+void
+mpi_rem(mpi *rop, mpi *num, mpi *den)
+{
+ mpi_divqr(NULL, rop, num, den);
+}
+
+/*
+ * Could/should be changed to not allocate qdigs if qrop is NULL
+ * Performance wouldn't suffer too much with a test on every loop iteration.
+ */
+void
+mpi_divqr(mpi *qrop, mpi *rrop, mpi *num, mpi *den)
+{
+ long i, j; /* counters */
+ int qsign; /* sign of quotient */
+ int rsign; /* sign of remainder */
+ BNI qsize; /* size of quotient */
+ BNI rsize; /* size of remainder */
+ BNS qest; /* estimative of quotient value */
+ BNS *qdigs, *rdigs; /* work copy or result */
+ BNS *ndigs, *ddigs; /* work copy or divisor and dividend */
+ BNI value; /* temporary result */
+ long svalue; /* signed temporary result (2's complement) */
+ BNS carry, scarry, denorm; /* carry and normalization */
+ BNI dpos, npos; /* offsets in data */
+
+ /* get signs */
+ rsign = num->sign;
+ qsign = rsign ^ den->sign;
+
+ /* check for special case */
+ if (num->size < den->size) {
+ /* quotient is zero and remainder is numerator */
+ if (rrop && rrop->digs != num->digs) {
+ if (rrop->alloc < num->size) {
+ rrop->digs = mp_realloc(rrop->digs, sizeof(BNS) * num->size);
+ rrop->alloc = num->size;
+ }
+ rrop->size = num->size;
+ memcpy(rrop->digs, num->digs, sizeof(BNS) * num->size);
+ rrop->sign = rsign;
+ }
+ if (qrop)
+ mpi_seti(qrop, 0);
+
+ return;
+ }
+
+ /* estimate result sizes */
+ rsize = den->size;
+ qsize = num->size - den->size + 1;
+
+ /* offsets */
+ npos = num->size - 1;
+ dpos = den->size - 1;
+
+ /* allocate space for quotient and remainder */
+ if (qrop == NULL || qrop->digs == num->digs || qrop->digs == den->digs)
+ qdigs = mp_calloc(1, sizeof(BNS) * qsize);
+ else {
+ if (qrop->alloc < qsize) {
+ qrop->digs = mp_realloc(qrop->digs, sizeof(BNS) * qsize);
+ qrop->alloc = qsize;
+ }
+ memset(qrop->digs, '\0', sizeof(BNS) * qsize);
+ qdigs = qrop->digs;
+ }
+ if (rrop) {
+ if (rrop->digs == num->digs || rrop->digs == den->digs)
+ rdigs = mp_calloc(1, sizeof(BNS) * rsize);
+ else {
+ if (rrop->alloc < rsize) {
+ rrop->digs = mp_realloc(rrop->digs, sizeof(BNS) * rsize);
+ rrop->alloc = rsize;
+ }
+ memset(rrop->digs, '\0', sizeof(BNS) * rsize);
+ rdigs = rrop->digs;
+ }
+ }
+ else
+ rdigs = NULL; /* fix gcc warning */
+
+ /* special case, only one word in divisor */
+ if (dpos == 0) {
+ for (carry = 0, i = npos; i >= 0; i--) {
+ value = ((BNI)carry << BNSBITS) + num->digs[i];
+ qdigs[i] = (BNS)(value / den->digs[0]);
+ carry = (BNS)(value % den->digs[0]);
+ }
+ if (rrop)
+ rdigs[0] = carry;
+
+ goto mpi_divqr_done;
+ }
+
+ /* make work copy of numerator */
+ ndigs = mp_malloc(sizeof(BNS) * (num->size + 1));
+ /* allocate one extra word an update offset */
+ memcpy(ndigs, num->digs, sizeof(BNS) * num->size);
+ ndigs[num->size] = 0;
+ ++npos;
+
+ /* normalize */
+ denorm = (BNS)((BNI)CARRY / ((BNI)(den->digs[dpos]) + 1));
+
+ if (denorm > 1) {
+ /* i <= num->size because ndigs has an extra word */
+ for (carry = 0, i = 0; i <= num->size; i++) {
+ value = ndigs[i] * (BNI)denorm + carry;
+ ndigs[i] = (BNS)value;
+ carry = (BNS)(value >> BNSBITS);
+ }
+ /* make work copy of denominator */
+ ddigs = mp_malloc(sizeof(BNS) * den->size);
+ memcpy(ddigs, den->digs, sizeof(BNS) * den->size);
+ for (carry = 0, i = 0; i < den->size; i++) {
+ value = ddigs[i] * (BNI)denorm + carry;
+ ddigs[i] = (BNS)value;
+ carry = (BNS)(value >> BNSBITS);
+ }
+ }
+ else
+ /* only allocate copy of denominator if going to change it */
+ ddigs = den->digs;
+
+ /* divide mp integers */
+ for (j = qsize - 1; j >= 0; j--, npos--) {
+ /* estimate quotient */
+ if (ndigs[npos] == ddigs[dpos])
+ qest = (BNS)SMASK;
+ else
+ qest = (BNS)((((BNI)(ndigs[npos]) << 16) + ndigs[npos - 1]) /
+ ddigs[dpos]);
+
+ while ((value = ((BNI)(ndigs[npos]) << 16) + ndigs[npos - 1] -
+ qest * (BNI)(ddigs[dpos])) < CARRY &&
+ ddigs[dpos - 1] * (BNI)qest >
+ (value << BNSBITS) + ndigs[npos - 2])
+ --qest;
+
+ /* multiply and subtract */
+ carry = scarry = 0;
+ for (i = 0; i < den->size; i++) {
+ value = qest * (BNI)ddigs[i] + carry;
+ carry = (BNS)(value >> BNSBITS);
+ svalue = (long)ndigs[npos - dpos + i - 1] - (long)(value & SMASK) -
+ (long)scarry;
+ ndigs[npos - dpos + i - 1] = (BNS)svalue;
+ scarry = svalue < 0;
+ }
+
+ svalue = (long)ndigs[npos] - (long)(carry & SMASK) - (long)scarry;
+ ndigs[npos] = (BNS)svalue;
+
+ if (svalue & LMASK) {
+ /* quotient too big */
+ --qest;
+ carry = 0;
+ for (i = 0; i < den->size; i++) {
+ value = ndigs[npos - dpos + i - 1] + (BNI)carry + (BNI)ddigs[i];
+ ndigs[npos - dpos + i - 1] = (BNS)value;
+ carry = (BNS)(value >> BNSBITS);
+ }
+ ndigs[npos] += carry;
+ }
+
+ qdigs[j] = qest;
+ }
+
+ /* calculate remainder */
+ if (rrop) {
+ for (carry = 0, j = dpos; j >= 0; j--) {
+ value = ((BNI)carry << BNSBITS) + ndigs[j];
+ rdigs[j] = (BNS)(value / denorm);
+ carry = (BNS)(value % denorm);
+ }
+ }
+
+ mp_free(ndigs);
+ if (ddigs != den->digs)
+ mp_free(ddigs);
+
+mpi_divqr_done:
+ if (rrop) {
+ if (rrop->digs != rdigs)
+ mp_free(rrop->digs);
+ /* normalize remainder */
+ for (i = rsize - 1; i >= 0; i--)
+ if (rdigs[i] != 0)
+ break;
+ if (i != rsize - 1) {
+ if (i < 0) {
+ rsign = 0;
+ rsize = 1;
+ }
+ else
+ rsize = i + 1;
+ }
+ rrop->digs = rdigs;
+ rrop->sign = rsign;
+ rrop->size = rsize;
+ }
+
+ /* normalize quotient */
+ if (qrop) {
+ if (qrop->digs != qdigs)
+ mp_free(qrop->digs);
+ for (i = qsize - 1; i >= 0; i--)
+ if (qdigs[i] != 0)
+ break;
+ if (i != qsize - 1) {
+ if (i < 0) {
+ qsign = 0;
+ qsize = 1;
+ }
+ else
+ qsize = i + 1;
+ }
+ qrop->digs = qdigs;
+ qrop->sign = qsign;
+ qrop->size = qsize;
+ }
+ else
+ mp_free(qdigs);
+}
+
+long
+mpi_divqri(mpi *qrop, mpi *num, long den)
+{
+ BNS ddigs[2];
+ mpi dop, rrop;
+ long remainder;
+
+ dop.digs = (BNS*)ddigs;
+ _mpi_seti(&dop, den);
+
+ memset(&rrop, '\0', sizeof(mpi));
+ mpi_init(&rrop);
+ mpi_divqr(qrop, &rrop, num, &dop);
+ remainder = rrop.digs[0];
+ if (rrop.size > 1)
+ remainder |= (BNI)(rrop.digs[1]) << BNSBITS;
+ if (rrop.sign)
+ remainder = -remainder;
+ mpi_clear(&rrop);
+
+ return (remainder);
+}
+
+void
+mpi_divi(mpi *rop, mpi *num, long den)
+{
+ BNS ddigs[2];
+ mpi dop;
+
+ dop.digs = (BNS*)ddigs;
+ _mpi_seti(&dop, den);
+
+ mpi_divqr(rop, NULL, num, &dop);
+}
+
+long
+mpi_remi(mpi *num, long den)
+{
+ return (mpi_divqri(NULL, num, den));
+}
+
+void
+mpi_mod(mpi *rop, mpi *num, mpi *den)
+{
+ mpi_rem(rop, num, den);
+ if (num->sign ^ den->sign)
+ mpi_add(rop, rop, den);
+}
+
+long
+mpi_modi(mpi *num, long den)
+{
+ long remainder;
+
+ remainder = mpi_remi(num, den);
+ if (num->sign ^ (den < 0))
+ remainder += den;
+
+ return (remainder);
+}
+
+void
+mpi_gcd(mpi *rop, mpi *num, mpi *den)
+{
+ long cmp;
+ mpi rem;
+
+ /* check if result already given */
+ cmp = mpi_cmpabs(num, den);
+
+ /* check if num is equal to den or if num is zero */
+ if (cmp == 0 || (num->size == 1 && num->digs[0] == 0)) {
+ mpi_set(rop, den);
+ rop->sign = 0;
+ return;
+ }
+ /* check if den is not zero */
+ if (den->size == 1 && den->digs[0] == 0) {
+ mpi_set(rop, num);
+ rop->sign = 0;
+ return;
+ }
+
+ /* don't call mpi_init, relies on realloc(0, size) == malloc(size) */
+ memset(&rem, '\0', sizeof(mpi));
+
+ /* if num larger than den */
+ if (cmp > 0) {
+ mpi_rem(&rem, num, den);
+ if (rem.size == 1 && rem.digs[0] == 0) {
+ /* exact division */
+ mpi_set(rop, den);
+ rop->sign = 0;
+ mpi_clear(&rem);
+ return;
+ }
+ mpi_set(rop, den);
+ }
+ else {
+ mpi_rem(&rem, den, num);
+ if (rem.size == 1 && rem.digs[0] == 0) {
+ /* exact division */
+ mpi_set(rop, num);
+ rop->sign = 0;
+ mpi_clear(&rem);
+ return;
+ }
+ mpi_set(rop, num);
+ }
+
+ /* loop using positive values */
+ rop->sign = rem.sign = 0;
+
+ /* cannot optimize this inverting rem/rop assignment earlier
+ * because rop mais be an operand */
+ mpi_swap(rop, &rem);
+
+ /* Euclides algorithm */
+ for (;;) {
+ mpi_rem(&rem, &rem, rop);
+ if (rem.size == 1 && rem.digs[0] == 0)
+ break;
+ mpi_swap(rop, &rem);
+ }
+ mpi_clear(&rem);
+}
+
+void
+mpi_lcm(mpi *rop, mpi *num, mpi *den)
+{
+ mpi gcd;
+
+ /* check for zero operand */
+ if ((num->size == 1 && num->digs[0] == 0) ||
+ (den->size == 1 && den->digs[0] == 0)) {
+ rop->digs[0] = 0;
+ rop->sign = 0;
+ return;
+ }
+
+ /* don't call mpi_init, relies on realloc(0, size) == malloc(size) */
+ memset(&gcd, '\0', sizeof(mpi));
+
+ mpi_gcd(&gcd, num, den);
+ mpi_div(&gcd, den, &gcd);
+ mpi_mul(rop, &gcd, num);
+ rop->sign = 0;
+
+ mpi_clear(&gcd);
+}
+
+void
+mpi_pow(mpi *rop, mpi *op, unsigned long exp)
+{
+ mpi zop, top;
+
+ if (exp == 2) {
+ mpi_mul(rop, op, op);
+ return;
+ }
+ /* check for op**0 */
+ else if (exp == 0) {
+ rop->digs[0] = 1;
+ rop->size = 1;
+ rop->sign = 0;
+ return;
+ }
+ else if (exp == 1) {
+ mpi_set(rop, op);
+ return;
+ }
+ else if (op->size == 1) {
+ if (op->digs[0] == 0) {
+ mpi_seti(rop, 0);
+ return;
+ }
+ else if (op->digs[0] == 1) {
+ mpi_seti(rop, op->sign && (exp & 1) ? -1 : 1);
+ return;
+ }
+ }
+
+ memset(&zop, '\0', sizeof(mpi));
+ memset(&top, '\0', sizeof(mpi));
+ mpi_set(&zop, op);
+ mpi_set(&top, op);
+ for (--exp; exp; exp >>= 1) {
+ if (exp & 1)
+ mpi_mul(&zop, &top, &zop);
+ mpi_mul(&top, &top, &top);
+ }
+
+ mpi_clear(&top);
+ rop->sign = zop.sign;
+ mp_free(rop->digs);
+ rop->digs = zop.digs;
+ rop->size = zop.size;
+}
+
+/* Find integer root of given number using the iteration
+ * x{n+1} = ((K-1) * x{n} + N / x{n}^(K-1)) / K
+ */
+int
+mpi_root(mpi *rop, mpi *op, unsigned long nth)
+{
+ long bits, cmp;
+ int exact;
+ int sign;
+ mpi *r, t, temp, quot, old, rem;
+
+ sign = op->sign;
+
+ /* divide by zero op**1/0 */
+ if (nth == 0) {
+ int one = 1, zero = 0;
+ one = one / zero;
+ }
+ /* result is complex */
+ if (sign && !(nth & 1)) {
+ int one = 1, zero = 0;
+ one = one / zero;
+ }
+
+ /* special case op**1/1 = op */
+ if (nth == 1) {
+ mpi_set(rop, op);
+ return (1);
+ }
+
+ bits = mpi_getsize(op, 2) - 2;
+
+ if (bits < 0 || bits / nth == 0) {
+ /* integral root is surely less than 2 */
+ exact = op->size == 1 && (op->digs[0] == 1 || op->digs[0] == 0);
+ mpi_seti(rop, sign ? -1 : op->digs[0] == 0 ? 0 : 1);
+
+ return (exact == 1);
+ }
+
+ /* initialize */
+ if (rop != op)
+ r = rop;
+ else {
+ r = &t;
+ memset(r, '\0', sizeof(mpi));
+ }
+ memset(&temp, '\0', sizeof(mpi));
+ memset(&quot, '\0', sizeof(mpi));
+ memset(&old, '\0', sizeof(mpi));
+ memset(&rem, '\0', sizeof(mpi));
+
+ if (sign)
+ r->sign = 0;
+
+ /* root aproximation */
+ mpi_ash(r, op, -(bits - (bits / nth)));
+
+ for (;;) {
+ mpi_pow(&temp, r, nth - 1);
+ mpi_divqr(&quot, &rem, op, &temp);
+ cmp = mpi_cmpabs(r, &quot);
+ if (cmp == 0) {
+ exact = mpi_cmpi(&rem, 0) == 0;
+ break;
+ }
+ else if (cmp < 0) {
+ if (mpi_cmpabs(r, &old) == 0) {
+ exact = 0;
+ break;
+ }
+ mpi_set(&old, r);
+ }
+ mpi_muli(&temp, r, nth - 1);
+ mpi_add(&quot, &quot, &temp);
+ mpi_divi(r, &quot, nth);
+ }
+
+ mpi_clear(&temp);
+ mpi_clear(&quot);
+ mpi_clear(&old);
+ mpi_clear(&rem);
+ if (r != rop) {
+ mpi_set(rop, r);
+ mpi_clear(r);
+ }
+ rop->sign = sign;
+
+ return (exact);
+}
+
+/*
+ * Find square root using the iteration:
+ * x{n+1} = (x{n}+N/x{n})/2
+ */
+int
+mpi_sqrt(mpi *rop, mpi *op)
+{
+ long bits, cmp;
+ int exact;
+ mpi *r, t, quot, rem, old;
+
+ /* result is complex */
+ if (op->sign) {
+ int one = 1, zero = 0;
+ one = one / zero;
+ }
+
+ bits = mpi_getsize(op, 2) - 2;
+
+ if (bits < 2) {
+ /* integral root is surely less than 2 */
+ exact = op->size == 1 && (op->digs[0] == 1 || op->digs[0] == 0);
+ mpi_seti(rop, op->digs[0] == 0 ? 0 : 1);
+
+ return (exact == 1);
+ }
+
+ /* initialize */
+ if (rop != op)
+ r = rop;
+ else {
+ r = &t;
+ memset(r, '\0', sizeof(mpi));
+ }
+ memset(&quot, '\0', sizeof(mpi));
+ memset(&rem, '\0', sizeof(mpi));
+ memset(&old, '\0', sizeof(mpi));
+
+ /* root aproximation */
+ mpi_ash(r, op, -(bits - (bits / 2)));
+
+ for (;;) {
+ if (mpi_cmpabs(r, &old) == 0) {
+ exact = 0;
+ break;
+ }
+ mpi_divqr(&quot, &rem, op, r);
+ cmp = mpi_cmpabs(&quot, r);
+ if (cmp == 0) {
+ exact = mpi_cmpi(&rem, 0) == 0;
+ break;
+ }
+ else if (cmp > 0 && rem.size == 1 && rem.digs[0] == 0)
+ mpi_subi(&quot, &quot, 1);
+ mpi_set(&old, r);
+ mpi_add(r, r, &quot);
+ mpi_ash(r, r, -1);
+ }
+ mpi_clear(&quot);
+ mpi_clear(&rem);
+ mpi_clear(&old);
+ if (r != rop) {
+ mpi_set(rop, r);
+ mpi_clear(r);
+ }
+
+ return (exact);
+}
+
+void
+mpi_ash(mpi *rop, mpi *op, long shift)
+{
+ long i; /* counter */
+ long xsize; /* maximum result size */
+ BNS *digs;
+
+ /* check for 0 shift, multiply/divide by 1 */
+ if (shift == 0) {
+ if (rop != op) {
+ if (rop->alloc < op->size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * op->size);
+ rop->alloc = op->size;
+ }
+ rop->size = op->size;
+ memcpy(rop->digs, op->digs, sizeof(BNS) * op->size);
+ }
+
+ return;
+ }
+ else if (op->size == 1 && op->digs[0] == 0) {
+ rop->sign = 0;
+ rop->size = 1;
+ rop->digs[0] = 0;
+
+ return;
+ }
+
+ /* check shift and initialize */
+ if (shift > 0)
+ xsize = op->size + (shift / BNSBITS) + 1;
+ else {
+ xsize = op->size - ((-shift) / BNSBITS);
+ if (xsize <= 0) {
+ rop->size = 1;
+ rop->sign = op->sign;
+ rop->digs[0] = op->sign ? 1 : 0;
+
+ return;
+ }
+ }
+
+ /* allocate/adjust memory for result */
+ if (rop == op)
+ digs = mp_malloc(sizeof(BNS) * xsize);
+ else {
+ if (rop->alloc < xsize) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * xsize);
+ rop->alloc = xsize;
+ }
+ digs = rop->digs;
+ }
+
+ /* left shift, multiply by power of two */
+ if (shift > 0)
+ rop->size = mp_lshift(digs, op->digs, op->size, shift);
+ /* right shift, divide by power of two */
+ else {
+ long carry = 0;
+
+ if (op->sign) {
+ BNI words, bits;
+
+ words = -shift / BNSBITS;
+ bits = -shift % BNSBITS;
+ for (i = 0; i < words; i++)
+ carry |= op->digs[xsize + i];
+ if (!carry) {
+ for (i = 0; i < bits; i++)
+ if (op->digs[op->size - xsize] & (1 << i)) {
+ carry = 1;
+ break;
+ }
+ }
+ }
+ rop->size = mp_rshift(digs, op->digs, op->size, -shift);
+
+ if (carry)
+ /* emulates two's complement subtracting 1 from the result */
+ rop->size = mp_add(digs, digs, mpone.digs, rop->size, 1);
+ }
+
+ if (rop->digs != digs) {
+ mp_free(rop->digs);
+ rop->alloc = rop->size;
+ rop->digs = digs;
+ }
+ rop->sign = op->sign;
+}
+
+static INLINE BNS
+mpi_logic(BNS op1, BNS op2, BNS op)
+{
+ switch (op) {
+ case '&':
+ return (op1 & op2);
+ case '|':
+ return (op1 | op2);
+ case '^':
+ return (op1 ^ op2);
+ }
+
+ return (SMASK);
+}
+
+static void
+mpi_log(mpi *rop, mpi *op1, mpi *op2, BNS op)
+{
+ long i; /* counter */
+ long c, c1, c2; /* carry */
+ BNS *digs, *digs1, *digs2; /* pointers to mp data */
+ BNI size, size1, size2;
+ BNS sign, sign1, sign2;
+ BNS n, n1, n2; /* logical operands */
+ BNI sum;
+
+ /* initialize */
+ size1 = op1->size;
+ size2 = op2->size;
+
+ sign1 = op1->sign ? SMASK : 0;
+ sign2 = op2->sign ? SMASK : 0;
+
+ sign = mpi_logic(sign1, sign2, op);
+
+ size = MAX(size1, size2);
+ if (sign)
+ ++size;
+ if (rop->alloc < size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * size);
+ rop->alloc = size;
+ }
+
+ digs = rop->digs;
+ digs1 = op1->digs;
+ digs2 = op2->digs;
+
+ c = c1 = c2 = 1;
+
+ /* apply logical operation */
+ for (i = 0; i < size; i++) {
+ if (i >= size1)
+ n1 = sign1;
+ else if (sign1) {
+ sum = (BNI)(BNS)(~digs1[i]) + c1;
+ c1 = (long)(sum >> BNSBITS);
+ n1 = (BNS)sum;
+ }
+ else
+ n1 = digs1[i];
+
+ if (i >= size2)
+ n2 = sign2;
+ else if (sign2) {
+ sum = (BNI)(BNS)(~digs2[i]) + c2;
+ c2 = (long)(sum >> BNSBITS);
+ n2 = (BNS)sum;
+ }
+ else
+ n2 = digs2[i];
+
+ n = mpi_logic(n1, n2, op);
+ if (sign) {
+ sum = (BNI)(BNS)(~n) + c;
+ c = (long)(sum >> BNSBITS);
+ digs[i] = (BNS)sum;
+ }
+ else
+ digs[i] = n;
+ }
+
+ /* normalize */
+ for (i = size - 1; i >= 0; i--)
+ if (digs[i] != 0)
+ break;
+ if (i != size - 1) {
+ if (i < 0) {
+ sign = 0;
+ size = 1;
+ }
+ else
+ size = i + 1;
+ }
+
+ rop->sign = sign != 0;
+ rop->size = size;
+}
+
+void
+mpi_and(mpi *rop, mpi *op1, mpi *op2)
+{
+ mpi_log(rop, op1, op2, '&');
+}
+
+void
+mpi_ior(mpi *rop, mpi *op1, mpi *op2)
+{
+ mpi_log(rop, op1, op2, '|');
+}
+
+void
+mpi_xor(mpi *rop, mpi *op1, mpi *op2)
+{
+ mpi_log(rop, op1, op2, '^');
+}
+
+void
+mpi_com(mpi *rop, mpi *op)
+{
+ static BNS digs[1] = { 1 };
+ static mpi one = { 0, 1, 1, (BNS*)&digs };
+
+ mpi_log(rop, rop, &one, '^');
+}
+
+void
+mpi_neg(mpi *rop, mpi *op)
+{
+ int sign = op->sign ^ 1;
+
+ if (rop->digs != op->digs) {
+ if (rop->alloc < op->size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * rop->size);
+ rop->alloc = op->size;
+ }
+ rop->size = op->size;
+ memcpy(rop->digs, op->digs, sizeof(BNS) * rop->size);
+ }
+
+ rop->sign = sign;
+}
+
+void
+mpi_abs(mpi *rop, mpi *op)
+{
+ if (rop->digs != op->digs) {
+ if (rop->alloc < op->size) {
+ rop->digs = mp_realloc(rop->digs, sizeof(BNS) * rop->size);
+ rop->alloc = op->size;
+ }
+ rop->size = op->size;
+ memcpy(rop->digs, op->digs, sizeof(BNS) * rop->size);
+ }
+
+ rop->sign = 0;
+}
+
+int
+mpi_cmp(mpi *op1, mpi *op2)
+{
+ if (op1->sign ^ op2->sign)
+ return (op1->sign ? -1 : 1);
+
+ if (op1->size == op2->size) {
+ long i, cmp = 0;
+
+ for (i = op1->size - 1; i >= 0; i--)
+ if ((cmp = (long)op1->digs[i] - (long)op2->digs[i]) != 0)
+ break;
+
+ return (cmp == 0 ? 0 : (cmp < 0) ^ op1->sign ? -1 : 1);
+ }
+
+ return ((op1->size < op2->size) ^ op1->sign ? -1 : 1);
+}
+
+int
+mpi_cmpi(mpi *op1, long op2)
+{
+ long cmp;
+
+ if (op1->size > 2)
+ return (op1->sign ? -1 : 1);
+
+ cmp = op1->digs[0];
+ if (op1->size == 2) {
+ cmp |= (long)op1->digs[1] << BNSBITS;
+ if (cmp == MINSLONG)
+ return (op2 == cmp && op1->sign ? 0 : op1->sign ? -1 : 1);
+ }
+ if (op1->sign)
+ cmp = -cmp;
+
+ return (cmp - op2);
+}
+
+int
+mpi_cmpabs(mpi *op1, mpi *op2)
+{
+ if (op1->size == op2->size) {
+ long i, cmp = 0;
+
+ for (i = op1->size - 1; i >= 0; i--)
+ if ((cmp = (long)op1->digs[i] - (long)op2->digs[i]) != 0)
+ break;
+
+ return (cmp);
+ }
+
+ return ((op1->size < op2->size) ? -1 : 1);
+}
+
+int
+mpi_cmpabsi(mpi *op1, long op2)
+{
+ unsigned long cmp;
+
+ if (op1->size > 2)
+ return (1);
+
+ cmp = op1->digs[0];
+ if (op1->size == 2)
+ cmp |= (unsigned long)op1->digs[1] << BNSBITS;
+
+ return (cmp > op2 ? 1 : cmp == op2 ? 0 : -1);
+}
+
+int
+mpi_sgn(mpi *op)
+{
+ return (op->sign ? -1 : op->size > 1 || op->digs[0] ? 1 : 0);
+}
+
+void
+mpi_swap(mpi *op1, mpi *op2)
+{
+ if (op1 != op2) {
+ mpi swap;
+
+ memcpy(&swap, op1, sizeof(mpi));
+ memcpy(op1, op2, sizeof(mpi));
+ memcpy(op2, &swap, sizeof(mpi));
+ }
+}
+
+int
+mpi_fiti(mpi *op)
+{
+ if (op->size == 1)
+ return (1);
+ else if (op->size == 2) {
+ unsigned long value = ((BNI)(op->digs[1]) << BNSBITS) | op->digs[0];
+
+ if (value & MINSLONG)
+ return (op->sign && value == MINSLONG) ? 1 : 0;
+
+ return (1);
+ }
+
+ return (0);
+}
+
+long
+mpi_geti(mpi *op)
+{
+ long value;
+
+ value = op->digs[0];
+ if (op->size > 1)
+ value |= (BNI)(op->digs[1]) << BNSBITS;
+
+ return (op->sign && value != MINSLONG ? -value : value);
+}
+
+double
+mpi_getd(mpi *op)
+{
+ long i, len;
+ double d = 0.0;
+ int exponent;
+
+#define FLOATDIGS sizeof(double) / sizeof(BNS)
+
+ switch (op->size) {
+ case 2:
+ d = (BNI)(op->digs[1]) << BNSBITS;
+ case 1:
+ d += op->digs[0];
+ return (op->sign ? -d : d);
+ default:
+ break;
+ }
+
+ for (i = 0, len = op->size; len > 0 && i < FLOATDIGS; i++)
+ d = ldexp(d, BNSBITS) + op->digs[--len];
+ d = frexp(d, &exponent);
+ if (len > 0)
+ exponent += len * BNSBITS;
+
+ if (d == 0.0)
+ return (d);
+
+ d = ldexp(d, exponent);
+
+ return (op->sign ? -d : d);
+}
+
+/* how many digits in a given base, floor(log(CARRY) / log(base)) */
+#ifdef LONG64
+static char dig_bases[37] = {
+ 0, 0, 32, 20, 16, 13, 12, 11, 10, 10, 9, 9, 8, 8, 8, 8,
+ 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6,
+};
+#else
+static char dig_bases[37] = {
+ 0, 0, 16, 10, 8, 6, 6, 5, 5, 5, 4, 4, 4, 4, 4, 4,
+ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3,
+};
+#endif
+
+/* how many digits per bit in a given base, log(2) / log(base) */
+static double bit_bases[37] = {
+ 0.0000000000000000, 0.0000000000000000, 1.0000000000000000,
+ 0.6309297535714575, 0.5000000000000000, 0.4306765580733931,
+ 0.3868528072345416, 0.3562071871080222, 0.3333333333333334,
+ 0.3154648767857287, 0.3010299956639811, 0.2890648263178878,
+ 0.2789429456511298, 0.2702381544273197, 0.2626495350371936,
+ 0.2559580248098155, 0.2500000000000000, 0.2446505421182260,
+ 0.2398124665681315, 0.2354089133666382, 0.2313782131597592,
+ 0.2276702486969530, 0.2242438242175754, 0.2210647294575037,
+ 0.2181042919855316, 0.2153382790366965, 0.2127460535533632,
+ 0.2103099178571525, 0.2080145976765095, 0.2058468324604344,
+ 0.2037950470905062, 0.2018490865820999, 0.2000000000000000,
+ 0.1982398631705605, 0.1965616322328226, 0.1949590218937863,
+ 0.1934264036172708,
+};
+
+/* normalization base for string conversion, pow(base, dig_bases[base]) & ~CARRY */
+#ifdef LONG64
+static BNS big_bases[37] = {
+ 0x00000001, 0x00000001, 0x00000000, 0xCFD41B91, 0x00000000, 0x48C27395,
+ 0x81BF1000, 0x75DB9C97, 0x40000000, 0xCFD41B91, 0x3B9ACA00, 0x8C8B6D2B,
+ 0x19A10000, 0x309F1021, 0x57F6C100, 0x98C29B81, 0x00000000, 0x18754571,
+ 0x247DBC80, 0x3547667B, 0x4C4B4000, 0x6B5A6E1D, 0x94ACE180, 0xCAF18367,
+ 0x0B640000, 0x0E8D4A51, 0x1269AE40, 0x17179149, 0x1CB91000, 0x23744899,
+ 0x2B73A840, 0x34E63B41, 0x40000000, 0x4CFA3CC1, 0x5C13D840, 0x6D91B519,
+ 0x81BF1000,
+};
+#else
+static BNS big_bases[37] = {
+ 0x0001, 0x0001, 0x0000, 0xE6A9, 0x0000, 0x3D09, 0xB640, 0x41A7, 0x8000,
+ 0xE6A9, 0x2710, 0x3931, 0x5100, 0x6F91, 0x9610, 0xC5C1, 0x0000, 0x1331,
+ 0x16C8, 0x1ACB, 0x1F40, 0x242D, 0x2998, 0x2F87, 0x3600, 0x3D09, 0x44A8,
+ 0x4CE3, 0x55C0, 0x5F45, 0x6978, 0x745F, 0x8000, 0x8C61, 0x9988, 0xA77B,
+ 0xb640,
+};
+#endif
+
+unsigned long
+mpi_getsize(mpi *op, int base)
+{
+ unsigned long value, bits;
+
+ value = op->digs[op->size - 1];
+
+ /* count leading bits */
+ if (value) {
+ unsigned long count, carry;
+
+ for (count = 0, carry = CARRY >> 1; carry; count++, carry >>= 1)
+ if (value & carry)
+ break;
+
+ bits = BNSBITS - count;
+ }
+ else
+ bits = 0;
+
+ return ((bits + (op->size - 1) * BNSBITS) * bit_bases[base] + 1);
+}
+
+char *
+mpi_getstr(char *str, mpi *op, int base)
+{
+ long i; /* counter */
+ BNS *digs, *xdigs; /* copy of op data */
+ BNI size; /* size of op */
+ BNI digits; /* digits per word in given base */
+ BNI bigbase; /* big base of given base */
+ BNI strsize; /* size of resulting string */
+ char *cp; /* pointer in str for conversion */
+
+ /* initialize */
+ size = op->size;
+ strsize = mpi_getsize(op, base) + op->sign + 1;
+
+ if (str == NULL)
+ str = mp_malloc(strsize);
+
+ /* check for zero */
+ if (size == 1 && op->digs[0] == 0) {
+ str[0] = '0';
+ str[1] = '\0';
+
+ return (str);
+ }
+
+ digits = dig_bases[base];
+ bigbase = big_bases[base];
+
+ cp = str + strsize;
+ *--cp = '\0';
+
+ /* make copy of op data and adjust digs */
+ xdigs = mp_malloc(size * sizeof(BNS));
+ memcpy(xdigs, op->digs, size * sizeof(unsigned short));
+ digs = xdigs + size - 1;
+
+ /* convert to string */
+ for (;;) {
+ long count = -1;
+ BNI value;
+ BNS quotient, remainder = 0;
+
+ /* if power of two base */
+ if ((base & (base - 1)) == 0) {
+ for (i = 0; i < size; i++) {
+ quotient = remainder;
+ remainder = digs[-i];
+ digs[-i] = quotient;
+ if (count < 0 && quotient)
+ count = i;
+ }
+ }
+ else {
+ for (i = 0; i < size; i++) {
+ value = digs[-i] + ((BNI)remainder << BNSBITS);
+ quotient = (BNS)(value / bigbase);
+ remainder = (BNS)(value % bigbase);
+ digs[-i] = quotient;
+ if (count < 0 && quotient)
+ count = i;
+ }
+ }
+ quotient = remainder;
+ for (i = 0; i < digits; i++) {
+ if (quotient == 0 && count < 0)
+ break;
+ remainder = quotient % base;
+ quotient /= base;
+ *--cp = remainder < 10 ? remainder + '0' : remainder - 10 + 'A';
+ }
+ if (count < 0)
+ break;
+ digs -= count;
+ size -= count;
+ }
+
+ /* adjust sign */
+ if (op->sign)
+ *--cp = '-';
+
+ /* remove any extra characters */
+ if (cp > str)
+ strcpy(str, cp);
+
+ mp_free(xdigs);
+
+ return (str);
+}
diff --git a/lisp/mp/mpr.c b/lisp/mp/mpr.c
new file mode 100644
index 0000000..8b26fe0
--- /dev/null
+++ b/lisp/mp/mpr.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/mp/mpr.c,v 1.2 2002/11/08 08:01:00 paulo Exp $ */
+
+#include "mp.h"
+
+/*
+ * TODO:
+ * Implement a fast gcd and divexact for integers, so that this code
+ * could be changed to do intermediary calculations faster using smaller
+ * numbers.
+ */
+
+/*
+ * Prototypes
+ */
+ /* do the hard work of mpr_add and mpr_sub */
+static void mpr_addsub(mpr *rop, mpr *op1, mpr *op2, int sub);
+
+ /* do the hard work of mpr_cmp and mpr_cmpabs */
+static int mpr_docmp(mpr *op1, mpr *op2, int sign);
+
+/*
+ * Implementation
+ */
+void
+mpr_init(mpr *op)
+{
+ op->num.digs = mp_malloc(sizeof(BNS));
+ op->num.sign = 0;
+ op->num.size = op->num.alloc = 1;
+ op->num.digs[0] = 0;
+
+ op->den.digs = mp_malloc(sizeof(BNS));
+ op->den.sign = 0;
+ op->den.size = op->den.alloc = 1;
+ op->den.digs[0] = 1;
+}
+
+void
+mpr_clear(mpr *op)
+{
+ op->num.sign = 0;
+ op->num.size = op->num.alloc = 0;
+ mp_free(op->num.digs);
+
+ op->den.sign = 0;
+ op->den.size = op->den.alloc = 0;
+ mp_free(op->den.digs);
+}
+
+void
+mpr_set(mpr *rop, mpr *op)
+{
+ if (rop != op) {
+ mpi_set(mpr_num(rop), mpr_num(op));
+ mpi_set(mpr_den(rop), mpr_den(op));
+ }
+}
+
+void
+mpr_seti(mpr *rop, long num, long den)
+{
+ mpi_seti(mpr_num(rop), num);
+ mpi_seti(mpr_den(rop), den);
+}
+
+void
+mpr_setd(mpr *rop, double d)
+{
+ double val, num;
+ int e, sign;
+
+ /* initialize */
+ if (d < 0) {
+ sign = 1;
+ val = -d;
+ }
+ else {
+ sign = 0;
+ val = d;
+ }
+
+ val = frexp(val, &e);
+ while (modf(val, &num) != 0.0 && val <= DBL_MAX / 2.0) {
+ --e;
+ val *= 2.0;
+ }
+ if (e >= 0) {
+ mpi_setd(mpr_num(rop), d);
+ mpi_seti(mpr_den(rop), 1);
+ }
+ else {
+ mpi_setd(mpr_num(rop), sign ? -num : num);
+ mpi_setd(mpr_den(rop), ldexp(1.0, -e));
+ }
+}
+
+void
+mpr_setstr(mpr *rop, char *str, int base)
+{
+ char *slash = strchr(str, '/');
+
+ mpi_setstr(mpr_num(rop), str, base);
+ if (slash != NULL)
+ mpi_setstr(mpr_den(rop), slash + 1, base);
+ else
+ mpi_seti(mpr_den(rop), 1);
+}
+
+void
+mpr_canonicalize(mpr *op)
+{
+ mpi gcd;
+
+ memset(&gcd, '\0', sizeof(mpi));
+
+ mpi_gcd(&gcd, mpr_num(op), mpr_den(op));
+ if (mpi_cmpabsi(&gcd, 1)) {
+ mpi_div(mpr_num(op), mpr_num(op), &gcd);
+ mpi_div(mpr_den(op), mpr_den(op), &gcd);
+ }
+
+ if (op->den.sign) {
+ op->num.sign = !op->num.sign;
+ op->den.sign = 0;
+ }
+
+ mpi_clear(&gcd);
+}
+
+void
+mpr_add(mpr *rop, mpr *op1, mpr *op2)
+{
+ mpr_addsub(rop, op1, op2, 0);
+}
+
+void
+mpr_addi(mpr *rop, mpr *op1, long op2)
+{
+ mpi prod;
+
+ memset(&prod, '\0', sizeof(mpi));
+
+ mpi_muli(&prod, mpr_den(op1), op2);
+ mpi_add(mpr_num(rop), mpr_num(op1), &prod);
+ mpi_clear(&prod);
+}
+
+void
+mpr_sub(mpr *rop, mpr *op1, mpr *op2)
+{
+ mpr_addsub(rop, op1, op2, 1);
+}
+
+void
+mpr_subi(mpr *rop, mpr *op1, long op2)
+{
+ mpi prod;
+
+ memset(&prod, '\0', sizeof(mpi));
+
+ mpi_muli(&prod, mpr_den(op1), op2);
+ mpi_sub(mpr_num(rop), mpr_num(op1), &prod);
+ mpi_clear(&prod);
+}
+
+static void
+mpr_addsub(mpr *rop, mpr *op1, mpr *op2, int sub)
+{
+ mpi prod1, prod2;
+
+ memset(&prod1, '\0', sizeof(mpi));
+ memset(&prod2, '\0', sizeof(mpi));
+
+ mpi_mul(&prod1, mpr_num(op1), mpr_den(op2));
+ mpi_mul(&prod2, mpr_num(op2), mpr_den(op1));
+
+ if (sub)
+ mpi_sub(mpr_num(rop), &prod1, &prod2);
+ else
+ mpi_add(mpr_num(rop), &prod1, &prod2);
+
+ mpi_clear(&prod1);
+ mpi_clear(&prod2);
+
+ mpi_mul(mpr_den(rop), mpr_den(op1), mpr_den(op2));
+}
+
+void
+mpr_mul(mpr *rop, mpr *op1, mpr *op2)
+{
+ /* check if temporary storage is required */
+ if (op1 == op2 && rop == op1) {
+ mpi prod;
+
+ memset(&prod, '\0', sizeof(mpi));
+
+ mpi_mul(&prod, mpr_num(op1), mpr_num(op2));
+ mpi_mul(mpr_den(rop), mpr_den(op1), mpr_den(op2));
+ mpi_set(mpr_num(rop), &prod);
+
+ mpi_clear(&prod);
+ }
+ else {
+ mpi_mul(mpr_num(rop), mpr_num(op1), mpr_num(op2));
+ mpi_mul(mpr_den(rop), mpr_den(op1), mpr_den(op2));
+ }
+}
+
+void
+mpr_muli(mpr *rop, mpr *op1, long op2)
+{
+ mpi_muli(mpr_num(rop), mpr_num(op1), op2);
+}
+
+void
+mpr_div(mpr *rop, mpr *op1, mpr *op2)
+{
+ /* check if temporary storage is required */
+ if (op1 == op2 && rop == op1) {
+ mpi prod;
+
+ memset(&prod, '\0', sizeof(mpi));
+
+ mpi_mul(&prod, mpr_num(op1), mpr_den(op2));
+ mpi_mul(mpr_den(rop), mpr_num(op2), mpr_den(op1));
+ mpi_set(mpr_num(rop), &prod);
+
+ mpi_clear(&prod);
+ }
+ else {
+ mpi_mul(mpr_num(rop), mpr_num(op1), mpr_den(op2));
+ mpi_mul(mpr_den(rop), mpr_num(op2), mpr_den(op1));
+ }
+}
+
+void
+mpr_divi(mpr *rop, mpr *op1, long op2)
+{
+ mpi_muli(mpr_den(rop), mpr_den(op1), op2);
+}
+
+void
+mpr_inv(mpr *rop, mpr *op)
+{
+ if (rop == op)
+ mpi_swap(mpr_num(op), mpr_den(op));
+ else {
+ mpi_set(mpr_num(rop), mpr_den(op));
+ mpi_set(mpr_den(rop), mpr_num(op));
+ }
+}
+
+void
+mpr_neg(mpr *rop, mpr *op)
+{
+ mpi_neg(mpr_num(rop), mpr_num(op));
+ mpi_set(mpr_den(rop), mpr_den(op));
+}
+
+void
+mpr_abs(mpr *rop, mpr *op)
+{
+ if (mpr_num(op)->sign)
+ mpi_neg(mpr_num(rop), mpr_num(op));
+ else
+ mpi_set(mpr_num(rop), mpr_num(op));
+
+ /* op may not be canonicalized */
+ if (mpr_den(op)->sign)
+ mpi_neg(mpr_den(rop), mpr_den(op));
+ else
+ mpi_set(mpr_den(rop), mpr_den(op));
+}
+
+int
+mpr_cmp(mpr *op1, mpr *op2)
+{
+ return (mpr_docmp(op1, op2, 1));
+}
+
+int
+mpr_cmpi(mpr *op1, long op2)
+{
+ int cmp;
+ mpr rat;
+
+ mpr_init(&rat);
+ mpi_seti(mpr_num(&rat), op2);
+ cmp = mpr_docmp(op1, &rat, 1);
+ mpr_clear(&rat);
+
+ return (cmp);
+}
+
+int
+mpr_cmpabs(mpr *op1, mpr *op2)
+{
+ return (mpr_docmp(op1, op2, 0));
+}
+
+int
+mpr_cmpabsi(mpr *op1, long op2)
+{
+ int cmp;
+ mpr rat;
+
+ mpr_init(&rat);
+ mpi_seti(mpr_num(&rat), op2);
+ cmp = mpr_docmp(op1, &rat, 0);
+ mpr_clear(&rat);
+
+ return (cmp);
+}
+
+static int
+mpr_docmp(mpr *op1, mpr *op2, int sign)
+{
+ int cmp, neg;
+ mpi prod1, prod2;
+
+ neg = 0;
+ if (sign) {
+ /* if op1 is negative */
+ if (mpr_num(op1)->sign ^ mpr_den(op1)->sign) {
+ /* if op2 is positive */
+ if (!(mpr_num(op2)->sign ^ mpr_den(op2)->sign))
+ return (-1);
+ else
+ neg = 1;
+ }
+ /* if op2 is negative */
+ else if (mpr_num(op2)->sign ^ mpr_den(op2)->sign)
+ return (1);
+ /* else same sign */
+ }
+
+ /* if denominators are equal, compare numerators */
+ if (mpi_cmpabs(mpr_den(op1), mpr_den(op2)) == 0) {
+ cmp = mpi_cmpabs(mpr_num(op1), mpr_num(op2));
+ if (cmp == 0)
+ return (0);
+ if (sign && neg)
+ return (cmp < 0 ? 1 : -1);
+ return (cmp);
+ }
+
+ memset(&prod1, '\0', sizeof(mpi));
+ memset(&prod2, '\0', sizeof(mpi));
+
+ /* "divide" op1 by op2
+ * if result is smaller than 1, op1 is smaller than op2 */
+ mpi_mul(&prod1, mpr_num(op1), mpr_den(op2));
+ mpi_mul(&prod2, mpr_num(op2), mpr_den(op1));
+
+ cmp = mpi_cmpabs(&prod1, &prod2);
+
+ mpi_clear(&prod1);
+ mpi_clear(&prod2);
+
+ if (sign && neg)
+ return (cmp < 0 ? 1 : -1);
+ return (cmp);
+}
+
+void
+mpr_swap(mpr *op1, mpr *op2)
+{
+ if (op1 != op2) {
+ mpr swap;
+
+ memcpy(&swap, op1, sizeof(mpr));
+ memcpy(op1, op2, sizeof(mpr));
+ memcpy(op2, &swap, sizeof(mpr));
+ }
+}
+
+int
+mpr_fiti(mpr *op)
+{
+ return (mpi_fiti(mpr_num(op)) && mpi_fiti(mpr_den(op)));
+}
+
+double
+mpr_getd(mpr *op)
+{
+ return (mpi_getd(mpr_num(op)) / mpi_getd(mpr_den(op)));
+}
+
+char *
+mpr_getstr(char *str, mpr *op, int base)
+{
+ int len;
+
+ if (str == NULL) {
+ len = mpi_getsize(mpr_num(op), base) + mpr_num(op)->sign + 1 +
+ mpi_getsize(mpr_den(op), base) + mpr_den(op)->sign + 1;
+
+ str = mp_malloc(len);
+ }
+
+ (void)mpi_getstr(str, mpr_num(op), base);
+ len = strlen(str);
+ str[len] = '/';
+ (void)mpi_getstr(str + len + 1, mpr_den(op), base);
+
+ return (str);
+}
diff --git a/lisp/package.c b/lisp/package.c
new file mode 100644
index 0000000..c046e55
--- /dev/null
+++ b/lisp/package.c
@@ -0,0 +1,865 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/package.c,v 1.20 2002/11/30 23:13:12 paulo Exp $ */
+
+#include "package.h"
+#include "private.h"
+
+/*
+ * Prototypes
+ */
+static int LispDoSymbol(LispObj*, LispAtom*, int, int);
+static LispObj *LispReallyDoSymbols(LispBuiltin*, int, int);
+static LispObj *LispDoSymbols(LispBuiltin*, int, int);
+static LispObj *LispFindSymbol(LispBuiltin*, int);
+static LispObj *LispFindPackageOrDie(LispBuiltin*, LispObj*);
+static void LispDoExport(LispBuiltin*, LispObj*, LispObj*, int);
+static void LispDoImport(LispBuiltin*, LispObj*);
+
+/*
+ * Initialization
+ */
+extern LispProperty *NOPROPERTY;
+static LispObj *Kinternal, *Kexternal, *Kinherited;
+
+/*
+ * Implementation
+ */
+void
+LispPackageInit(void)
+{
+ Kinternal = KEYWORD("INTERNAL");
+ Kexternal = KEYWORD("EXTERNAL");
+ Kinherited = KEYWORD("INHERITED");
+}
+
+LispObj *
+LispFindPackageFromString(char *string)
+{
+ LispObj *list, *package, *nick;
+
+ for (list = PACK; CONSP(list); list = CDR(list)) {
+ package = CAR(list);
+ if (strcmp(THESTR(package->data.package.name), string) == 0)
+ return (package);
+ for (nick = package->data.package.nicknames;
+ CONSP(nick); nick = CDR(nick))
+ if (strcmp(THESTR(CAR(nick)), string) == 0)
+ return (package);
+ }
+
+ return (NIL);
+}
+
+LispObj *
+LispFindPackage(LispObj *name)
+{
+ char *string = NULL;
+
+ if (PACKAGEP(name))
+ return (name);
+
+ if (SYMBOLP(name))
+ string = ATOMID(name);
+ else if (STRINGP(name))
+ string = THESTR(name);
+ else
+ LispDestroy("FIND-PACKAGE: %s is not a string or symbol", STROBJ(name));
+
+ return (LispFindPackageFromString(string));
+}
+
+int
+LispCheckAtomString(char *string)
+{
+ char *ptr;
+
+ if (*string == '\0')
+ return (0);
+
+ for (ptr = string; *ptr; ptr++) {
+ if (islower(*ptr) || strchr("\"\\;#()`'|:", *ptr) ||
+ ((ptr == string || ptr[1] == '\0') && strchr(".,@", *ptr)))
+ return (0);
+ }
+
+ return (1);
+}
+
+/* This function is used to avoid some namespace polution caused by the
+ * way builtin functions are created, all function name arguments enter
+ * the current package, but most of them do not have a property */
+static int
+LispDoSymbol(LispObj *package, LispAtom *atom, int if_extern, int all_packages)
+{
+ int dosymbol;
+
+ /* condition 1: atom package is current package */
+ dosymbol = !all_packages || atom->package == package;
+ if (dosymbol) {
+ /* condition 2: intern and extern symbols or symbol is extern */
+ dosymbol = !if_extern || atom->ext;
+ if (dosymbol) {
+ /* condition 3: atom has properties or is in
+ * the current package */
+ dosymbol = atom->property != NOPROPERTY ||
+ package == lisp__data.keyword ||
+ package == PACKAGE;
+ }
+ }
+
+ return (dosymbol);
+}
+
+static LispObj *
+LispFindPackageOrDie(LispBuiltin *builtin, LispObj *name)
+{
+ LispObj *package;
+
+ package = LispFindPackage(name);
+
+ if (package == NIL)
+ LispDestroy("%s: package %s is not available",
+ STRFUN(builtin), STROBJ(name));
+
+ return (package);
+}
+
+/* package must be of type LispPackage_t, symbol type is checked
+ bypass lisp.c:LispExportSymbol() */
+static void
+LispDoExport(LispBuiltin *builtin,
+ LispObj *package, LispObj *symbol, int export)
+{
+ CHECK_SYMBOL(symbol);
+ if (!export) {
+ if (package == lisp__data.keyword ||
+ symbol->data.atom->package == lisp__data.keyword)
+ LispDestroy("%s: symbol %s cannot be unexported",
+ STRFUN(builtin), STROBJ(symbol));
+ }
+
+ if (package == PACKAGE)
+ symbol->data.atom->ext = export ? 1 : 0;
+ else {
+ int i;
+ char *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 = atom->next;
+ }
+
+ LispDestroy("%s: the symbol %s is not available in package %s",
+ STRFUN(builtin), STROBJ(symbol),
+ THESTR(package->data.package.name));
+ }
+}
+
+static void
+LispDoImport(LispBuiltin *builtin, LispObj *symbol)
+{
+ CHECK_SYMBOL(symbol);
+ LispImportSymbol(symbol);
+}
+
+static LispObj *
+LispReallyDoSymbols(LispBuiltin *builtin, int only_externs, int all_symbols)
+{
+ int i, head = lisp__data.env.length;
+ LispPackage *pack = NULL;
+ LispAtom *atom, *next_atom;
+ LispObj *variable, *package = NULL, *list, *code, *result_form;
+
+ LispObj *init, *body;
+
+ body = ARGUMENT(1);
+ init = ARGUMENT(0);
+
+ /* Prepare for loop */
+ CHECK_CONS(init);
+ variable = CAR(init);
+ CHECK_SYMBOL(variable);
+
+ if (!all_symbols) {
+ /* if all_symbols, a package name is not specified in the init form */
+
+ init = CDR(init);
+ if (!CONSP(init))
+ LispDestroy("%s: missing package name", STRFUN(builtin));
+
+ /* Evaluate package specification */
+ package = EVAL(CAR(init));
+ if (!PACKAGEP(package))
+ package = LispFindPackageOrDie(builtin, package);
+
+ pack = package->data.package.package;
+ }
+
+ result_form = NIL;
+
+ init = CDR(init);
+ if (CONSP(init))
+ result_form = init;
+
+ /* Initialize iteration variable */
+ CHECK_CONSTANT(variable);
+ LispAddVar(variable, NIL);
+ ++lisp__data.env.head;
+
+ for (list = PACK; CONSP(list); list = CDR(list)) {
+ if (all_symbols) {
+ package = CAR(list);
+ pack = package->data.package.package;
+ }
+
+ /* Traverse the symbol list, executing body */
+ for (i = 0; i < STRTBLSZ; i++) {
+ atom = pack->atoms[i];
+ while (atom) {
+ /* 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 (!all_symbols)
+ break;
+ }
+
+ /* Variable is still bound */
+ for (code = result_form; CONSP(code); code = CDR(code))
+ EVAL(CAR(code));
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (NIL);
+}
+
+static LispObj *
+LispDoSymbols(LispBuiltin *builtin, int only_externs, int all_symbols)
+{
+ int did_jump, *pdid_jump = &did_jump;
+ LispObj *result, **presult = &result;
+ LispBlock *block;
+
+ *presult = NIL;
+ *pdid_jump = 1;
+ block = LispBeginBlock(NIL, LispBlockTag);
+ if (setjmp(block->jmp) == 0) {
+ *presult = LispReallyDoSymbols(builtin, only_externs, all_symbols);
+ *pdid_jump = 0;
+ }
+ LispEndBlock(block);
+ if (*pdid_jump)
+ *presult = lisp__data.block.block_ret;
+
+ return (*presult);
+}
+
+LispObj *
+LispFindSymbol(LispBuiltin *builtin, int intern)
+{
+ int i;
+ char *ptr;
+ LispAtom *atom;
+ LispObj *symbol;
+ LispPackage *pack;
+
+ LispObj *string, *package;
+
+ package = ARGUMENT(1);
+ string = ARGUMENT(0);
+
+ CHECK_STRING(string);
+ if (package != UNSPEC)
+ package = LispFindPackageOrDie(builtin, package);
+ else
+ package = PACKAGE;
+
+ /* If got here, package is a LispPackage_t */
+ pack = package->data.package.package;
+
+ /* Search symbol in specified package */
+ ptr = THESTR(string);
+
+ RETURN_COUNT = 1;
+
+ symbol = NULL;
+ /* Fix for current behaviour where NIL and T aren't symbols... */
+ if (STRLEN(string) == 3 && memcmp(ptr, "NIL", 3) == 0)
+ symbol = NIL;
+ else if (STRLEN(string) == 1 && ptr[0] == 'T')
+ symbol = T;
+ if (symbol) {
+ RETURN(0) = NIL;
+ return (symbol);
+ }
+
+ i = STRHASH(ptr);
+ atom = pack->atoms[i];
+ while (atom) {
+ if (strcmp(atom->string, ptr) == 0) {
+ symbol = atom->object;
+ break;
+ }
+ atom = atom->next;
+ }
+
+ if (symbol == NULL || symbol->data.atom->package == NULL) {
+ RETURN(0) = NIL;
+ if (intern) {
+ /* symbol does not exist in the specified package, create a new
+ * internal symbol */
+
+ if (package == PACKAGE)
+ symbol = ATOM(ptr);
+ else {
+ LispPackage *savepack;
+ LispObj *savepackage;
+
+ /* Save package environment */
+ savepackage = PACKAGE;
+ savepack = lisp__data.pack;
+
+ /* Change package environment */
+ PACKAGE = package;
+ lisp__data.pack = package->data.package.package;
+
+ symbol = ATOM(ptr);
+
+ /* Restore package environment */
+ PACKAGE = savepackage;
+ lisp__data.pack = savepack;
+ }
+
+ symbol->data.atom->unreadable = !LispCheckAtomString(ptr);
+ /* If symbol being create in the keyword package, make it external */
+ if (package == lisp__data.keyword)
+ symbol->data.atom->ext = symbol->data.atom->constant = 1;
+ }
+ else
+ symbol = NIL;
+ }
+ else {
+ if (symbol->data.atom->package == package)
+ RETURN(0) = symbol->data.atom->ext ? Kexternal : Kinternal;
+ else
+ RETURN(0) = Kinherited;
+ }
+
+ return (symbol);
+}
+
+
+LispObj *
+Lisp_DoAllSymbols(LispBuiltin *builtin)
+/*
+ do-all-symbols init &rest body
+ */
+{
+ return (LispDoSymbols(builtin, 0, 1));
+}
+
+LispObj *
+Lisp_DoExternalSymbols(LispBuiltin *builtin)
+/*
+ do-external-symbols init &rest body
+ */
+{
+ return (LispDoSymbols(builtin, 1, 0));
+}
+
+LispObj *
+Lisp_DoSymbols(LispBuiltin *builtin)
+/*
+ do-symbols init &rest body
+ */
+{
+ return (LispDoSymbols(builtin, 0, 0));
+}
+
+LispObj *
+Lisp_FindAllSymbols(LispBuiltin *builtin)
+/*
+ find-all-symbols string-or-symbol
+ */
+{
+ GC_ENTER();
+ char *string = NULL;
+ LispAtom *atom;
+ LispPackage *pack;
+ LispObj *list, *package, *result;
+ int i;
+
+ LispObj *string_or_symbol;
+
+ string_or_symbol = ARGUMENT(0);
+
+ if (STRINGP(string_or_symbol))
+ string = THESTR(string_or_symbol);
+ else if (SYMBOLP(string_or_symbol))
+ string = ATOMID(string_or_symbol);
+ 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 */
+
+ 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();
+
+ return (result);
+}
+
+LispObj *
+Lisp_FindSymbol(LispBuiltin *builtin)
+/*
+ find-symbol string &optional package
+ */
+{
+ return (LispFindSymbol(builtin, 0));
+}
+
+LispObj *
+Lisp_FindPackage(LispBuiltin *builtin)
+/*
+ find-package name
+ */
+{
+ LispObj *name;
+
+ name = ARGUMENT(0);
+
+ return (LispFindPackage(name));
+}
+
+LispObj *
+Lisp_Export(LispBuiltin *builtin)
+/*
+ export symbols &optional package
+ */
+{
+ LispObj *list;
+
+ LispObj *symbols, *package;
+
+ package = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ /* If specified, make sure package is available */
+ if (package != UNSPEC)
+ package = LispFindPackageOrDie(builtin, package);
+ else
+ package = PACKAGE;
+
+ /* Export symbols */
+ if (CONSP(symbols)) {
+ for (list = symbols; CONSP(list); list = CDR(list))
+ LispDoExport(builtin, package, CAR(list), 1);
+ }
+ else
+ LispDoExport(builtin, package, symbols, 1);
+
+ return (T);
+}
+
+LispObj *
+Lisp_Import(LispBuiltin *builtin)
+/*
+ import symbols &optional package
+ */
+{
+ int restore_package;
+ LispPackage *savepack = NULL;
+ LispObj *list, *savepackage = NULL;
+
+ LispObj *symbols, *package;
+
+ package = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ /* If specified, make sure package is available */
+ if (package != UNSPEC)
+ package = LispFindPackageOrDie(builtin, package);
+ else
+ package = PACKAGE;
+
+ restore_package = package != PACKAGE;
+ if (restore_package) {
+ /* Save package environment */
+ savepackage = PACKAGE;
+ savepack = lisp__data.pack;
+
+ /* Change package environment */
+ PACKAGE = package;
+ lisp__data.pack = package->data.package.package;
+ }
+
+ /* Export symbols */
+ if (CONSP(symbols)) {
+ for (list = symbols; CONSP(list); list = CDR(list))
+ LispDoImport(builtin, CAR(list));
+ }
+ else
+ LispDoImport(builtin, symbols);
+
+ if (restore_package) {
+ /* Restore package environment */
+ PACKAGE = savepackage;
+ lisp__data.pack = savepack;
+ }
+
+ return (T);
+}
+
+LispObj *
+Lisp_InPackage(LispBuiltin *builtin)
+/*
+ in-package name
+ */
+{
+ LispObj *package;
+
+ LispObj *name;
+
+ name = ARGUMENT(0);
+
+ package = LispFindPackageOrDie(builtin, name);
+
+ /* Update pointer to package symbol table */
+ lisp__data.pack = package->data.package.package;
+ PACKAGE = package;
+
+ return (package);
+}
+
+LispObj *
+Lisp_Intern(LispBuiltin *builtin)
+/*
+ intern string &optional package
+ */
+{
+ return (LispFindSymbol(builtin, 1));
+}
+
+LispObj *
+Lisp_ListAllPackages(LispBuiltin *builtin)
+/*
+ list-all-packages
+ */
+{
+ /* Maybe this should be read-only or a copy of the package list.
+ * But, if properly implemented, it should be possible to (rplaca)
+ * this variable from lisp code with no problems. Don't do it at home. */
+
+ return (PACK);
+}
+
+LispObj *
+Lisp_MakePackage(LispBuiltin *builtin)
+/*
+ make-package package-name &key nicknames use
+ */
+{
+ GC_ENTER();
+ LispObj *list, *package, *nicks, *cons, *savepackage;
+
+ LispObj *package_name, *nicknames, *use;
+
+ use = ARGUMENT(2);
+ nicknames = ARGUMENT(1);
+ package_name = ARGUMENT(0);
+
+ /* Check if package already exists */
+ package = LispFindPackage(package_name);
+ if (package != NIL)
+ /* FIXME: this should be a correctable error */
+ LispDestroy("%s: package %s already defined",
+ STRFUN(builtin), STROBJ(package_name));
+
+ /* Error checks done, package_name is either a symbol or string */
+ if (!XSTRINGP(package_name))
+ package_name = STRING(ATOMID(package_name));
+
+ GC_PROTECT(package_name);
+
+ /* Check nicknames */
+ nicks = cons = NIL;
+ for (list = nicknames; CONSP(list); list = CDR(list)) {
+ package = LispFindPackage(CAR(list));
+ if (package != NIL)
+ /* FIXME: this should be a correctable error */
+ LispDestroy("%s: nickname %s matches package %s",
+ STRFUN(builtin), STROBJ(CAR(list)),
+ THESTR(package->data.package.name));
+ /* Store all nicknames as strings */
+ package = CAR(list);
+ if (!XSTRINGP(package))
+ package = STRING(ATOMID(package));
+ if (nicks == NIL) {
+ nicks = cons = CONS(package, NIL);
+ GC_PROTECT(nicks);
+ }
+ else {
+ RPLACD(cons, CONS(package, NIL));
+ cons = CDR(cons);
+ }
+ }
+
+ /* Check use list */
+ for (list = use; CONSP(list); list = CDR(list))
+ (void)LispFindPackageOrDie(builtin, CAR(list));
+
+ /* No errors, create new package */
+ package = LispNewPackage(package_name, nicks);
+
+ /* Update list of packages */
+ PACK = CONS(package, PACK);
+
+ /* No need for gc protection anymore */
+ GC_LEAVE();
+
+ /* Import symbols from use list */
+ savepackage = PACKAGE;
+
+ /* Update pointer to package symbol table */
+ lisp__data.pack = package->data.package.package;
+ PACKAGE = package;
+
+ if (use != UNSPEC) {
+ for (list = use; CONSP(list); list = CDR(list))
+ LispUsePackage(LispFindPackage(CAR(list)));
+ }
+ else
+ LispUsePackage(lisp__data.lisp);
+
+ /* Restore pointer to package symbol table */
+ lisp__data.pack = savepackage->data.package.package;
+ PACKAGE = savepackage;
+
+ return (package);
+}
+
+LispObj *
+Lisp_Packagep(LispBuiltin *builtin)
+/*
+ packagep object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (PACKAGEP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_PackageName(LispBuiltin *builtin)
+/*
+ package-name package
+ */
+{
+ LispObj *package;
+
+ package = ARGUMENT(0);
+
+ package = LispFindPackageOrDie(builtin, package);
+
+ return (package->data.package.name);
+}
+
+LispObj *
+Lisp_PackageNicknames(LispBuiltin *builtin)
+/*
+ package-nicknames package
+ */
+{
+ LispObj *package;
+
+ package = ARGUMENT(0);
+
+ package = LispFindPackageOrDie(builtin, package);
+
+ return (package->data.package.nicknames);
+}
+
+LispObj *
+Lisp_PackageUseList(LispBuiltin *builtin)
+/*
+ package-use-list package
+ */
+{
+ /* If the variable returned by this function is expected to be changeable,
+ * need to change the layout of the LispPackage structure. */
+
+ LispPackage *pack;
+ LispObj *package, *use, *cons;
+
+ package = ARGUMENT(0);
+
+ package = LispFindPackageOrDie(builtin, package);
+
+ use = cons = NIL;
+ pack = package->data.package.package;
+
+ if (pack->use.length) {
+ GC_ENTER();
+ int i = pack->use.length - 1;
+
+ use = cons = CONS(pack->use.pairs[i], NIL);
+ GC_PROTECT(use);
+ for (--i; i >= 0; i--) {
+ RPLACD(cons, CONS(pack->use.pairs[i], NIL));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+ }
+
+ return (use);
+}
+
+LispObj *
+Lisp_PackageUsedByList(LispBuiltin *builtin)
+/*
+ package-used-by-list package
+ */
+{
+ GC_ENTER();
+ int i;
+ LispPackage *pack;
+ LispObj *package, *other, *used, *cons, *list;
+
+ package = ARGUMENT(0);
+
+ package = LispFindPackageOrDie(builtin, package);
+
+ used = cons = NIL;
+
+ for (list = PACK; CONSP(list); list = CDR(list)) {
+ other = CAR(list);
+ if (package == other)
+ /* Surely package uses itself */
+ continue;
+
+ pack = other->data.package.package;
+
+ for (i = 0; i < pack->use.length; i++) {
+ if (pack->use.pairs[i] == package) {
+ if (used == NIL) {
+ used = cons = CONS(other, NIL);
+ GC_PROTECT(used);
+ }
+ else {
+ RPLACD(cons, CONS(other, NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+ }
+
+ GC_LEAVE();
+
+ return (used);
+}
+
+LispObj *
+Lisp_Unexport(LispBuiltin *builtin)
+/*
+ unexport symbols &optional package
+ */
+{
+ LispObj *list;
+
+ LispObj *symbols, *package;
+
+ package = ARGUMENT(1);
+ symbols = ARGUMENT(0);
+
+ /* If specified, make sure package is available */
+ if (package != UNSPEC)
+ package = LispFindPackageOrDie(builtin, package);
+ else
+ package = PACKAGE;
+
+ /* Export symbols */
+ if (CONSP(symbols)) {
+ for (list = symbols; CONSP(list); list = CDR(list))
+ LispDoExport(builtin, package, CAR(list), 0);
+ }
+ else
+ LispDoExport(builtin, package, symbols, 0);
+
+ return (T);
+}
diff --git a/lisp/package.h b/lisp/package.h
new file mode 100644
index 0000000..23ad822
--- /dev/null
+++ b/lisp/package.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/package.h,v 1.7 2002/11/26 04:06:28 paulo Exp $ */
+
+#ifndef Lisp_package_h
+#define Lisp_package_h
+
+#include "internal.h"
+
+void LispPackageInit(void);
+LispObj *LispFindPackage(LispObj*);
+LispObj *LispFindPackageFromString(char*);
+/* returns 1 if string can safely be read back */
+int LispCheckAtomString(char*);
+
+LispObj *Lisp_DoAllSymbols(LispBuiltin*);
+LispObj *Lisp_DoExternalSymbols(LispBuiltin*);
+LispObj *Lisp_DoSymbols(LispBuiltin*);
+LispObj *Lisp_FindAllSymbols(LispBuiltin*);
+LispObj *Lisp_FindPackage(LispBuiltin*);
+LispObj *Lisp_FindSymbol(LispBuiltin*);
+LispObj *Lisp_Export(LispBuiltin*);
+LispObj *Lisp_Import(LispBuiltin*);
+LispObj *Lisp_InPackage(LispBuiltin*);
+LispObj *Lisp_Intern(LispBuiltin*);
+LispObj *Lisp_ListAllPackages(LispBuiltin*);
+LispObj *Lisp_MakePackage(LispBuiltin*);
+LispObj *Lisp_Packagep(LispBuiltin*);
+LispObj *Lisp_PackageName(LispBuiltin*);
+LispObj *Lisp_PackageNicknames(LispBuiltin*);
+LispObj *Lisp_PackageUseList(LispBuiltin*);
+LispObj *Lisp_PackageUsedByList(LispBuiltin*);
+LispObj *Lisp_Unexport(LispBuiltin*);
+
+#endif /* Lisp_package_h */
diff --git a/lisp/pathname.c b/lisp/pathname.c
new file mode 100644
index 0000000..6af8cd1
--- /dev/null
+++ b/lisp/pathname.c
@@ -0,0 +1,1096 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/pathname.c,v 1.17 2002/12/24 00:25:39 dawes Exp $ */
+
+#include <stdio.h> /* including dirent.h first may cause problems */
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include "pathname.h"
+#include "private.h"
+
+#define NOREAD_SKIP 0
+#define NOREAD_ERROR 1
+
+/*
+ * Initialization
+ */
+LispObj *Oparse_namestring, *Kerror, *Kabsolute, *Krelative, *Kskip;
+
+/*
+ * Implementation
+ */
+void
+LispPathnameInit(void)
+{
+ Kerror = KEYWORD("ERROR");
+ Oparse_namestring = STATIC_ATOM("PARSE-NAMESTRING");
+ Kabsolute = KEYWORD("ABSOLUTE");
+ Krelative = KEYWORD("RELATIVE");
+}
+
+static int
+glob_match(char *cmp1, char *cmp2)
+/*
+ * Note: this code was written from scratch, and may generate incorrect
+ * results for very complex glob masks.
+ */
+{
+ for (;;) {
+ while (*cmp1 && *cmp1 == *cmp2) {
+ ++cmp1;
+ ++cmp2;
+ }
+ if (*cmp2) {
+ if (*cmp1 == '*') {
+ while (*cmp1 == '*')
+ ++cmp1;
+ if (*cmp1) {
+ int count = 0, settmp = 1;
+ char *tmp = cmp2, *sav2;
+
+ while (*cmp1 && *cmp1 == '?') {
+ ++cmp1;
+ ++count;
+ }
+
+ /* need to recurse here to make sure
+ * all cases are tested.
+ */
+ while (*cmp2 && *cmp2 != *cmp1)
+ ++cmp2;
+ if (!*cmp1 && cmp2 - tmp < count)
+ return (0);
+ sav2 = cmp2;
+
+ /* if recursive calls fails, make sure all '?'
+ * following '*' are processed */
+ while (*sav2 && sav2 - tmp < count)
+ ++sav2;
+
+ for (; *cmp2;) {
+ if (settmp) /* repeated letters: *?o? => boot, root */
+ tmp = cmp2;
+ else
+ settmp = 1;
+ while (*cmp2 && *cmp2 != *cmp1)
+ ++cmp2;
+ if (cmp2 - tmp < count) {
+ if (*cmp2)
+ ++cmp2;
+ settmp = 0;
+ continue;
+ }
+ if (*cmp2) {
+ if (glob_match(cmp1, cmp2))
+ return (1);
+ ++cmp2;
+ }
+ }
+ cmp2 = sav2;
+ }
+ else {
+ while (*cmp2)
+ ++cmp2;
+ break;
+ }
+ }
+ else if (*cmp1 == '?') {
+ while (*cmp1 == '?' && *cmp2) {
+ ++cmp1;
+ ++cmp2;
+ }
+ continue;
+ }
+ else
+ break;
+ }
+ else {
+ while (*cmp1 == '*')
+ ++cmp1;
+ break;
+ }
+ }
+
+ return (*cmp1 == '\0' && *cmp2 == '\0');
+}
+
+/*
+ * Since directory is a function to be extended by the implementation,
+ * current extensions are:
+ * all => list files and directories
+ * it is an error to call
+ * (directory "<pathname-spec>/" :all t)
+ * if non nil, it is like the shell command
+ * echo <pathname-spec>, but normally, not in the
+ * same order, as the code does not sort the result.
+ * !=nil => list files and directories
+ * (default) nil => list only files, or only directories if
+ * <pathname-spec> ends with PATH_SEP char.
+ * if-cannot-read => if opendir fails on a directory
+ * :error => generate an error
+ * (default) :skip => skip search in this directory
+ */
+LispObj *
+Lisp_Directory(LispBuiltin *builtin)
+/*
+ directory pathname &key all if-cannot-read
+ */
+{
+ GC_ENTER();
+ DIR *dir;
+ struct stat st;
+ struct dirent *ent;
+ int length, listdirs, i, ndirs, nmatches;
+ char name[PATH_MAX + 1], path[PATH_MAX + 2], directory[PATH_MAX + 2];
+ char *sep, *base, *ptr, **dirs, **matches,
+ dot[] = {'.', PATH_SEP, '\0'},
+ dotdot[] = {'.', '.', PATH_SEP, '\0'};
+ int cannot_read;
+
+ LispObj *pathname, *all, *if_cannot_read, *result, *cons, *object;
+
+ if_cannot_read = ARGUMENT(2);
+ all = ARGUMENT(1);
+ pathname = ARGUMENT(0);
+ result = NIL;
+
+ cons = NIL;
+
+ if (if_cannot_read != UNSPEC) {
+ if (!KEYWORDP(if_cannot_read) ||
+ (if_cannot_read != Kskip &&
+ if_cannot_read != Kerror))
+ LispDestroy("%s: bad :IF-CANNOT-READ %s",
+ STRFUN(builtin), STROBJ(if_cannot_read));
+ if (if_cannot_read != Kskip)
+ cannot_read = NOREAD_SKIP;
+ else
+ cannot_read = NOREAD_ERROR;
+ }
+ else
+ cannot_read = NOREAD_SKIP;
+
+ if (PATHNAMEP(pathname))
+ pathname = CAR(pathname->data.pathname);
+ else if (STREAMP(pathname) && pathname->data.stream.type == LispStreamFile)
+ pathname = CAR(pathname->data.stream.pathname->data.pathname);
+ else if (!STRINGP(pathname))
+ LispDestroy("%s: %s is not a pathname",
+ STRFUN(builtin), STROBJ(pathname));
+
+ strncpy(name, THESTR(pathname), sizeof(name) - 1);
+ name[sizeof(name) - 1] = '\0';
+ length = strlen(name);
+ if (length < STRLEN(pathname))
+ LispDestroy("%s: pathname too long %s",
+ STRFUN(builtin), name);
+
+ if (length == 0) {
+ if (getcwd(path, sizeof(path) - 2) == NULL)
+ LispDestroy("%s: getcwd(): %s", STRFUN(builtin), strerror(errno));
+ length = strlen(path);
+ if (!length || path[length - 1] != PATH_SEP) {
+ path[length++] = PATH_SEP;
+ path[length] = '\0';
+ }
+ result = APPLY1(Oparse_namestring, LSTRING(path, length));
+ GC_LEAVE();
+
+ return (result);
+ }
+
+ if (name[length - 1] == PATH_SEP) {
+ listdirs = 1;
+ if (length > 1) {
+ --length;
+ name[length] = '\0';
+ }
+ }
+ else
+ listdirs = 0;
+
+ if (name[0] != PATH_SEP) {
+ if (getcwd(path, sizeof(path) - 2) == NULL)
+ LispDestroy("%s: getcwd(): %s", STRFUN(builtin), strerror(errno));
+ length = strlen(path);
+ if (!length || path[length - 1] != PATH_SEP) {
+ path[length++] = PATH_SEP;
+ path[length] = '\0';
+ }
+ }
+ else
+ path[0] = '\0';
+
+ result = NIL;
+
+ /* list intermediate directories */
+ matches = NULL;
+ nmatches = 0;
+ dirs = LispMalloc(sizeof(char*));
+ ndirs = 1;
+ if (snprintf(directory, sizeof(directory), "%s%s%c",
+ path, name, PATH_SEP) > PATH_MAX)
+ LispDestroy("%s: pathname too long %s", STRFUN(builtin), directory);
+
+ /* Remove ../ */
+ sep = directory;
+ for (sep = strstr(sep, dotdot); sep; sep = strstr(sep, dotdot)) {
+ if (sep <= directory + 1)
+ strcpy(directory, sep + 2);
+ else if (sep[-1] == PATH_SEP) {
+ for (base = sep - 2; base > directory; base--)
+ if (*base == PATH_SEP)
+ break;
+ strcpy(base, sep + 2);
+ sep = base;
+ }
+ else
+ ++sep;
+ }
+
+ /* Remove "./" */
+ sep = directory;
+ for (sep = strstr(sep, dot); sep; sep = strstr(sep, dot)) {
+ if (sep == directory || sep[-1] == PATH_SEP)
+ strcpy(sep, sep + 2);
+ else
+ ++sep;
+ }
+
+ /* This will happen when there are too many '../' in the path */
+ if (directory[1] == '\0') {
+ directory[1] = PATH_SEP;
+ directory[2] = '\0';
+ }
+
+ base = directory;
+ sep = strchr(base + 1, PATH_SEP);
+ dirs[0] = LispMalloc(2);
+ dirs[0][0] = PATH_SEP;
+ dirs[0][1] = '\0';
+
+ for (base = directory + 1, sep = strchr(base, PATH_SEP); ;
+ base = sep + 1, sep = strchr(base, PATH_SEP)) {
+ *sep = '\0';
+ if (sep[1] == '\0')
+ sep = NULL;
+ length = strlen(base);
+ if (length == 0) {
+ if (sep)
+ *sep = PATH_SEP;
+ else
+ break;
+ continue;
+ }
+
+ for (i = 0; i < ndirs; i++) {
+ length = strlen(dirs[i]);
+ if (length > 1)
+ dirs[i][length - 1] = '\0'; /* remove trailing / */
+ if ((dir = opendir(dirs[i])) != NULL) {
+ (void)readdir(dir); /* "." */
+ (void)readdir(dir); /* ".." */
+ if (length > 1)
+ dirs[i][length - 1] = PATH_SEP; /* add trailing / again */
+
+ snprintf(path, sizeof(path), "%s", dirs[i]);
+ length = strlen(path);
+ ptr = path + length;
+
+ while ((ent = readdir(dir)) != NULL) {
+ int isdir;
+ unsigned d_namlen = strlen(ent->d_name);
+
+ if (length + d_namlen + 2 < sizeof(path))
+ strcpy(ptr, ent->d_name);
+ else {
+ closedir(dir);
+ LispDestroy("%s: pathname too long %s",
+ STRFUN(builtin), dirs[i]);
+ }
+
+ if (stat(path, &st) != 0)
+ isdir = 0;
+ else
+ isdir = S_ISDIR(st.st_mode);
+
+ if (all != UNSPEC || ((isdir && (listdirs || sep)) ||
+ (!listdirs && !sep && !isdir))) {
+ if (glob_match(base, ent->d_name)) {
+ if (isdir) {
+ length = strlen(ptr);
+ ptr[length++] = PATH_SEP;
+ ptr[length] = '\0';
+ }
+ /* XXX won't closedir on memory allocation failure! */
+ matches = LispRealloc(matches, sizeof(char*) *
+ nmatches + 1);
+ matches[nmatches++] = LispStrdup(ptr);
+ }
+ }
+ }
+ closedir(dir);
+
+ if (nmatches == 0) {
+ if (sep || !listdirs || *base) {
+ LispFree(dirs[i]);
+ if (i + 1 < ndirs)
+ memmove(dirs + i, dirs + i + 1,
+ sizeof(char*) * (ndirs - (i + 1)));
+ --ndirs;
+ --i; /* XXX playing with for loop */
+ }
+ }
+ else {
+ int j;
+
+ length = strlen(dirs[i]);
+ if (nmatches > 1) {
+ dirs = LispRealloc(dirs, sizeof(char*) *
+ (ndirs + nmatches));
+ if (i + 1 < ndirs)
+ memmove(dirs + i + nmatches, dirs + i + 1,
+ sizeof(char*) * (ndirs - (i + 1)));
+ }
+ for (j = 1; j < nmatches; j++) {
+ dirs[i + j] = LispMalloc(length +
+ strlen(matches[j]) + 1);
+ sprintf(dirs[i + j], "%s%s", dirs[i], matches[j]);
+ }
+ dirs[i] = LispRealloc(dirs[i],
+ length + strlen(matches[0]) + 1);
+ strcpy(dirs[i] + length, matches[0]);
+ i += nmatches - 1; /* XXX playing with for loop */
+ ndirs += nmatches - 1;
+
+ for (j = 0; j < nmatches; j++)
+ LispFree(matches[j]);
+ LispFree(matches);
+ matches = NULL;
+ nmatches = 0;
+ }
+ }
+ else {
+ if (cannot_read == NOREAD_ERROR)
+ LispDestroy("%s: opendir(%s): %s",
+ STRFUN(builtin), dirs[i], strerror(errno));
+ else {
+ LispFree(dirs[i]);
+ if (i + 1 < ndirs)
+ memmove(dirs + i, dirs + i + 1,
+ sizeof(char*) * (ndirs - (i + 1)));
+ --ndirs;
+ --i; /* XXX playing with for loop */
+ }
+ }
+ }
+ if (sep)
+ *sep = PATH_SEP;
+ else
+ break;
+ }
+
+ for (i = 0; i < ndirs; i++) {
+ object = APPLY1(Oparse_namestring, STRING2(dirs[i]));
+ if (result == NIL) {
+ result = cons = CONS(object, NIL);
+ GC_PROTECT(result);
+ }
+ else {
+ RPLACD(cons, CONS(object, NIL));
+ cons = CDR(cons);
+ }
+ }
+ LispFree(dirs);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_ParseNamestring(LispBuiltin *builtin)
+/*
+ parse-namestring object &optional host defaults &key start end junk-allowed
+ */
+{
+ GC_ENTER();
+ LispObj *result;
+
+ LispObj *object, *host, *defaults, *ostart, *oend, *junk_allowed;
+
+ junk_allowed = ARGUMENT(5);
+ oend = ARGUMENT(4);
+ ostart = ARGUMENT(3);
+ defaults = ARGUMENT(2);
+ host = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (host == UNSPEC)
+ host = NIL;
+ if (defaults == UNSPEC)
+ defaults = NIL;
+
+ RETURN_COUNT = 1;
+ if (STREAMP(object)) {
+ if (object->data.stream.type == LispStreamFile)
+ object = object->data.stream.pathname;
+ /* else just check for JUNK-ALLOWED... */
+ }
+ if (PATHNAMEP(object)) {
+ RETURN(0) = FIXNUM(0);
+ return (object);
+ }
+
+ if (host != NIL) {
+ CHECK_STRING(host);
+ }
+ if (defaults != NIL) {
+ if (!PATHNAMEP(defaults)) {
+ defaults = APPLY1(Oparse_namestring, defaults);
+ GC_PROTECT(defaults);
+ }
+ }
+
+ result = NIL;
+ if (STRINGP(object)) {
+ LispObj *cons, *cdr;
+ char *name = THESTR(object), *ptr, *str, data[PATH_MAX + 1],
+ string[PATH_MAX + 1], *namestr, *typestr, *send;
+ long start, end, length, alength, namelen, typelen;
+
+ LispCheckSequenceStartEnd(builtin, object, ostart, oend,
+ &start, &end, &length);
+ alength = end - start;
+
+ if (alength > sizeof(data) - 1)
+ LispDestroy("%s: string %s too large",
+ STRFUN(builtin), STROBJ(object));
+ memcpy(data, name + start, alength);
+#ifndef KEEP_EXTRA_PATH_SEP
+ ptr = data;
+ send = ptr + alength;
+ while (ptr < send) {
+ if (*ptr++ == PATH_SEP) {
+ for (str = ptr; str < send && *str == PATH_SEP; str++)
+ ;
+ if (str - ptr) {
+ memmove(ptr, str, alength - (str - data));
+ alength -= str - ptr;
+ send -= str - ptr;
+ }
+ }
+ }
+#endif
+ data[alength] = '\0';
+ memcpy(string, data, alength + 1);
+
+ if (PATHNAMEP(defaults))
+ defaults = defaults->data.pathname;
+
+ /* string name */
+ result = cons = CONS(NIL, NIL);
+ GC_PROTECT(result);
+
+ /* host */
+ if (defaults != NIL)
+ defaults = CDR(defaults);
+ cdr = defaults == NIL ? NIL : CAR(defaults);
+ RPLACD(cons, CONS(cdr, NIL));
+ cons = CDR(cons);
+
+ /* device */
+ if (defaults != NIL)
+ defaults = CDR(defaults);
+ cdr = defaults == NIL ? NIL : CAR(defaults);
+ RPLACD(cons, CONS(cdr, NIL));
+ cons = CDR(cons);
+
+ /* directory */
+ if (defaults != NIL)
+ defaults = CDR(defaults);
+ if (*data == PATH_SEP)
+ cdr = CONS(Kabsolute, NIL);
+ else
+ cdr = CONS(Krelative, NIL);
+ RPLACD(cons, CONS(cdr, NIL));
+ cons = CDR(cons);
+ /* directory components */
+ ptr = data;
+ send = data + alength;
+ if (*ptr == PATH_SEP)
+ ++ptr;
+ for (str = ptr; str < send; str++) {
+ if (*str == PATH_SEP)
+ break;
+ }
+ while (str < send) {
+ *str++ = '\0';
+ if (str - ptr > NAME_MAX)
+ LispDestroy("%s: directory name too long %s",
+ STRFUN(builtin), ptr);
+ RPLACD(cdr, CONS(LSTRING(ptr, str - ptr - 1), NIL));
+ cdr = CDR(cdr);
+ for (ptr = str; str < send; str++) {
+ if (*str == PATH_SEP)
+ break;
+ }
+ }
+ if (str - ptr > NAME_MAX)
+ LispDestroy("%s: file name too long %s", STRFUN(builtin), ptr);
+ if (CAAR(cons) == Krelative &&
+ defaults != NIL && CAAR(defaults) == Kabsolute) {
+ /* defaults specify directory and pathname doesn't */
+ char *tstring;
+ long dlength, tlength;
+ LispObj *dir = CDAR(defaults);
+
+ for (dlength = 1; CONSP(dir); dir = CDR(dir))
+ dlength += STRLEN(CAR(dir)) + 1;
+ if (alength + dlength < PATH_MAX) {
+ memmove(data + dlength, data, alength + 1);
+ memmove(string + dlength, string, alength + 1);
+ alength += dlength;
+ ptr += dlength;
+ send += dlength;
+ CAAR(cons) = Kabsolute;
+ for (dir = CDAR(defaults), cdr = CAR(cons);
+ CONSP(dir);
+ dir = CDR(dir)) {
+ RPLACD(cdr, CONS(CAR(dir), CDR(cdr)));
+ cdr = CDR(cdr);
+ }
+ dir = CDAR(defaults);
+ data[0] = string[0] = PATH_SEP;
+ for (dlength = 1; CONSP(dir); dir = CDR(dir)) {
+ tstring = THESTR(CAR(dir));
+ tlength = STRLEN(CAR(dir));
+ memcpy(data + dlength, tstring, tlength);
+ memcpy(string + dlength, tstring, tlength);
+ dlength += tlength;
+ data[dlength] = string[dlength] = PATH_SEP;
+ ++dlength;
+ }
+ }
+ }
+
+ /* name */
+ if (defaults != NIL)
+ defaults = CDR(defaults);
+ cdr = defaults == NIL ? NIL : CAR(defaults);
+ for (typelen = 0, str = ptr; str < send; str++) {
+ if (*str == PATH_TYPESEP) {
+ typelen = 1;
+ break;
+ }
+ }
+ if (*ptr)
+ cdr = LSTRING(ptr, str - ptr);
+ if (STRINGP(cdr)) {
+ namestr = THESTR(cdr);
+ namelen = STRLEN(cdr);
+ }
+ else {
+ namestr = "";
+ namelen = 0;
+ }
+ RPLACD(cons, CONS(cdr, NIL));
+ cons = CDR(cons);
+
+ /* type */
+ if (defaults != NIL)
+ defaults = CDR(defaults);
+ cdr = defaults == NIL ? NIL : CAR(defaults);
+ ptr = str + typelen;
+ if (*ptr)
+ cdr = LSTRING(ptr, send - ptr);
+ if (STRINGP(cdr)) {
+ typestr = THESTR(cdr);
+ typelen = STRLEN(cdr);
+ }
+ else {
+ typestr = "";
+ typelen = 0;
+ }
+ RPLACD(cons, CONS(cdr, NIL));
+ cons = CDR(cons);
+
+ /* version */
+ if (defaults != NIL)
+ defaults = CDR(defaults);
+ cdr = defaults == NIL ? NIL : CAR(defaults);
+ RPLACD(cons, CONS(cdr, NIL));
+
+ /* string representation, must be done here to use defaults */
+ for (ptr = string + alength; ptr >= string; ptr--) {
+ if (*ptr == PATH_SEP)
+ break;
+ }
+ if (ptr >= string)
+ ++ptr;
+ else
+ ptr = string;
+ *ptr = '\0';
+
+ length = ptr - string;
+
+ alength = namelen;
+ if (alength) {
+ if (length + alength + 2 > sizeof(string))
+ alength = sizeof(string) - length - 2;
+ memcpy(string + length, namestr, alength);
+ length += alength;
+ }
+
+ alength = typelen;
+ if (alength) {
+ if (length + 2 < sizeof(string))
+ string[length++] = PATH_TYPESEP;
+ if (length + alength + 2 > sizeof(string))
+ alength = sizeof(string) - length - 2;
+ memcpy(string + length, typestr, alength);
+ length += alength;
+ }
+ string[length] = '\0';
+
+ RPLACA(result, LSTRING(string, length));
+ RETURN(0) = FIXNUM(end);
+
+ result = PATHNAME(result);
+ }
+ else if (junk_allowed == UNSPEC || junk_allowed == NIL)
+ LispDestroy("%s: bad argument %s", STRFUN(builtin), STROBJ(object));
+ else
+ RETURN(0) = NIL;
+
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_MakePathname(LispBuiltin *builtin)
+/*
+ make-pathname &key host device directory name type version defaults
+ */
+{
+ GC_ENTER();
+ int length, alength;
+ char *string, pathname[PATH_MAX + 1];
+ LispObj *result, *cdr, *cons;
+
+ LispObj *host, *device, *directory, *name, *type, *version, *defaults;
+
+ defaults = ARGUMENT(6);
+ version = ARGUMENT(5);
+ type = ARGUMENT(4);
+ name = ARGUMENT(3);
+ directory = ARGUMENT(2);
+ device = ARGUMENT(1);
+ host = ARGUMENT(0);
+
+ if (host != UNSPEC) {
+ CHECK_STRING(host);
+ }
+ if (device != UNSPEC) {
+ CHECK_STRING(device);
+ }
+
+ if (directory != UNSPEC) {
+ LispObj *dir;
+
+ CHECK_CONS(directory);
+ dir = CAR(directory);
+ CHECK_KEYWORD(dir);
+ if (dir != Kabsolute && dir != Krelative)
+ LispDestroy("%s: directory type %s unknown",
+ STRFUN(builtin), STROBJ(dir));
+ }
+
+ if (name != UNSPEC) {
+ CHECK_STRING(name);
+ }
+ if (type != UNSPEC) {
+ CHECK_STRING(type);
+ }
+
+ if (version != UNSPEC && version != NIL) {
+ switch (OBJECT_TYPE(version)) {
+ case LispFixnum_t:
+ if (FIXNUM_VALUE(version) >= 0)
+ goto version_ok;
+ case LispInteger_t:
+ if (INT_VALUE(version) >= 0)
+ goto version_ok;
+ break;
+ case LispDFloat_t:
+ if (DFLOAT_VALUE(version) >= 0.0)
+ goto version_ok;
+ break;
+ default:
+ break;
+ }
+ LispDestroy("%s: %s is not a positive real number",
+ STRFUN(builtin), STROBJ(version));
+ }
+version_ok:
+
+ if (defaults != UNSPEC && !PATHNAMEP(defaults) &&
+ (host == UNSPEC || device == UNSPEC || directory == UNSPEC ||
+ name == UNSPEC || type == UNSPEC || version == UNSPEC)) {
+ defaults = APPLY1(Oparse_namestring, defaults);
+ GC_PROTECT(defaults);
+ }
+
+ if (defaults != UNSPEC) {
+ defaults = defaults->data.pathname;
+ defaults = CDR(defaults); /* host */
+ if (host == UNSPEC)
+ host = CAR(defaults);
+ defaults = CDR(defaults); /* device */
+ if (device == UNSPEC)
+ device = CAR(defaults);
+ defaults = CDR(defaults); /* directory */
+ if (directory == UNSPEC)
+ directory = CAR(defaults);
+ defaults = CDR(defaults); /* name */
+ if (name == UNSPEC)
+ name = CAR(defaults);
+ defaults = CDR(defaults); /* type */
+ if (type == UNSPEC)
+ type = CAR(defaults);
+ defaults = CDR(defaults); /* version */
+ if (version == UNSPEC)
+ version = CAR(defaults);
+ }
+
+ /* string representation */
+ length = 0;
+ if (CONSP(directory)) {
+ if (CAR(directory) == Kabsolute)
+ pathname[length++] = PATH_SEP;
+
+ for (cdr = CDR(directory); CONSP(cdr); cdr = CDR(cdr)) {
+ CHECK_STRING(CAR(cdr));
+ string = THESTR(CAR(cdr));
+ alength = STRLEN(CAR(cdr));
+ if (alength > NAME_MAX)
+ LispDestroy("%s: directory name too long %s",
+ STRFUN(builtin), string);
+ if (length + alength + 2 > sizeof(pathname))
+ alength = sizeof(pathname) - length - 2;
+ memcpy(pathname + length, string, alength);
+ length += alength;
+ pathname[length++] = PATH_SEP;
+ }
+ }
+ if (STRINGP(name)) {
+ int xlength = 0;
+
+ if (STRINGP(type))
+ xlength = STRLEN(type) + 1;
+
+ string = THESTR(name);
+ alength = STRLEN(name);
+ if (alength + xlength > NAME_MAX)
+ LispDestroy("%s: file name too long %s",
+ STRFUN(builtin), string);
+ if (length + alength + 2 > sizeof(pathname))
+ alength = sizeof(pathname) - length - 2;
+ memcpy(pathname + length, string, alength);
+ length += alength;
+ }
+ if (STRINGP(type)) {
+ if (length + 2 < sizeof(pathname))
+ pathname[length++] = PATH_TYPESEP;
+ string = THESTR(type);
+ alength = STRLEN(type);
+ if (length + alength + 2 > sizeof(pathname))
+ alength = sizeof(pathname) - length - 2;
+ memcpy(pathname + length, string, alength);
+ length += alength;
+ }
+ pathname[length] = '\0';
+ result = cons = CONS(LSTRING(pathname, length), NIL);
+ GC_PROTECT(result);
+
+ /* host */
+ RPLACD(cons, CONS(host == UNSPEC ? NIL : host, NIL));
+ cons = CDR(cons);
+
+ /* device */
+ RPLACD(cons, CONS(device == UNSPEC ? NIL : device, NIL));
+ cons = CDR(cons);
+
+ /* directory */
+ if (directory == UNSPEC)
+ cdr = CONS(Krelative, NIL);
+ else
+ cdr = directory;
+ RPLACD(cons, CONS(cdr, NIL));
+ cons = CDR(cons);
+
+ /* name */
+ RPLACD(cons, CONS(name == UNSPEC ? NIL : name, NIL));
+ cons = CDR(cons);
+
+ /* type */
+ RPLACD(cons, CONS(type == UNSPEC ? NIL : type, NIL));
+ cons = CDR(cons);
+
+ /* version */
+ RPLACD(cons, CONS(version == UNSPEC ? NIL : version, NIL));
+
+ GC_LEAVE();
+
+ return (PATHNAME(result));
+}
+
+LispObj *
+Lisp_PathnameHost(LispBuiltin *builtin)
+/*
+ pathname-host pathname
+ */
+{
+ return (LispPathnameField(PATH_HOST, 0));
+}
+
+LispObj *
+Lisp_PathnameDevice(LispBuiltin *builtin)
+/*
+ pathname-device pathname
+ */
+{
+ return (LispPathnameField(PATH_DEVICE, 0));
+}
+
+LispObj *
+Lisp_PathnameDirectory(LispBuiltin *builtin)
+/*
+ pathname-device pathname
+ */
+{
+ return (LispPathnameField(PATH_DIRECTORY, 0));
+}
+
+LispObj *
+Lisp_PathnameName(LispBuiltin *builtin)
+/*
+ pathname-name pathname
+ */
+{
+ return (LispPathnameField(PATH_NAME, 0));
+}
+
+LispObj *
+Lisp_PathnameType(LispBuiltin *builtin)
+/*
+ pathname-type pathname
+ */
+{
+ return (LispPathnameField(PATH_TYPE, 0));
+}
+
+LispObj *
+Lisp_PathnameVersion(LispBuiltin *builtin)
+/*
+ pathname-version pathname
+ */
+{
+ return (LispPathnameField(PATH_VERSION, 0));
+}
+
+LispObj *
+Lisp_FileNamestring(LispBuiltin *builtin)
+/*
+ file-namestring pathname
+ */
+{
+ return (LispPathnameField(PATH_NAME, 1));
+}
+
+LispObj *
+Lisp_DirectoryNamestring(LispBuiltin *builtin)
+/*
+ directory-namestring pathname
+ */
+{
+ return (LispPathnameField(PATH_DIRECTORY, 1));
+}
+
+LispObj *
+Lisp_EnoughNamestring(LispBuiltin *builtin)
+/*
+ enough-pathname pathname &optional defaults
+ */
+{
+ LispObj *pathname, *defaults;
+
+ defaults = ARGUMENT(1);
+ pathname = ARGUMENT(0);
+
+ if (defaults != UNSPEC && defaults != NIL) {
+ char *ppathname, *pdefaults, *pp, *pd;
+
+ if (!STRINGP(pathname)) {
+ if (PATHNAMEP(pathname))
+ pathname = CAR(pathname->data.pathname);
+ else if (STREAMP(pathname) &&
+ pathname->data.stream.type == LispStreamFile)
+ pathname = CAR(pathname->data.stream.pathname->data.pathname);
+ else
+ LispDestroy("%s: bad PATHNAME %s",
+ STRFUN(builtin), STROBJ(pathname));
+ }
+
+ if (!STRINGP(defaults)) {
+ if (PATHNAMEP(defaults))
+ defaults = CAR(defaults->data.pathname);
+ else if (STREAMP(defaults) &&
+ defaults->data.stream.type == LispStreamFile)
+ defaults = CAR(defaults->data.stream.pathname->data.pathname);
+ else
+ LispDestroy("%s: bad DEFAULTS %s",
+ STRFUN(builtin), STROBJ(defaults));
+ }
+
+ ppathname = pp = THESTR(pathname);
+ pdefaults = pd = THESTR(defaults);
+ while (*ppathname && *pdefaults && *ppathname == *pdefaults) {
+ ppathname++;
+ pdefaults++;
+ }
+ if (*pdefaults == '\0' && pdefaults > pd)
+ --pdefaults;
+ if (*ppathname && *pdefaults && *pdefaults != PATH_SEP) {
+ --ppathname;
+ while (*ppathname != PATH_SEP && ppathname > pp)
+ --ppathname;
+ if (*ppathname == PATH_SEP)
+ ++ppathname;
+ }
+
+ return (STRING(ppathname));
+ }
+ else {
+ if (STRINGP(pathname))
+ return (pathname);
+ else if (PATHNAMEP(pathname))
+ return (CAR(pathname->data.pathname));
+ else if (STREAMP(pathname)) {
+ if (pathname->data.stream.type == LispStreamFile)
+ return (CAR(pathname->data.stream.pathname->data.pathname));
+ }
+ }
+ LispDestroy("%s: bad PATHNAME %s", STRFUN(builtin), STROBJ(pathname));
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Namestring(LispBuiltin *builtin)
+/*
+ namestring pathname
+ */
+{
+ return (LispPathnameField(PATH_STRING, 1));
+}
+
+LispObj *
+Lisp_HostNamestring(LispBuiltin *builtin)
+/*
+ host-namestring pathname
+ */
+{
+ return (LispPathnameField(PATH_HOST, 1));
+}
+
+LispObj *
+Lisp_Pathnamep(LispBuiltin *builtin)
+/*
+ pathnamep object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (PATHNAMEP(object) ? T : NIL);
+}
+
+/* XXX only checks if host is a string and only checks the HOME enviroment
+ * variable */
+LispObj *
+Lisp_UserHomedirPathname(LispBuiltin *builtin)
+/*
+ user-homedir-pathname &optional host
+ */
+{
+ GC_ENTER();
+ int length;
+ char *home = getenv("HOME"), data[PATH_MAX + 1];
+ LispObj *result;
+
+ LispObj *host;
+
+ host = ARGUMENT(0);
+
+ if (host != UNSPEC && !STRINGP(host))
+ LispDestroy("%s: bad hostname %s", STRFUN(builtin), STROBJ(host));
+
+ length = 0;
+ if (home) {
+ length = strlen(home);
+ strncpy(data, home, length);
+ if (length && home[length - 1] != PATH_SEP)
+ data[length++] = PATH_SEP;
+ }
+ data[length] = '\0';
+
+ result = LSTRING(data, length);
+ GC_PROTECT(result);
+ result = APPLY1(Oparse_namestring, result);
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_Truename(LispBuiltin *builtin)
+{
+ return (LispProbeFile(builtin, 0));
+}
+
+LispObj *
+Lisp_ProbeFile(LispBuiltin *builtin)
+{
+ return (LispProbeFile(builtin, 1));
+}
diff --git a/lisp/pathname.h b/lisp/pathname.h
new file mode 100644
index 0000000..f99917e
--- /dev/null
+++ b/lisp/pathname.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/pathname.h,v 1.4 2002/11/08 08:00:57 paulo Exp $ */
+
+#ifndef Lisp_pathname_h
+#define Lisp_pathname_h
+
+#include "internal.h"
+
+#define PATH_SEP '/'
+#define PATH_TYPESEP '.'
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#ifndef NAME_MAX
+#define NAME_MAX 256
+#endif
+
+
+#define PATH_STRING 0
+#define PATH_HOST 1
+#define PATH_DEVICE 2
+#define PATH_DIRECTORY 3
+#define PATH_NAME 4
+#define PATH_TYPE 5
+#define PATH_VERSION 6
+
+void LispPathnameInit(void);
+
+LispObj *Lisp_Directory(LispBuiltin*);
+LispObj *Lisp_Namestring(LispBuiltin*);
+LispObj *Lisp_FileNamestring(LispBuiltin*);
+LispObj *Lisp_DirectoryNamestring(LispBuiltin*);
+LispObj *Lisp_EnoughNamestring(LispBuiltin*);
+LispObj *Lisp_HostNamestring(LispBuiltin*);
+LispObj *Lisp_MakePathname(LispBuiltin*);
+LispObj *Lisp_Pathnamep(LispBuiltin*);
+LispObj *Lisp_ParseNamestring(LispBuiltin*);
+LispObj *Lisp_PathnameHost(LispBuiltin*);
+LispObj *Lisp_PathnameDevice(LispBuiltin*);
+LispObj *Lisp_PathnameDirectory(LispBuiltin*);
+LispObj *Lisp_PathnameName(LispBuiltin*);
+LispObj *Lisp_PathnameType(LispBuiltin*);
+LispObj *Lisp_PathnameVersion(LispBuiltin*);
+LispObj *Lisp_Truename(LispBuiltin*);
+LispObj *Lisp_ProbeFile(LispBuiltin*);
+LispObj *Lisp_UserHomedirPathname(LispBuiltin*);
+
+#endif /* Lisp_pathname_h */
diff --git a/lisp/private.h b/lisp/private.h
new file mode 100644
index 0000000..6e5b128
--- /dev/null
+++ b/lisp/private.h
@@ -0,0 +1,536 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/private.h,v 1.39 2002/12/20 04:32:46 paulo Exp $ */
+
+#ifndef Lisp_private_h
+#define Lisp_private_h
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "internal.h"
+
+#include "core.h"
+#ifdef DEBUGGER
+#include "debugger.h"
+#endif
+#include "helper.h"
+#include "string.h"
+#include "struct.h"
+
+/*
+ * Defines
+ */
+#define STRTBLSZ 23
+#define MULTIPLE_VALUES_LIMIT 127
+#define MAX_STACK_DEPTH 16384
+
+#define FEATURES \
+ lisp__data.features->data.atom->a_object ? \
+ lisp__data.features->data.atom->property->value : \
+ NIL
+#define PACK lisp__data.packlist
+#define PACKAGE lisp__data.package->data.atom->property->value
+#define MOD lisp__data.modlist
+#define COD lisp__data.codlist
+#define RUN lisp__data.runlist
+#define RES lisp__data.reslist
+#define DBG lisp__data.dbglist
+#define BRK lisp__data.brklist
+#define PRO lisp__data.prolist
+
+#define SINPUT lisp__data.input
+#define SOUTPUT lisp__data.output
+#define STANDARD_INPUT \
+ lisp__data.standard_input->data.atom->property->value
+#define STANDARD_OUTPUT \
+ lisp__data.standard_output->data.atom->property->value
+#define STANDARDSTREAM(file, desc, flags) \
+ LispNewStandardStream(file, desc, flags)
+
+/*
+ * Types
+ */
+typedef struct _LispStream LispStream;
+typedef struct _LispBlock LispBlock;
+typedef struct _LispOpaque LispOpaque;
+typedef struct _LispModule LispModule;
+typedef struct _LispProperty LispProperty;
+typedef struct _LispObjList LispObjList;
+typedef struct _LispStringHash LispStringHash;
+typedef struct _LispCharInfo LispCharInfo;
+
+
+/* Normal function/macro arguments */
+typedef struct _LispNormalArgs {
+ int num_symbols;
+ LispObj **symbols; /* symbol names */
+} LispNormalArgs;
+
+/* &optional function/macro arguments */
+typedef struct _LispOptionalArgs {
+ int num_symbols;
+ LispObj **symbols; /* symbol names */
+ LispObj **defaults; /* default values, when unspecifed */
+ LispObj **sforms; /* T if variable specified, NIL otherwise */
+} LispOptionalArgs;
+
+/* &key function/macro arguments */
+typedef struct _LispKeyArgs {
+ int num_symbols;
+ LispObj **symbols; /* symbol names */
+ LispObj **defaults; /* default values */
+ LispObj **sforms; /* T if variable specified, NIL otherwise */
+ LispObj **keys; /* key names, for special keywords */
+} LispKeyArgs;
+
+/* &aux function/macro arguments */
+typedef struct _LispAuxArgs {
+ int num_symbols;
+ LispObj **symbols; /* symbol names */
+ LispObj **initials; /* initial values */
+} LispAuxArgs;
+
+/* characters in the field description have the format:
+ * '.' normals has a list of normal arguments
+ * 'o' optionals has a list of &optional arguments
+ * 'k' keys has a list of &key arguments
+ * 'r' rest is a valid pointer to a &rest symbol
+ * 'a' auxs has a list of &aux arguments
+ */
+typedef struct _LispArgList {
+ LispNormalArgs normals;
+ LispOptionalArgs optionals;
+ LispKeyArgs keys;
+ LispObj *rest;
+ LispAuxArgs auxs;
+ int num_arguments;
+ char *description;
+} LispArgList;
+
+typedef enum _LispDocType_t {
+ LispDocVariable,
+ LispDocFunction,
+ LispDocStructure,
+ LispDocType,
+ LispDocSetf
+} LispDocType_t;
+
+struct _LispProperty {
+ /* may be used by multiple packages */
+ unsigned int refcount;
+
+ /* package where the property was created */
+ LispPackage *package;
+
+ /* value of variable attached to symbol */
+ LispObj *value;
+
+ union {
+ /* function attached to symbol */
+ LispObj *function;
+ /* builtin function attached to symbol*/
+ LispBuiltin *builtin;
+ } fun;
+ /* function/macro argument list description */
+ LispArgList *alist;
+
+ /* symbol properties list */
+ LispObj *properties;
+
+ /* setf method */
+ LispObj *setf;
+ /* setf argument list description */
+ LispArgList *salist;
+
+ /* structure information */
+ struct {
+ LispObj *definition;
+#define STRUCT_NAME -3
+#define STRUCT_CHECK -2
+#define STRUCT_CONSTRUCTOR -1
+ int function; /* if >= 0, it is a structure field index */
+ } structure;
+};
+
+struct _LispAtom {
+ /* hint: dynamically binded variable */
+ unsigned int dyn : 1;
+
+ /* Property has useful data in value field */
+ unsigned int a_object : 1;
+ /* Property has useful data in fun.function field */
+ unsigned int a_function : 1;
+ /* Property has useful data in fun.builtin field */
+ unsigned int a_builtin : 1;
+ /* Property has useful data in fun.function field */
+ unsigned int a_compiled : 1;
+ /* Property has useful data in properties field */
+ unsigned int a_property : 1;
+ /* Property has useful data in setf field */
+ unsigned int a_defsetf : 1;
+ /* Property has useful data in defstruct field */
+ unsigned int a_defstruct : 1;
+
+ /* Symbol is extern */
+ unsigned int ext : 1;
+
+ /* Symbol must be quoted with '|' to be allow reading back */
+ unsigned int unreadable : 1;
+
+ /* Symbol value may need special handling when changed */
+ unsigned int watch : 1;
+
+ /* 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];
+};
+
+struct _LispObjList {
+ LispObj **pairs; /* name0 ... nameN */
+ int length; /* number of objects */
+ int space; /* space allocated in field pairs */
+};
+
+struct _LispPackage {
+ LispObjList glb; /* global symbols in package */
+ LispObjList use; /* inherited packages */
+ LispAtom *atoms[STRTBLSZ]; /* atoms in this package */
+};
+
+struct _LispOpaque {
+ int type;
+ char *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;
+};
+
+typedef enum _LispBlockType {
+ LispBlockNone, /* no block */
+ LispBlockTag, /* may become "invisible" */
+ LispBlockCatch, /* can be used to jump across function calls */
+ LispBlockClosure, /* hides blocks of type LispBlockTag bellow it */
+ LispBlockProtect, /* used by unwind-protect */
+ LispBlockBody /* used by tagbody and go */
+} LispBlockType;
+
+struct _LispBlock {
+ LispBlockType type;
+ LispObj *tag;
+ jmp_buf jmp;
+ int stack;
+ int protect;
+ int block_level;
+#ifdef DEBUGGER
+ int debug_level;
+ int debug_step;
+#endif
+};
+
+struct _LispModule {
+ LispModule *next;
+ void *handle;
+ LispModuleData *data;
+};
+
+typedef struct _LispUngetInfo {
+ char buffer[16];
+ int offset;
+} LispUngetInfo;
+
+struct _LispMac {
+ /* stack for builtin function arguments */
+ struct {
+ LispObj **values;
+ int base; /* base of arguments to function */
+ int length;
+ int space;
+ } stack;
+
+ /* environment */
+ struct {
+ LispObj **values;
+ Atom_id *names;
+ int lex; /* until where variables are visible */
+ int head; /* top of environment */
+ int length; /* number of used pairs */
+ int space; /* number of objects in pairs */
+ } env;
+
+ struct {
+ LispObj **values;
+ int count;
+ } returns;
+
+ struct {
+ LispObj **objects;
+ int length;
+ int space;
+ } protect;
+
+ LispObj *package; /* package object */
+ LispPackage *pack; /* pointer to lisp__data.package->data.package.package */
+
+ /* fast access to the KEYWORD package */
+ LispObj *keyword;
+ LispPackage *key;
+
+ /* the LISP package */
+ LispObj *lisp;
+
+ /* only used if the package was changed, but an error generated
+ * before returning to the toplevel */
+ LispObj *savepackage;
+ LispPackage *savepack;
+
+ struct {
+ int block_level;
+ int block_size;
+ LispObj *block_ret;
+ LispBlock **block;
+ } block;
+
+ sigjmp_buf jmp;
+
+ struct {
+ unsigned int expandbits : 3; /* code doesn't look like reusing cells
+ * so try to have a larger number of
+ * free cells */
+ unsigned int immutablebits : 1; /* need to reset immutable bits */
+ unsigned int timebits : 1; /* update gctime counter */
+ unsigned int count;
+ long gctime;
+ int average; /* of cells freed after gc calls */
+ } gc;
+
+ LispStringHash *strings[STRTBLSZ];
+ LispOpaque *opqs[STRTBLSZ];
+ int opaque;
+
+ LispObj *standard_input, *input, *input_list;
+ LispObj *standard_output, *output, *output_list;
+ LispObj *error_stream;
+ LispUngetInfo **unget;
+ int iunget, nunget;
+ int eof;
+
+ int interactive;
+ int errexit;
+
+ struct {
+ int index;
+ int level;
+ int space;
+ void **mem;
+ } mem; /* memory from Lisp*Alloc, to be release in error */
+ LispModule *module;
+ LispObj *modules;
+ char *prompt;
+
+ LispObj *features;
+
+ LispObj *modlist; /* module list */
+ LispObj *packlist; /* list of packages */
+ LispObj *codlist; /* current code */
+ LispObj *runlist[3]; /* +, ++, and +++ */
+ LispObj *reslist[3]; /* *, **, and *** */
+#ifdef DEBUGGER
+ LispObj *dbglist; /* debug information */
+ LispObj *brklist; /* breakpoints information */
+#endif
+ LispObj *prolist; /* protect objects list */
+
+#ifdef SIGNALRETURNSINT
+ int (*sigint)(int);
+ int (*sigfpe)(int);
+#else
+ void (*sigint)(int);
+ void (*sigfpe)(int);
+#endif
+
+ int destroyed; /* reached LispDestroy, used by unwind-protect */
+ int running; /* there is somewhere to siglongjmp */
+
+ int ignore_errors; /* inside a ignore-errors block */
+ LispObj *error_condition; /* actually, a string */
+
+ int debugging; /* debugger enabled? */
+#ifdef DEBUGGER
+ int debug_level; /* almost always the same as lisp__data.level */
+ int debug_step; /* control for stoping and printing output */
+ int debug_break; /* next breakpoint number */
+ LispDebugState debug;
+#endif
+};
+
+struct _LispCharInfo {
+ char **names;
+};
+
+
+/*
+ * Prototypes
+ */
+void LispUseArgList(LispArgList*);
+void LispFreeArgList(LispArgList*);
+LispArgList *LispCheckArguments(LispFunType, LispObj*, char*, int);
+LispObj *LispListProtectedArguments(LispArgList*);
+
+LispObj *LispGetDoc(LispObj*);
+LispObj *LispGetVar(LispObj*);
+#ifdef DEBUGGER
+void *LispGetVarAddr(LispObj*); /* used by debugger */
+#endif
+LispObj *LispAddVar(LispObj*, LispObj*);
+LispObj *LispSetVar(LispObj*, LispObj*);
+void LispUnsetVar(LispObj*);
+
+ /* only used at initialization time */
+LispObj *LispNewStandardStream(LispFile*, LispObj*, int);
+
+ /* create a new package */
+LispObj *LispNewPackage(LispObj*, LispObj*);
+ /* add package to use-list of current, and imports all extern symbols */
+void LispUsePackage(LispObj*);
+ /* make symbol extern in the current package */
+void LispExportSymbol(LispObj*);
+ /* imports symbol to current package */
+void LispImportSymbol(LispObj*);
+
+ /* always returns the same string */
+char *LispGetAtomString(char*, int);
+
+/* destructive fast reverse, note that don't receive a LispMac* argument */
+LispObj *LispReverse(LispObj *list);
+
+char *LispIntToOpaqueType(int);
+
+/* (print) */
+void LispPrint(LispObj*, LispObj*, int);
+
+LispBlock *LispBeginBlock(LispObj*, LispBlockType);
+#define BLOCKJUMP(block) \
+ lisp__data.stack.length = (block)->stack; \
+ lisp__data.protect.length = (block)->protect; \
+ longjmp((block)->jmp, 1)
+void LispEndBlock(LispBlock*);
+ /* if unwind-protect active, jump to cleanup code, else do nothing */
+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*);
+ /* put value in atom's property list */
+LispObj *LispPutAtomProperty(LispAtom*, LispObj*, LispObj*);
+ /* remove value from atom's property list */
+LispObj *LispRemAtomProperty(LispAtom*, LispObj*);
+ /* replace atom's property list */
+LispObj *LispReplaceAtomPropertyList(LispAtom*, LispObj*);
+
+ /* returns function associated with symbol */
+LispObj *LispSymbolFunction(LispObj*);
+ /* returns symbol string name */
+LispObj *LispSymbolName(LispObj*);
+
+ /* define byte compiled function, or replace definition */
+void LispSetAtomCompiledProperty(LispAtom*, LispObj*);
+ /* remove byte compiled function property */
+void LispRemAtomCompiledProperty(LispAtom*);
+ /* define function, or replace function definition */
+void LispSetAtomFunctionProperty(LispAtom*, LispObj*, LispArgList*);
+ /* remove function property */
+void LispRemAtomFunctionProperty(LispAtom*);
+ /* define builtin, or replace builtin definition */
+void LispSetAtomBuiltinProperty(LispAtom*, LispBuiltin*, LispArgList*);
+ /* remove builtin property */
+void LispRemAtomBuiltinProperty(LispAtom*);
+ /* define setf macro, or replace current definition */
+void LispSetAtomSetfProperty(LispAtom*, LispObj*, LispArgList*);
+ /* remove setf macro */
+void LispRemAtomSetfProperty(LispAtom*);
+ /* create or change structure property */
+void LispSetAtomStructProperty(LispAtom*, LispObj*, int);
+ /* remove structure property */
+void LispRemAtomStructProperty(LispAtom*);
+
+void LispProclaimSpecial(LispObj*, LispObj*, LispObj*);
+void LispDefconstant(LispObj*, LispObj*, LispObj*);
+
+void LispAddDocumentation(LispObj*, LispObj*, LispDocType_t);
+void LispRemDocumentation(LispObj*, LispDocType_t);
+LispObj *LispGetDocumentation(LispObj*, LispDocType_t);
+
+/* increases storage for functions returning multiple values */
+void LispMoreReturns(void);
+
+/* increases storage for temporarily protected data */
+void LispMoreProtects(void);
+
+/* Initialization */
+extern int LispArgList_t;
+extern LispCharInfo LispChars[256];
+
+/* This function will return if the interpreter cannot be stopped */
+extern void LispSignal(int);
+
+void LispDisableInterrupts(void);
+void LispEnableInterrupts(void);
+#define DISABLE_INTERRUPTS() LispDisableInterrupts()
+#define ENABLE_INTERRUPTS() LispEnableInterrupts()
+
+/* Value returned by LispBegin, used everywhere in the code.
+ * Only one interpreter instance allowed. */
+extern LispMac lisp__data;
+
+#endif /* Lisp_private_h */
diff --git a/lisp/re/README b/lisp/re/README
new file mode 100644
index 0000000..848e1e9
--- /dev/null
+++ b/lisp/re/README
@@ -0,0 +1,121 @@
+$XFree86: xc/programs/xedit/lisp/re/README,v 1.4 2002/11/15 07:01:32 paulo Exp $
+
+LAST UPDATED: $Date$
+
+ This is a small regex library for fast matching tokens in text. It was built
+to be used by xedit and it's syntax highlight code. It is not compliant with
+IEEE Std 1003.2, but is expected to be used where very fast matching is
+required, and exotic patterns will not be used.
+
+ To understand what kind of patterns this library is expected to be used with,
+see the file <XRoot>xc/programs/xedit/lisp/modules/progmodes/c.lsp and some
+samples in the file tests.txt, with comments for patterns that will not work,
+or may give incorrect results.
+
+ The library is not built upon the standard regex library by Henry Spencer,
+but is completely written from scratch, but it's syntax is heavily based on
+that library, and the only reason for it to exist is that unfortunately
+the standard version does not fit the requirements needed by xedit.
+Anyways, I would like to thanks Henry for his regex library, it is a really
+very useful tool.
+
+ Small description of understood tokens:
+
+ M A T C H I N G
+------------------------------------------------------------------------
+. Any character (won't match newline if compiled with RE_NEWLINE)
+\w Any word letter (shortcut to [a-zA-Z0-9_]
+\W Not a word letter (shortcut to [^a-zA-Z0-9_]
+\d Decimal number
+\D Not a decimal number
+\s A space
+\S Not a space
+\l A lower case letter
+\u An upper case letter
+\c A control character, currently the range 1-32 (minus tab)
+\C Not a control character
+\o Octal number
+\O Not an octal number
+\x Hexadecimal number
+\X Not an hexadecimal number
+\< Beginning of a word (matches an empty string)
+\> End of a word (matches an empty string)
+^ Beginning of a line (matches an empty string)
+$ End of a line (matches an empty string)
+[...] Matches one of the characters inside the brackets
+ ranges are specified separating two characters with "-".
+ If the first character is "^", matches only if the
+ character is not in this range. To add a "]" make it
+ the first character, and to add a "-" make it the last.
+\1 to \9 Backreference, matches the text that was matched by a group,
+ that is, text that was matched by the pattern inside
+ "(" and ")".
+
+
+ O P E R A T O R S
+------------------------------------------------------------------------
+() Any pattern inside works as a backreference, and is also
+ used to group patterns.
+| Alternation, allows choosing different possibilities, like
+ character ranges, but allows patterns of different lengths.
+
+
+ R E P E T I T I O N
+------------------------------------------------------------------------
+<re>* <re> may occur any number of times, including zero
+<re>+ <re> must occur at least once
+<re>? <re> is optional
+<re>{<e>} <re> must occur exactly <e> times
+<re>{<n>,} <re> must occur at least <n> times
+<re>{,<m>} <re> must not occur more than <m> times
+<re>{<n>,<m>} <re> must occur at least <n> times, but no more than <m>
+
+
+ Note that "." is a special character, and when used with a repetition
+operator it changes completely its meaning. For example, ".*" matches
+anything up to the end of the input string (unless the pattern was compiled
+with RE_NEWLINE, in that case it will match anything, but a newline).
+
+
+ Limitations:
+
+o Only minimal matches supported. The engine has only one level "backtracking",
+ so, it also only does minimal matches to allow backreferences working
+ properly, and to avoid failing to match depending on the input.
+
+o Only one level "grouping", for example, with the pattern:
+ (a(b)c)
+ If "abc" is anywhere in the input, it will be in "\1", but there will
+ not exist a "\2" for "b".
+
+o Some "special repetitions" were not implemented, these are:
+ .{<e>}
+ .{<n>,}
+ .{,<m>}
+ .{<n>,<m>}
+
+o Some patterns will never match, for example:
+ \w*\d
+ Since "\w*" already includes all possible matches of "\d", "\d" will
+ only be tested when "\w*" failed. There are no plans to make such
+ patterns work.
+
+
+ Some of these limitations may be worked on future versions of the library,
+but this is not what the library is expected to do, and, adding support for
+correct handling of these would probably make the library slower, what is
+not the reason of it to exist in the first time.
+
+ If you need "true" regex than this library is not for you, but if all
+you need is support for very quickly finding simple patterns, than this
+library can be a very powerful tool, on some patterns it can run more
+than 200 times faster than "true" regex implementations! And this is
+the reason it was written.
+
+
+
+ Send comments and code to me (paulo@XFree86.Org) or to the XFree86
+mailing/patch lists.
+
+--
+Paulo
diff --git a/lisp/re/re.c b/lisp/re/re.c
new file mode 100644
index 0000000..d848a4b
--- /dev/null
+++ b/lisp/re/re.c
@@ -0,0 +1,2648 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/re/re.c,v 1.9 2002/12/11 04:44:28 paulo Exp $ */
+
+#include <stdio.h>
+#include "rep.h"
+#define DEBUG
+/*
+ * Types
+ */
+
+/* Information used when generating the final form of the compiled re.
+ */
+struct _re_inf {
+ rec_alt *alt;
+ unsigned char *cod;
+ long len;
+ long spc;
+
+ /* Start offset of special repetition instruction */
+ long sr[MAX_DEPTH];
+
+ /* Jump offset of special repetition instruction */
+ long sj[MAX_DEPTH];
+
+ /* Just a flag, to know if this nesting is for a special repetition */
+ char sp[MAX_DEPTH];
+
+ int bas; /* Alternatives/repetitions depth */
+ int par; /* Open parenthesis counter */
+ int ref; /* Backreference counter */
+
+ rec_pat *apat; /* Alternatives duplicate patterns
+ * if a special repetition is found,
+ * this is done to somewhat simplify
+ * the bytecode engine and still allow
+ * some complex (and time consuming)
+ * patterns. */
+
+ int flags;
+ int ecode;
+};
+
+/* This structure is not associated with re_cod as it's data only matters
+ * to the current match search.
+ */
+struct _re_eng {
+ unsigned char *bas; /* Base string pointer */
+ unsigned char *str; /* String to search for pattern */
+ unsigned char *end; /* Where to stop searching */
+ unsigned char *cod; /* Pointer in the re_cod structure */
+ long off; /* Number of used entries in so/eo etc */
+
+ /* Match offset/nesting information */
+ long so[MAX_DEPTH]; /* (s)tart of (m)atch */
+ long eo[MAX_DEPTH]; /* (e)nd of (m)atch */
+ long sv[MAX_DEPTH]; /* (s)a(v)e match end offset */
+ long re[MAX_DEPTH]; /* (re)petition count */
+ long ss[MAX_DEPTH]; /* (s)ave (s)tart of match */
+ unsigned char *rcod[MAX_DEPTH]; /* restart position in regex code */
+ unsigned char *rstr[MAX_DEPTH]; /* restart position in string */
+
+ /* Group/backreference information */
+ long goff;
+ long gso[9];
+ long geo[9];
+};
+
+/*
+ * Prototypes
+ */
+static void reinit(void);
+static int rec_check(re_inf*, int);
+static int rec_code(re_inf*, ReCode);
+static int rec_byte(re_inf*, int);
+static int rec_byte_byte(re_inf*, int, int);
+static int rec_code_byte(re_inf*, ReCode, int);
+static int rec_length(re_inf*, int);
+static int rec_code_byte_byte(re_inf*, ReCode, int, int);
+static int rec_build_alt(re_inf*, rec_alt*);
+static int rec_build_pat(re_inf*, rec_pat*);
+static int rec_build_rng(re_inf*, rec_rng*);
+static int rec_build_grp(re_inf*, rec_grp*);
+static int rec_build_stl(re_inf*, rec_stl*);
+static int rec_build_rep(re_inf*, rec_rep*);
+static int rec_inc_spc(re_inf*);
+static int rec_dec_spc(re_inf*);
+static int rec_add_spc(re_inf*, int);
+static int rec_off_spc(re_inf*);
+static int rec_alt_spc(re_inf*, int);
+static int rec_rep_spc(re_inf*, int);
+#ifdef DEBUG
+static void redump(re_cod*);
+#endif
+
+/*
+ * Initialization
+ */
+unsigned char re__alnum[256];
+unsigned char re__odigit[256];
+unsigned char re__ddigit[256];
+unsigned char re__xdigit[256];
+unsigned char re__control[256];
+
+/*
+ * Implementation
+ */
+int
+recomp(re_cod *preg, const char *pattern, int flags)
+{
+ int i, ecode;
+ re_inf inf;
+
+ reinit();
+
+ preg->cod = NULL;
+ inf.alt = irec_comp(pattern,
+ flags & RE_PEND ? preg->re_endp :
+ pattern + strlen(pattern),
+ flags, &ecode);
+ if (ecode != 0)
+ return (ecode);
+
+ inf.cod = NULL;
+ inf.len = inf.spc = 0;
+ inf.bas = 0;
+ inf.par = 0;
+ inf.ref = 0;
+ inf.apat = NULL;
+ inf.flags = flags;
+ inf.ecode = 0;
+ for (i = 0; i < MAX_DEPTH; i++)
+ inf.sp[i] = 0;
+
+ /* First byte is runtime modifier flags */
+ if (rec_byte(&inf, flags & (RE_NEWLINE | RE_NOSUB)) == 0 &&
+ rec_byte(&inf, 0xff) == 0 &&
+ rec_build_alt(&inf, inf.alt) == 0 &&
+ rec_rep_spc(&inf, 0) == 0 &&
+ rec_code(&inf, Re_Done) == 0) {
+ /* Number of possible references, loops will not leave this
+ * value correct, but it is cheap to read it from the second
+ * byte, instead of adding several extra checks in the bytecode. */
+ if (inf.ref)
+ inf.cod[1] = inf.ref - 1;
+ preg->cod = inf.cod;
+ /* Public structure member */
+ preg->re_nsub = inf.ref;
+ }
+
+ irec_free_alt(inf.alt);
+ if (inf.ecode)
+ free(inf.cod);
+#ifdef DEBUG
+ else if (flags & RE_DUMP)
+ redump(preg);
+#endif
+
+ return (inf.ecode);
+}
+
+int
+reexec(const re_cod *preg, const char *string,
+ int nmatch, re_mat pmat[], int flags)
+{
+ unsigned char *ptr, *str, newline, nosub;
+ int len, si, ci, bas, i, j, k, l, m;
+ re_eng eng;
+
+ if (preg == NULL || preg->cod == NULL || nmatch < 0 ||
+ ((flags & RE_STARTEND) &&
+ (pmat == NULL || pmat[0].rm_eo < pmat[0].rm_so)))
+ return (RE_INVARG);
+
+ eng.str = (unsigned char*)string;
+ if (flags & RE_STARTEND) {
+ eng.end = eng.str + pmat[0].rm_eo;
+ eng.str += pmat[0].rm_so;
+ }
+ else
+ eng.end = eng.str + strlen(string);
+ eng.bas = eng.str;
+ nosub = preg->cod[0] & RE_NOSUB;
+ newline = preg->cod[0] & RE_NEWLINE;
+ eng.cod = preg->cod + 2;
+
+ if (!nosub && preg->cod[1] != 0xff) {
+ for (i = 0; i <= preg->cod[1]; i++) {
+ eng.gso[i] = 0;
+ eng.geo[i] = -1;
+ }
+ }
+
+ /* Setup to search for start of match from the first character */
+ eng.so[0] = 0;
+ eng.eo[0] = eng.sv[0] = -1;
+ eng.rcod[0] = eng.cod;
+ eng.rstr[0] = eng.str + 1;
+ eng.off = 0;
+ eng.goff = -1;
+ for (ci = si = 1;;) {
+reset:
+ switch (*eng.cod) {
+ /****************************************************
+ * One byte codes *
+ ****************************************************/
+ case Re_Any:
+ if (eng.str == eng.end || (newline && eng.str[0] == '\n'))
+ goto fail;
+ goto match;
+ case Re_AnyEatAnyTimes:
+ if (newline) {
+ for (ptr = eng.str; ptr < eng.end; ptr++) {
+ if (*ptr == '\n')
+ break;
+ }
+ si = ptr - eng.str;
+ }
+ else
+ si = eng.end - eng.str;
+ goto match;
+ case Re_AnyEatMaybe:
+ si = eng.end > eng.str;
+ if (newline && si && eng.str[0] == '\n')
+ si = 0;
+ goto match;
+ case Re_AnyEatAtLeast:
+ if (newline) {
+ for (ptr = eng.str; ptr < eng.end; ptr++) {
+ if (*ptr == '\n')
+ break;
+ }
+ si = ptr - eng.str;
+ }
+ else
+ si = eng.end - eng.str;
+ if (si == 0) {
+ si = 1;
+ goto fail;
+ }
+ goto match;
+ case Re_Odigit:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (re__odigit[eng.str[0]])
+ goto match;
+ goto fail;
+ case Re_OdigitNot:
+ if (eng.str >= eng.end || re__odigit[eng.str[0]])
+ goto fail;
+ goto match;
+ case Re_Digit:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (re__ddigit[eng.str[0]])
+ goto match;
+ goto fail;
+ case Re_DigitNot:
+ if (eng.str >= eng.end || re__ddigit[eng.str[0]])
+ goto fail;
+ goto match;
+ case Re_Xdigit:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (re__xdigit[eng.str[0]])
+ goto match;
+ goto fail;
+ case Re_XdigitNot:
+ if (eng.str >= eng.end || re__xdigit[eng.str[0]])
+ goto fail;
+ goto match;
+ case Re_Space:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] == ' ' || eng.str[0] == '\t')
+ goto match;
+ goto fail;
+ case Re_SpaceNot:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] != ' ' && eng.str[0] != '\t')
+ goto match;
+ goto fail;
+ case Re_Tab:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] == '\t')
+ goto match;
+ goto fail;
+ case Re_Newline:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] == '\n')
+ goto match;
+ goto fail;
+ case Re_Lower:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] >= 'a' && eng.str[0] <= 'z')
+ goto match;
+ goto fail;
+ case Re_Upper:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] >= 'A' && eng.str[0] <= 'Z')
+ goto match;
+ goto fail;
+ case Re_Alnum:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (re__alnum[eng.str[0]])
+ goto match;
+ goto fail;
+ case Re_AlnumNot:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (re__alnum[eng.str[0]])
+ goto fail;
+ goto match;
+ case Re_Control:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (re__control[eng.str[0]])
+ goto match;
+ goto fail;
+ case Re_ControlNot:
+ if (eng.str >= eng.end || re__control[eng.str[0]])
+ goto fail;
+ goto match;
+
+ /****************************************************
+ * One byte codes, match special emtpy strings *
+ ****************************************************/
+ case Re_Bol:
+ if (eng.str == eng.bas) {
+ if ((flags & RE_NOTBOL)) {
+ /* String does not start at the beginning of a line */
+ if (newline)
+ goto fail;
+ goto wont;
+ }
+ si = 0;
+ goto match;
+ }
+ if (newline && eng.str[-1] == '\n') {
+ si = 0;
+ goto match;
+ }
+ goto fail;
+ case Re_Eol:
+ if (eng.str == eng.end) {
+ if (flags & RE_NOTEOL)
+ /* String does not finish at the end of a line */
+ goto wont;
+ si = 0;
+ goto match;
+ }
+ if (newline && eng.str[0] == '\n') {
+ si = 0;
+ goto match;
+ }
+ goto fail;
+ case Re_Bow:
+ if (eng.str >= eng.end ||
+ (eng.str > eng.bas &&
+ (re__alnum[eng.str[-1]])))
+ goto fail;
+ if (re__alnum[eng.str[0]]) {
+ si = 0;
+ goto match;
+ }
+ goto fail;
+ case Re_Eow:
+ if (eng.str == eng.bas ||
+ (eng.str < eng.end &&
+ re__alnum[eng.str[0]]))
+ goto fail;
+ if (re__alnum[eng.str[-1]]) {
+ si = 0;
+ goto match;
+ }
+ goto fail;
+
+ /****************************************************
+ * One byte code, one byte argument *
+ ****************************************************/
+ case Re_Literal:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] == eng.cod[1]) {
+ ci = 2;
+ goto match;
+ }
+ goto fail;
+ case Re_LiteralNot:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] != eng.cod[1]) {
+ ci = 2;
+ goto match;
+ }
+ goto fail;
+ case Re_SearchLiteral:
+ for (str = eng.str; str < eng.end; str++) {
+ if (*str == eng.cod[1]) {
+ ci = 2;
+ eng.str = str;
+ goto match;
+ }
+ }
+ /* This bytecode only happens in the toplevel */
+ eng.so[0] = str - eng.bas;
+ eng.str = str;
+ goto fail;
+
+ /****************************************************
+ * One byte code, two bytes argument *
+ ****************************************************/
+ case Re_CaseLiteral:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] == eng.cod[1] || eng.str[0] == eng.cod[2]) {
+ ci = 3;
+ goto match;
+ }
+ goto fail;
+ case Re_CaseLiteralNot:
+ if (eng.str >= eng.end)
+ goto fail;
+ if (eng.str[0] != eng.cod[1] && eng.str[0] != eng.cod[2]) {
+ ci = 3;
+ goto match;
+ }
+ goto fail;
+ case Re_SearchCaseLiteral:
+ for (str = eng.str; str < eng.end; str++) {
+ if (*str == eng.cod[1] || *str == eng.cod[2]) {
+ ci = 3;
+ eng.str = str;
+ goto match;
+ }
+ }
+ eng.so[0] = str - eng.bas;
+ eng.str = str;
+ goto fail;
+
+ /****************************************************
+ * One byte codes, two arguments, n bytes *
+ ****************************************************/
+ case Re_String:
+ len = eng.cod[1];
+ if (len & 0x80) {
+ i = 3;
+ len = (len & 0x7f) + (eng.cod[2] << 7);
+ }
+ else
+ i = 2;
+ if (eng.end - eng.str < len)
+ goto fail;
+ ptr = eng.cod + i;
+ str = eng.str;
+ for (k = len; k > 0; k--) {
+ if (*ptr++ != *str++)
+ goto fail;
+ }
+ ci = i + len;
+ si = len;
+ goto match;
+ case Re_SearchString:
+ len = eng.cod[1];
+ if (len & 0x80) {
+ i = 3;
+ len = (len & 0x7f) + (eng.cod[2] << 7);
+ }
+ else
+ i = 2;
+ for (str = eng.str; eng.end - str >= len; str = eng.str++) {
+ for (ptr = eng.cod + i, str = eng.str, k = len; k > 0; k--)
+ if (*ptr++ != *str++)
+ break;
+ if (k == 0) {
+ /* Substring found */
+ ci = i + len;
+ si = str - eng.str;
+ goto match;
+ }
+ }
+ eng.so[0] = eng.end - eng.bas;
+ eng.str = eng.end;
+ goto fail;
+
+ case Re_CaseString:
+ len = eng.cod[1];
+ if (len & 0x80) {
+ i = 3;
+ len = (len & 0x7f) + (eng.cod[2] << 7);
+ }
+ else
+ i = 2;
+
+ len >>= 1;
+ /* Check if there are at least len/2 bytes left, string
+ * is represented as two bytes, lower and upper case */
+ if (eng.end - eng.str < len)
+ goto fail;
+ ptr = eng.cod + i;
+ str = eng.str;
+ for (k = len; k > 0; str++, ptr += 2, k--) {
+ if (*str != ptr[0] && *str != ptr[1])
+ goto fail;
+ }
+ ci = i + (len << 1);
+ si = len;
+ goto match;
+ case Re_SearchCaseString:
+ len = eng.cod[1];
+ if (len & 0x80) {
+ i = 3;
+ len = (len & 0x7f) + (eng.cod[2] << 7);
+ }
+ else
+ i = 2;
+ len >>= 1;
+ for (str = eng.str; eng.end - str >= len; str = eng.str++) {
+ for (ptr = eng.cod + i, str = eng.str, k = len;
+ k > 0; k--, ptr += 2, str++)
+ if (ptr[0] != str[0] && ptr[1] != str[0])
+ break;
+ if (k == 0) {
+ /* Substring found */
+ ci = i + (len << 1);
+ si = str - eng.str;
+ goto match;
+ }
+ }
+ eng.so[0] = eng.end - eng.bas;
+ eng.str = eng.end;
+ goto fail;
+
+ case Re_StringList:
+ /* Number of strings */
+ k = eng.cod[1];
+
+ /* Where to jump after match */
+ bas = eng.cod[2] | (eng.cod[3] << 8);
+
+ str = eng.str;
+ ptr = eng.cod + k + 4;
+ l = eng.end - eng.str;
+ for (j = 0; j < k; j++) {
+ len = eng.cod[j + 4];
+ if (len <= l) {
+ for (i = 0; i < len; i++)
+ if (ptr[i] != str[i])
+ goto next_stl;
+ goto stl_match;
+ }
+next_stl:
+ ptr += len;
+ }
+ goto fail;
+stl_match:
+ ci = bas;
+ si = len;
+ goto match;
+
+ case Re_CaseStringList:
+ /* Number of strings */
+ k = eng.cod[1];
+
+ /* Where to jump after match */
+ bas = eng.cod[2] | (eng.cod[3] << 8);
+
+ str = eng.str;
+ ptr = eng.cod + k + 4;
+ l = eng.end - eng.str;
+ for (j = 0; j < k; j++) {
+ len = eng.cod[j + 4];
+ if ((len >> 1) <= l) {
+ for (i = m = 0; i < len; m++, i += 2)
+ if (ptr[i] != str[m] && ptr[i + 1] != str[m])
+ goto next_cstl;
+ goto cstl_match;
+ }
+next_cstl:
+ ptr += len;
+ }
+ goto fail;
+cstl_match:
+ ci = bas;
+ si = len >> 1;
+ goto match;
+
+
+ case Re_LargeStringList:
+ /* Where to jump after match */
+ bas = eng.cod[1] | (eng.cod[2] << 8);
+
+ str = eng.str;
+
+ /* First entry in index map */
+ ptr = eng.cod + 3;
+ i = (int)str[0] << 1;
+ j = ptr[i] | (ptr[i + 1] << 8);
+ if (j == 0xffff)
+ /* No entry with this byte */
+ goto fail;
+
+ /* Bytes left in input */
+ l = eng.end - eng.str;
+
+ /* First entry matching initial byte */
+ ptr += 512 + j;
+
+ for (len = ptr[0];
+ str[0] == ptr[1];
+ ptr += len + 1, len = ptr[0]) {
+ if (len <= l) {
+ for (i = 1; i < len; i++) {
+ if (ptr[i + 1] != str[i])
+ goto next_lstl;
+ }
+ ci = bas;
+ si = len;
+ goto match;
+ }
+next_lstl:;
+ }
+ goto fail;
+
+ case Re_LargeCaseStringList:
+ /* Where to jump after match */
+ bas = eng.cod[1] | (eng.cod[2] << 8);
+
+ str = eng.str;
+
+ /* First entry in index map */
+ ptr = eng.cod + 3;
+ i = (int)str[0] << 1;
+ j = ptr[i] | (ptr[i + 1] << 8);
+ if (j == 0xffff)
+ /* No entry with this byte */
+ goto fail;
+
+ /* Bytes left in input */
+ l = eng.end - eng.str;
+
+ /* First entry matching initial byte */
+ ptr += 512 + j;
+
+ for (len = ptr[0];
+ str[0] == ptr[1] || str[0] == ptr[2];
+ ptr += len + 1, len = ptr[0]) {
+ if ((k = (len >> 1)) <= l) {
+ for (i = 2, j = 1; i < len; i += 2, j++) {
+ if (ptr[i + 1] != str[j] && ptr[i + 2] != str[j])
+ goto next_lcstl;
+ }
+ ci = bas;
+ si = k;
+ goto match;
+ }
+next_lcstl:;
+ }
+ goto fail;
+
+
+ /****************************************************
+ * Character range matching *
+ ****************************************************/
+ case Re_Range:
+ if (eng.str < eng.end && eng.cod[eng.str[0] + 1]) {
+ ci = 257;
+ goto match;
+ }
+ goto fail;
+ case Re_RangeNot:
+ if (eng.str >= eng.end || eng.cod[eng.str[0] + 1])
+ goto fail;
+ ci = 257;
+ goto match;
+
+ /****************************************************
+ * Group handling *
+ ****************************************************/
+ case Re_Open:
+ if (++eng.goff >= 9)
+ return (RE_ASSERT);
+ eng.gso[eng.goff] = eng.str - eng.bas;
+ ++eng.cod;
+ continue;
+ case Re_Close:
+ eng.geo[eng.goff] = eng.str - eng.bas;
+ ++eng.cod;
+ continue;
+ case Re_Update:
+ bas = eng.cod[1];
+ eng.geo[eng.goff] = eng.str - eng.bas;
+ eng.cod += 2; /* + Update + bas */
+ continue;
+
+ /****************************************************
+ * Backreference *
+ ****************************************************/
+ case Re_Backref:
+ i = eng.cod[1];
+ j = eng.gso[i];
+ k = eng.geo[i];
+ len = k - j;
+ if (k < j || eng.end - eng.str < len)
+ goto fail;
+ ptr = eng.bas + j;
+ str = eng.str;
+ for (l = len; l > 0; l--) {
+ if (*ptr++ != *str++)
+ goto fail;
+ }
+ ci = 2;
+ si = len;
+ goto match;
+
+ /****************************************************
+ * Alternatives handling *
+ ****************************************************/
+ case Re_Alt:
+ bas = eng.off;
+ if (++eng.off >= MAX_DEPTH)
+ return (RE_ASSERT);
+
+ /* Get offset of next alternative */
+ i = eng.cod[1] | (eng.cod[2] << 8);
+
+ /* Setup for next alternative if the current fails */
+ eng.rcod[eng.off] = eng.cod + i + 1; /* + Alt */
+
+ /* If fail, test the next alternative in the same string */
+ eng.rstr[eng.off] = eng.str;
+
+ /* Setup match offsets */
+ if (eng.so[bas] <= eng.eo[bas])
+ eng.so[eng.off] = eng.eo[bas];
+ else
+ eng.so[eng.off] = eng.so[bas];
+ eng.sv[eng.off] = eng.eo[eng.off] = eng.so[eng.off] - 1;
+
+ /* Save start of possible previous matches */
+ eng.ss[eng.off] = eng.so[bas];
+
+ /* Skip code */
+ eng.cod += 3;
+
+ /* Go try the first alternative */
+ continue;
+
+ case Re_AltNext:
+ bas = eng.off - 1;
+ /* Check if matched and if it is a better match */
+ if (eng.sv[eng.off] - eng.so[eng.off] <
+ eng.eo[eng.off] - eng.so[eng.off])
+ eng.sv[eng.off] = eng.eo[eng.off];
+
+ /* Get offset of next alternative */
+ i = eng.cod[1] | (eng.cod[2] << 8);
+
+ /* Setup for next alternative if the current fails */
+ eng.rcod[eng.off] = eng.cod + i + 1; /* + AltNext */
+
+ /* Setup match offset */
+ eng.eo[eng.off] = eng.so[eng.off] - 1;
+
+ /* Reset string for next alternative */
+ eng.str = eng.rstr[eng.off];
+
+ /* Skip code */
+ eng.cod += 3;
+
+ /* Go try the next alternative */
+ continue;
+
+ case Re_AltDone:
+ bas = eng.off - 1;
+ /* Check if matched and if it is a better match */
+ if (eng.sv[eng.off] - eng.so[eng.off] <
+ eng.eo[eng.off] - eng.so[eng.off])
+ eng.sv[eng.off] = eng.eo[eng.off];
+
+ /* If there is a match */
+ if (eng.sv[eng.off] >= eng.so[eng.off]) {
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.sv[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+
+ /* Pop stack and skip code */
+ --eng.off;
+ ++eng.cod;
+
+ /* Go try next regular expression pattern */
+ continue;
+ }
+
+ /* Failed, reset string and pop stack */
+ eng.str = eng.rstr[eng.off];
+ --eng.off;
+ goto fail;
+
+
+ /****************************************************
+ * Repetition *
+ ****************************************************/
+
+ /* Note that the repetition counter is not
+ * updated for <re>*, <re>+ and <re>?
+ * it is easy to updated, but since it is not
+ * really useful, code to do it was removed
+ * to save a few cpu cicles. */
+
+#define REPETITION_SETUP() \
+ if (++eng.off >= MAX_DEPTH) \
+ return (RE_ASSERT); \
+ \
+ /* Return here for recovery if match fail */ \
+ eng.rcod[eng.off] = eng.cod; \
+ \
+ /* Setup match offsets */ \
+ if (eng.so[bas] <= eng.eo[bas]) \
+ eng.so[eng.off] = eng.eo[bas]; \
+ else \
+ eng.so[eng.off] = eng.so[bas]; \
+ eng.ss[eng.off] = eng.so[bas]; \
+ eng.sv[eng.off] = eng.eo[eng.off] = eng.so[eng.off] - 1;\
+ \
+ /* Skip repetition instruction */ \
+ eng.cod += 4;
+
+
+ case Re_AnyTimes:
+ bas = eng.cod[1];
+ if (eng.off == bas) {
+ /* First iteration */
+ REPETITION_SETUP();
+ }
+ else {
+ if (eng.eo[eng.off] >= eng.so[eng.off] &&
+ eng.eo[eng.off] > eng.sv[eng.off]) {
+ /* Update offset of match */
+ eng.sv[eng.off] = eng.eo[eng.off];
+
+ /* Skip repetition instruction */
+ eng.cod += 4;
+ }
+ else {
+ /* Match failed but it is ok */
+ len = eng.cod[2] | (eng.cod[3] << 8);
+ eng.so[bas] = eng.ss[eng.off];
+ if (eng.sv[eng.off] >= eng.so[eng.off])
+ /* Something matched earlier, update */
+ eng.eo[bas] = eng.sv[eng.off];
+ else if (eng.eo[bas] < eng.so[bas])
+ /* Empty match */
+ eng.eo[bas] = eng.so[bas];
+
+ /* Try next pattern at correct offset */
+ eng.str = eng.bas + eng.eo[bas];
+
+ /* Pop stack and skip code */
+ --eng.off;
+ eng.cod += len;
+ }
+ }
+ continue;
+
+ case Re_Maybe:
+ bas = eng.cod[1];
+ if (eng.off == bas) {
+ /* First iteration */
+ REPETITION_SETUP();
+ }
+ else {
+ /* Matched or first iteration is done */
+ len = eng.cod[2] | (eng.cod[3] << 8);
+ eng.so[bas] = eng.ss[eng.off];
+ if (eng.eo[eng.off] > eng.so[eng.off]) {
+ /* Something matched earlier, update */
+ eng.eo[bas] = eng.eo[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+ /* Don't need to update counter */
+ }
+ else {
+ /* Empty match */
+ if (eng.eo[bas] < eng.so[bas])
+ eng.eo[bas] = eng.so[bas];
+
+ /* Try next pattern at correct offset */
+ eng.str = eng.bas + eng.eo[bas];
+ }
+
+ /* Pop stack */
+ --eng.off;
+
+ /* Skip code */
+ eng.cod += len;
+ }
+ continue;
+
+ case Re_AtLeast:
+ bas = eng.cod[1];
+ if (eng.off == bas) {
+ /* First iteration */
+ REPETITION_SETUP();
+ }
+ else {
+ if (eng.eo[eng.off] >= eng.so[eng.off] &&
+ eng.eo[eng.off] > eng.sv[eng.off]) {
+ /* Update offset of match */
+ eng.sv[eng.off] = eng.eo[eng.off];
+
+ /* Skip repetition instruction */
+ eng.cod += 4;
+ }
+ else {
+ /* Last try failed */
+ len = eng.cod[2] | (eng.cod[3] << 8);
+ if (eng.sv[eng.off] >= eng.so[eng.off]) {
+ /* Something matched earlier, update */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.sv[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+ }
+ else {
+ /* Do it here, so that the fail label does
+ * not need to do too expensive work for
+ * simple patterns. */
+ eng.so[bas] = eng.str - eng.bas;
+
+ /* Zero matches, pop stack and restart */
+ --eng.off;
+ goto fail;
+ }
+
+ /* Pop stack and skip code */
+ --eng.off;
+ eng.cod += len;
+ }
+ }
+ continue;
+
+
+ /****************************************************
+ * Repetition with arguments *
+ ****************************************************/
+ case Re_Exact:
+#define COMPLEX_REPETITION_SETUP_0() \
+ i = eng.cod[1]; \
+ bas = eng.cod[2];
+
+#define COMPLEX_REPETITION_SETUP() \
+ /* First iteration */ \
+ if (++eng.off >= MAX_DEPTH) \
+ return (RE_ASSERT); \
+ \
+ /* Remeber number or repetitions */ \
+ eng.re[eng.off] = 0; \
+ \
+ /* Return here for recovery if match fail */ \
+ eng.rcod[eng.off] = eng.cod; \
+ \
+ /* Setup match offsets */ \
+ if (eng.so[bas] <= eng.eo[bas]) \
+ eng.so[eng.off] = eng.eo[bas]; \
+ else \
+ eng.so[eng.off] = eng.so[bas]; \
+ eng.sv[eng.off] = eng.eo[eng.off] = eng.so[eng.off] - 1;\
+ eng.ss[eng.off] = eng.so[bas]; \
+ \
+ /* Skip repetition instruction */ \
+ eng.cod += 5;
+
+ COMPLEX_REPETITION_SETUP_0();
+ if (eng.off == bas) {
+ /* First iteration */
+ COMPLEX_REPETITION_SETUP();
+ }
+ else {
+ if (eng.eo[eng.off] >= eng.so[eng.off] &&
+ eng.eo[eng.off] > eng.sv[eng.off]) {
+ /* Update offset of match */
+ eng.sv[eng.off] = eng.eo[eng.off];
+
+ /* Update repetition counter */
+ if (++eng.re[eng.off] == i) {
+ /* Matched the required times */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.sv[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+
+ /* Update code */
+ k = eng.cod[3] | (eng.cod[4] << 8);
+ eng.cod += k;
+
+ /* Pop stack and go for next pattern */
+ --eng.off;
+ continue;
+ }
+
+ /* Skip repetition instruction */
+ eng.cod += 5;
+ }
+ else {
+ /* Do it here, so that the fail label does
+ * not need to do too expensive work for
+ * simple patterns. */
+ eng.so[bas] = eng.str - eng.bas;
+
+ /* Pop stack and restart */
+ --eng.off;
+ goto fail;
+ }
+ }
+ continue;
+
+ case Re_Min:
+ COMPLEX_REPETITION_SETUP_0();
+ if (eng.off == bas) {
+ /* First iteration */
+ COMPLEX_REPETITION_SETUP();
+ }
+ else {
+ if (eng.eo[eng.off] >= eng.so[eng.off] &&
+ eng.eo[eng.off] > eng.sv[eng.off]) {
+ /* Update offset of match */
+ eng.sv[eng.off] = eng.eo[eng.off];
+
+ /* Update repetition counter */
+ ++eng.re[eng.off];
+
+ /* Skip repetition instruction and try again */
+ eng.cod += 5;
+ }
+ else {
+ /* Match failed! */
+ if (eng.re[eng.off] < i) {
+ /* Do it here, so that the fail label does
+ * not need to do too expensive work for
+ * simple patterns. */
+ eng.so[bas] = eng.str - eng.bas;
+
+ /* Didn't match required number of times */
+ --eng.off;
+ goto fail;
+ }
+ else {
+ /* Matched minimum number of times */
+ eng.eo[bas] = eng.sv[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+ k = eng.cod[3] | (eng.cod[4] << 8);
+
+ /* Update code and pop stack */
+ eng.cod += k;
+ --eng.off;
+ }
+ }
+ }
+ continue;
+
+ case Re_Max:
+ COMPLEX_REPETITION_SETUP_0();
+ if (eng.off == bas) {
+ /* First iteration */
+ COMPLEX_REPETITION_SETUP();
+ }
+ else {
+ if (eng.eo[eng.off] >= eng.so[eng.off] &&
+ eng.eo[eng.off] > eng.sv[eng.off]) {
+ /* Update offset of match */
+ eng.sv[eng.off] = eng.eo[eng.off];
+
+ /* Update repetition counter */
+ if (++eng.re[eng.off] == i) {
+ /* Matched the maximum times */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.sv[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+
+ k = eng.cod[3] | (eng.cod[4] << 8);
+
+ /* Update code and pop stack */
+ eng.cod += k;
+ --eng.off;
+ continue;
+ }
+
+ /* Skip repetition instruction and try again */
+ eng.cod += 5;
+ }
+ else {
+ /* No matches, but zero matches are ok */
+ k = eng.cod[3] | (eng.cod[4] << 8);
+ if (eng.sv[eng.off] >= eng.so[eng.off]) {
+ /* Something matched earlier, update */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.sv[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+ }
+ else {
+ /* Empty match */
+ if (eng.eo[bas] < eng.so[bas])
+ eng.eo[bas] = eng.so[bas];
+
+ /* Try next pattern at correct offset */
+ eng.str = eng.bas + eng.eo[bas];
+ }
+
+ /* Pop stack and update code */
+ --eng.off;
+ eng.cod += k;
+ }
+ }
+ continue;
+
+ case Re_MinMax:
+ bas = eng.cod[3];
+ if (eng.off == bas) {
+ /* First iteration */
+ COMPLEX_REPETITION_SETUP();
+ }
+ else {
+ if (eng.eo[eng.off] >= eng.so[eng.off] &&
+ eng.eo[eng.off] > eng.sv[eng.off]) {
+ /* Update offset of match */
+ eng.sv[eng.off] = eng.eo[eng.off];
+
+ /* Update repetition counter */
+ if (++eng.re[eng.off] == eng.cod[2]) {
+ /* Matched the maximum times */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.sv[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+ k = eng.cod[4] | (eng.cod[5] << 8);
+
+ /* Update code and pop stack */
+ eng.cod += k;
+ --eng.off;
+ continue;
+ }
+
+ /* Skip repetition instruction and try again */
+ eng.cod += 6;
+ }
+ else {
+ /* Match failed! */
+ if (eng.re[eng.off] < eng.cod[1]) {
+ /* Do it here, so that the fail label does
+ * not need to do too expensive work for
+ * simple patterns. */
+ eng.so[bas] = eng.str - eng.bas;
+
+ /* Didn't match required number of times */
+ --eng.off;
+ goto fail;
+ }
+ else {
+ /* Matched minimum number of times */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.sv[eng.off];
+ eng.str = eng.bas + eng.eo[bas];
+ k = eng.cod[4] | (eng.cod[5] << 8);
+
+ /* Update code and pop stack */
+ eng.cod += k;
+ --eng.off;
+ }
+ }
+ }
+ continue;
+
+
+ /****************************************************
+ * Special repetition handling *
+ ****************************************************/
+ case Re_AnyAnyTimes:
+ /* code(1) + bas(1) + gbas(1) + jump(2) */
+ bas = eng.cod[1];
+ if (eng.off == bas) {
+ /* First iteration */
+ if (++eng.off >= MAX_DEPTH)
+ return (RE_ASSERT);
+
+ /* Return here for recovery if match fail */
+ eng.rcod[eng.off] = eng.cod;
+
+ /* If fail, test the next pattern at the same point */
+ eng.rstr[eng.off] = eng.str;
+
+ /* Setup match offsets */
+ eng.so[eng.off] = eng.str - eng.bas;
+ eng.eo[eng.off] = eng.so[eng.off] - 1;
+
+ if (newline)
+ /* Use the repetition counter to store start of
+ * skipped string, to later check if skipping a
+ * newline. */
+ eng.re[eng.off] = eng.so[eng.off];
+
+ /* Save start of possible previous matches */
+ eng.ss[eng.off] = eng.so[bas];
+
+ /* Skip repetition instruction */
+ eng.cod += 5;
+ }
+ else {
+ /* -1 as an unsigned char */
+ if (eng.cod[2] != 0xff)
+ eng.goff = eng.cod[2];
+ else
+ eng.goff = -1;
+
+ if (newline) {
+ ptr = eng.bas + eng.re[eng.off];
+ str = eng.bas + eng.so[eng.off];
+ for (; ptr < str; ptr++)
+ if (*ptr == '\n') {
+ eng.cod = eng.rcod[0];
+ eng.so[0] = ptr - eng.bas + 1;
+ eng.eo[0] = eng.so[0] - 1;
+ eng.rstr[0] = eng.str = ptr + 1;
+ eng.off = 0;
+ goto reset;
+ }
+ /* If looping, don't do too many noops */
+ eng.re[eng.off] = ptr - eng.bas;
+ }
+
+ if (eng.eo[eng.off] >= eng.so[eng.off]) {
+ /* Note that this is only true if all possibly
+ * nested special repetitions also matched. */
+
+ if (eng.goff >= 0) {
+ if (eng.cod[5] == Re_Update)
+ eng.gso[eng.goff] = eng.eo[bas] +
+ (eng.so[bas] > eng.eo[bas]);
+ else if (eng.geo[eng.goff] < eng.so[eng.off])
+ eng.geo[eng.goff] = eng.so[eng.off];
+ }
+
+ /* Jump relative offset */
+ len = eng.cod[3] | (eng.cod[4] << 8);
+
+ /* Restore offset from where started trying */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.eo[eng.off];
+
+ /* Pop stack and skip code */
+ --eng.off;
+ eng.cod += len;
+ }
+ else {
+ /* Only give up if the entire string was scanned */
+ if (eng.str < eng.end) {
+ /* Update restart point for next pattern */
+ eng.str = ++eng.rstr[eng.off];
+
+ /* Reset start of nested match */
+ eng.so[eng.off] = eng.str - eng.bas;
+
+ /* Skip repetition instruction */
+ eng.cod += 5;
+ }
+ else {
+ /* Entire string scanned and failed */
+
+ /* Jump relative offset */
+ len = eng.cod[3] | (eng.cod[4] << 8);
+
+ /* Restore offset from where started trying */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.ss[eng.off] - 1;
+
+ /* Pop stack and skip code */
+ --eng.off;
+ eng.cod += len;
+ }
+ }
+ }
+ continue;
+
+ /* This is significantly different than matching <re>.*<re>
+ * because it may need to restart several times since it is
+ * possible to find too many false positives, for example:
+ * a.*b => once one "a" is found, scan all
+ * the remaining string searching for a "b"
+ * a.?b => the string may have too many "a"s, but the
+ * first occurrences of "a" may not be followed
+ * by any-character and a "b" or a single "b".
+ */
+ case Re_AnyMaybe:
+ bas = eng.cod[1];
+ if (eng.off == bas) {
+ /* First iteration */
+ if (++eng.off >= MAX_DEPTH)
+ return (RE_ASSERT);
+
+ /* Return here for recovery if match fail */
+ eng.rcod[eng.off] = eng.cod;
+
+ /* First try without eating a byte */
+ eng.rstr[eng.off] = eng.str;
+
+ /* Remember this is the first try if match fail */
+ eng.re[eng.off] = 0;
+
+ /* Setup match offsets */
+ eng.so[eng.off] = eng.str - eng.bas;
+ eng.eo[eng.off] = eng.so[eng.off] - 1;
+
+ /* Save start of possible previous matches */
+ eng.ss[eng.off] = eng.so[bas];
+
+ /* Skip repetition instruction */
+ eng.cod += 5;
+ }
+ else {
+ /* -1 as an unsigned char */
+ if (eng.cod[2] != 0xff)
+ eng.goff = eng.cod[2];
+ else
+ eng.goff = -1;
+
+ if (eng.eo[eng.off] >= eng.so[eng.off]) {
+ /* Something matched */
+
+ if (eng.goff >= 0) {
+ if (eng.cod[5] == Re_Update)
+ eng.gso[eng.goff] = eng.eo[bas] +
+ (eng.so[bas] > eng.eo[bas]);
+ else if (eng.geo[eng.goff] < eng.so[eng.off])
+ eng.geo[eng.goff] = eng.so[eng.off];
+ }
+
+ /* Jump relative offset */
+ len = eng.cod[3] | (eng.cod[4] << 8);
+
+ /* Update offset of match */
+ eng.eo[bas] = eng.eo[eng.off];
+
+ /* Pop stack and skip code */
+ --eng.off;
+ eng.cod += len;
+ }
+ else if (eng.re[eng.off] == 0 &&
+ (!newline || eng.rstr[eng.off][1] != '\n')) {
+ /* Try this time skiping a byte */
+ ++eng.re[eng.off];
+
+ /* Reset string, skip code and go try one time more */
+ eng.str = ++eng.rstr[eng.off];
+ eng.cod += 5;
+ }
+ else {
+ /* Failed to match */
+
+ /* Update offsets */
+ eng.eo[bas] = eng.ss[eng.off];
+ eng.so[bas] = eng.eo[bas] + 1;
+
+ eng.str = eng.rstr[eng.off] + (eng.re[eng.off] == 0);
+
+ /* Pop stack and return to toplevel code */
+ --eng.off;
+ if (eng.str >= eng.end)
+ goto wont;
+ eng.cod = eng.rcod[bas];
+ }
+ }
+ continue;
+
+ /* .+ almost identical to .* but requires eating at least one byte */
+ case Re_AnyAtLeast:
+ bas = eng.cod[1];
+ if (eng.off == bas) {
+ /* First iteration */
+ if (++eng.off >= MAX_DEPTH)
+ return (RE_ASSERT);
+
+ /* Return here for recovery if match fail */
+ eng.rcod[eng.off] = eng.cod;
+
+ /* Skip one byte for the restart string */
+ if (newline && eng.str[0] == '\n') {
+ /* Cannot skip newline */
+ eng.cod = eng.rcod[0];
+ eng.rstr[0] = ++eng.str;
+ eng.so[0] = eng.str - eng.bas;
+ eng.eo[0] = eng.so[0] - 1;
+ eng.off = 0;
+ goto reset;
+ }
+ eng.rstr[eng.off] = ++eng.str;
+
+ /* Setup match offsets */
+ eng.so[eng.off] = eng.str - eng.bas;
+ eng.eo[eng.off] = eng.so[eng.off] - 1;
+
+ if (newline)
+ /* Use the repetition counter to store start of
+ * skipped string, to later check if skipping a
+ * newline. */
+ eng.re[eng.off] = eng.so[eng.off];
+
+ /* Save start of possible previous matches */
+ eng.ss[eng.off] = eng.so[bas];
+
+ /* Skip repetition instruction */
+ eng.cod += 5;
+ }
+ else {
+ /* -1 as an unsigned char */
+ if (eng.cod[2] != 0xff)
+ eng.goff = eng.cod[2];
+ else
+ eng.goff = -1;
+
+ if (newline) {
+ ptr = eng.bas + eng.re[eng.off];
+ str = eng.bas + eng.so[eng.off];
+ for (; ptr < str; ptr++)
+ if (*ptr == '\n') {
+ eng.cod = eng.rcod[0];
+ eng.so[0] = ptr - eng.bas + 1;
+ eng.eo[0] = eng.so[0] - 1;
+ eng.rstr[0] = eng.str = ptr + 1;
+ eng.off = 0;
+ goto reset;
+ }
+ /* If looping, don't do too many noops */
+ eng.re[eng.off] = ptr - eng.bas;
+ }
+
+ if (eng.eo[eng.off] >= eng.so[eng.off]) {
+ /* Note that this is only true if all possibly
+ * nested special repetitions also matched. */
+
+ if (eng.goff >= 0) {
+ if (eng.cod[5] == Re_Update)
+ eng.gso[eng.goff] = eng.eo[bas] +
+ (eng.so[bas] > eng.eo[bas]);
+ else if (eng.geo[eng.goff] < eng.so[eng.off])
+ eng.geo[eng.goff] = eng.so[eng.off];
+ }
+
+ /* Jump relative offset */
+ len = eng.cod[3] | (eng.cod[4] << 8);
+
+ /* Restore offset from where started trying */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.eo[eng.off];
+
+ /* Pop stack and skip code */
+ --eng.off;
+ eng.cod += len;
+ }
+ else {
+ /* Only give up if the entire string was scanned */
+ if (eng.str < eng.end) {
+ /* Update restart point for next pattern */
+ eng.str = ++eng.rstr[eng.off];
+
+ /* Reset start of nested match */
+ eng.so[eng.off] = eng.str - eng.bas;
+
+ /* Skip repetition instruction */
+ eng.cod += 5;
+ }
+ else {
+ /* Entire string scanned and failed */
+
+ /* Jump relative offset */
+ len = eng.cod[3] | (eng.cod[4] << 8);
+
+ /* Restore offset from where started trying */
+ eng.so[bas] = eng.ss[eng.off];
+ eng.eo[bas] = eng.ss[eng.off] - 1;
+
+ /* Pop stack and skip code */
+ --eng.off;
+ eng.cod += len;
+ }
+ }
+ }
+ continue;
+
+
+ /****************************************************
+ * Repetition matched! *
+ ****************************************************/
+ case Re_RepJump:
+ /* eng.cod[1] is toplevel offset of repetition */
+ if (eng.off > eng.cod[1])
+ /* If still needs to try matches */
+ eng.cod -= eng.cod[2];
+ else
+ eng.cod += 3; /* + RepJump + bas + len-size */
+ continue;
+
+ case Re_RepLongJump:
+ /* eng.cod[1] is toplevel offset of repetition */
+ if (eng.off > eng.cod[1])
+ /* If still needs to try matches */
+ eng.cod -= eng.cod[2] | (eng.cod[3] << 8);
+ else
+ eng.cod += 4; /* + RepLongJump + bas + len-size */
+ continue;
+
+ /****************************************************
+ * Finished *
+ ****************************************************/
+ case Re_DoneIf:
+ if (eng.eo[eng.off] >= eng.so[eng.off]) {
+ eng.so[0] = eng.ss[eng.off];
+ eng.eo[0] = eng.eo[eng.off];
+ goto done;
+ }
+ ++eng.cod;
+ continue;
+ case Re_MaybeDone:
+ if (eng.eo[eng.off] >= eng.so[eng.off]) {
+ eng.so[0] = eng.ss[eng.off];
+ eng.eo[0] = eng.eo[eng.off];
+ goto done;
+ }
+ ++eng.cod;
+ continue;
+ case Re_Done:
+ goto done;
+
+ default:
+ /* Fatal internal error */
+ return (RE_ASSERT);
+ }
+
+
+wont:
+ /* Surely won't match */
+ if (eng.off == 0) {
+ eng.eo[0] = eng.so[0] - 1;
+ break;
+ }
+
+
+fail:
+ if (eng.off == 0) {
+ /* If the entire string scanned */
+ if (++eng.str > eng.end) {
+ eng.eo[0] = eng.so[0] - 1;
+ break;
+ }
+ eng.goff = -1;
+ /* Update start of possible match after restart */
+ if (eng.eo[0] >= eng.so[0]) {
+ /* If first fail */
+ eng.str = eng.rstr[0];
+ ++eng.rstr[0];
+ eng.so[0] = eng.str - eng.bas;
+ eng.eo[0] = eng.so[eng.off] - 1;
+ }
+ else
+ /* Just trying at next byte */
+ ++eng.so[0];
+ }
+ else
+ /* Remember this match failed */
+ eng.eo[eng.off] = eng.so[eng.off] - 1;
+
+ /* Restart code */
+ eng.cod = eng.rcod[eng.off];
+ continue;
+
+
+match:
+ /* If first match */
+ if (eng.eo[eng.off] < eng.so[eng.off]) {
+ if (eng.off == 0)
+ eng.rstr[0] = eng.str + 1;
+ eng.so[eng.off] = eng.eo[eng.off] = eng.str - eng.bas;
+ }
+ eng.eo[eng.off] += si;
+ eng.cod += ci;
+ eng.str += si;
+ ci = si = 1;
+ continue;
+
+done:
+ break;
+ }
+
+ if (nmatch) {
+ if (flags & RE_STARTEND)
+ len = pmat[0].rm_so;
+ else
+ len = 0;
+ if (!nosub) {
+ if (preg->cod[1] != 0xff)
+ eng.goff = preg->cod[1];
+ pmat[0].rm_so = eng.so[0];
+ pmat[0].rm_eo = eng.eo[0];
+ for (i = 1; i < nmatch; i++) {
+ if (i - 1 <= eng.goff) {
+ pmat[i].rm_so = eng.gso[i - 1];
+ pmat[i].rm_eo = eng.geo[i - 1];
+ }
+ else {
+ pmat[i].rm_so = 0;
+ pmat[i].rm_eo = -1;
+ }
+ }
+ if (len) {
+ /* Update offsets, since the match was done in a substring */
+ j = eng.goff + 2 > nmatch ? nmatch : eng.goff + 2;
+ for (i = 0; i < j; i++) {
+ pmat[i].rm_so += len;
+ pmat[i].rm_eo += len;
+ }
+ }
+ }
+ else {
+ /* Already know these values, allow compiling the regex with
+ * RE_NOSUB to use parenthesis only for grouping, but avoiding
+ * the runtime overhead of keeping track of the subexpression
+ * offsets. */
+ pmat[0].rm_so = eng.so[0] + len;
+ pmat[0].rm_eo = eng.eo[0] + len;
+ }
+ }
+
+ return (eng.so[0] <= eng.eo[0] ? 0 : RE_NOMATCH);
+}
+
+int
+reerror(int ecode, const re_cod *preg, char *ebuffer, int ebuffer_size)
+{
+ static char *errors[] = {
+ "No error",
+ "Failed to match", /* NOMATCH */
+
+ /* Errors not generated */
+ "Invalid regular expression", /* BADPAT */
+ "Invalid collating element", /* ECOLLATE */
+ "Invalid character class", /* ECTYPE */
+
+ "`\' applied to unescapable character", /* EESCAPE */
+ "Invalid backreference number", /* ESUBREG */
+ "Brackets `[ ]' not balanced", /* EBRACK */
+ "Parentheses `( )' not balanced", /* EPAREN */
+ "Braces `{ }' not balanced", /* EBRACE */
+ "Invalid repetition count(s) in `{ }'", /* BADBR */
+ "Invalid character range in `[ ]'", /* ERANGE */
+ "Out of memory", /* ESPACE */
+ "`?', `*', or `+' operand invalid", /* BADRPT */
+ "Empty (sub)expression", /* EMPTY */
+ "Assertion error - you found a bug", /* ASSERT */
+ "Invalid argument" /* INVARG */
+ };
+ char *str;
+
+ if (ecode >= 0 && ecode < sizeof(errors) / sizeof(errors[0]))
+ str = errors[ecode];
+ else
+ str = "Unknown error";
+
+ return (snprintf(ebuffer, ebuffer_size, "%s", str));
+}
+
+void
+refree(re_cod *cod)
+{
+ free(cod->cod);
+ cod->cod = NULL;
+}
+
+static void
+reinit(void)
+{
+ int i;
+ static int first = 1;
+
+ if (!first)
+ return;
+ first = 0;
+
+ re__alnum['_'] = 1;
+
+ for (i = '0'; i <= '7'; i++)
+ re__alnum[i] = re__odigit[i] = re__ddigit[i] = re__xdigit[i] = 1;
+
+ for (; i <= '9'; i++)
+ re__alnum[i] = re__ddigit[i] = re__xdigit[i] = 1;
+
+ for (i = 'a'; i <= 'f'; i++)
+ re__alnum[i] = re__xdigit[i] = 1;
+ for (; i <= 'z'; i++)
+ re__alnum[i] = 1;
+
+ for (i = 'A'; i <= 'F'; i++)
+ re__alnum[i] = re__xdigit[i] = 1;
+ for (; i <= 'Z'; i++)
+ re__alnum[i] = 1;
+
+ for (i = 1; i < 32; i++)
+ re__control[i] = 1;
+ re__control[127] = 1;
+ /* Don't show tabs as control characters */
+ re__control['\t'] = 0;
+}
+
+static int
+rec_check(re_inf *inf, int count)
+{
+ if (inf->len + count >= inf->spc) {
+ int spc;
+ unsigned char *cod;
+
+ if ((spc = (count % 64)) != 0)
+ spc = 64 - spc;
+ spc += count + inf->spc;
+ if ((cod = realloc(inf->cod, spc)) == NULL)
+ return (inf->ecode = RE_ESPACE);
+ inf->cod = cod;
+ inf->spc = spc;
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_code(re_inf *inf, ReCode code)
+{
+ if (rec_check(inf, 1) == 0)
+ inf->cod[inf->len++] = code;
+
+ return (inf->ecode);
+}
+
+static int
+rec_byte(re_inf *inf, int value)
+{
+ if (rec_check(inf, 1) == 0)
+ inf->cod[inf->len++] = value;
+
+ return (inf->ecode);
+}
+
+static int
+rec_code_byte(re_inf *inf, ReCode code, int value)
+{
+ if (rec_check(inf, 2) == 0) {
+ inf->cod[inf->len++] = code;
+ inf->cod[inf->len++] = value;
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_length(re_inf *inf, int length)
+{
+ int lo, hi, two;
+
+ if (length >= 16384)
+ return (inf->ecode = RE_ESPACE);
+
+ lo = length & 0xff;
+ hi = length & 0xff00;
+ two = ((length > 0x7f) != 0) + 1;
+ if (two == 2) {
+ hi <<= 1;
+ hi |= (lo & 0x80) != 0;
+ lo |= 0x80;
+ }
+
+ if (rec_check(inf, two) == 0) {
+ inf->cod[inf->len++] = lo;
+ if (two == 2)
+ inf->cod[inf->len++] = hi >> 8;
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_byte_byte(re_inf *inf, int value0, int value1)
+{
+ if (rec_check(inf, 2) == 0) {
+ inf->cod[inf->len++] = value0;
+ inf->cod[inf->len++] = value1;
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_code_byte_byte(re_inf *inf, ReCode code, int value0, int value1)
+{
+ if (rec_check(inf, 3) == 0) {
+ inf->cod[inf->len++] = code;
+ inf->cod[inf->len++] = value0;
+ inf->cod[inf->len++] = value1;
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_build_alt(re_inf *inf, rec_alt *alt)
+{
+ int offset, value, bas = inf->bas + 1;
+
+ if (alt) {
+ if (alt->next) {
+ if (rec_inc_spc(inf))
+ return (inf->ecode);
+
+ /* A real a list of alternatives */
+ rec_code(inf, Re_Alt);
+
+ offset = inf->len; /* Remember current offset */
+ rec_byte_byte(inf, 0, 0); /* Reserve two bytes for retry address */
+ while (alt && inf->ecode == 0) {
+ if (rec_build_pat(inf, alt->pat))
+ break;
+ alt = alt->next;
+ if (alt && inf->ecode == 0) {
+ /* Handle (hyper)complex repetitions */
+ if (inf->bas != bas) {
+ /* Duplicate patterns up to end of expression */
+ rec_build_pat(inf, inf->apat);
+ /* Restore engine state for next alternative(s) */
+ rec_alt_spc(inf, bas - 1);
+ }
+
+ /* If the jump would be so long */
+ if ((value = inf->len - offset) >= 16384) {
+ inf->ecode = RE_ESPACE;
+ break;
+ }
+ inf->cod[offset] = value & 0xff;
+ inf->cod[offset + 1] = (value & 0xff00) >> 8;
+
+ rec_code(inf, Re_AltNext);
+ offset = inf->len;
+ rec_byte_byte(inf, 0, 0);
+ }
+ }
+ if (inf->ecode == 0) {
+ /* Handle (hyper)complex repetitions */
+ if (inf->bas != bas) {
+ /* Duplicate patterns up to end of expression */
+ rec_build_pat(inf, inf->apat);
+ /* Restore engine state for next alternative(s) */
+ rec_alt_spc(inf, bas - 1);
+ }
+
+ /* If the jump would be so long */
+ if ((value = inf->len - offset) >= 16384)
+ return (inf->ecode = RE_ESPACE);
+ inf->cod[offset] = value & 0xff;
+ inf->cod[offset + 1] = (value & 0xff00) >> 8;
+ /* Last jump is here */
+ rec_code(inf, Re_AltDone);
+ }
+ rec_dec_spc(inf);
+ }
+ else
+ /* Single alternative */
+ rec_build_pat(inf, alt->pat);
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_build_pat(re_inf *inf, rec_pat *pat)
+{
+ rec_pat *apat;
+ int length, offset = 0, distance, jump = 0, bas = 0;
+
+ while (pat && inf->ecode == 0) {
+ if (pat->rep) {
+ bas = inf->bas;
+ if (pat->type == Rep_Group && !inf->par && rec_code(inf, Re_Open))
+ return (inf->ecode);
+ if (rec_inc_spc(inf))
+ return (inf->ecode);
+ offset = inf->len;
+ if (rec_build_rep(inf, pat->rep))
+ break;
+ /* Reserve space to jump after repetition done */
+ jump = inf->len;
+ rec_byte_byte(inf, 0, 0);
+ }
+ switch (pat->type) {
+ case Rep_AnyAnyTimes:
+ case Rep_AnyMaybe:
+ case Rep_AnyAtLeast:
+ if (rec_add_spc(inf, pat->type == Rep_AnyMaybe))
+ return (inf->ecode);
+ if (rec_code(inf, (ReCode)pat->type) == 0 &&
+ rec_byte(inf, inf->bas - 1) == 0 &&
+ rec_byte(inf, inf->ref - 1) == 0)
+ rec_off_spc(inf);
+ break;
+ case Rep_Literal:
+ case Rep_LiteralNot:
+ case Rep_SearchLiteral:
+ rec_code_byte(inf, (ReCode)pat->type, pat->data.chr);
+ break;
+ case Rep_CaseLiteral:
+ case Rep_CaseLiteralNot:
+ case Rep_SearchCaseLiteral:
+ rec_code_byte_byte(inf, (ReCode)pat->type,
+ pat->data.cse.lower, pat->data.cse.upper);
+ break;
+ case Rep_Range:
+ case Rep_RangeNot:
+ if (rec_code(inf, (ReCode)pat->type) == 0)
+ rec_build_rng(inf, pat->data.rng);
+ break;
+ case Rep_String:
+ case Rep_SearchString:
+ case Rep_CaseString:
+ case Rep_SearchCaseString:
+ rec_code(inf, (ReCode)pat->type);
+ length = strlen((char*)pat->data.str);
+ if (rec_length(inf, length) == 0 && rec_check(inf, length) == 0) {
+ memcpy(inf->cod + inf->len, pat->data.str, length);
+ inf->len += length;
+ }
+ break;
+ case Rep_Any:
+ case Rep_AnyEatAnyTimes:
+ case Rep_AnyEatMaybe:
+ case Rep_AnyEatAtLeast:
+ case Rep_Odigit:
+ case Rep_OdigitNot:
+ case Rep_Digit:
+ case Rep_DigitNot:
+ case Rep_Xdigit:
+ case Rep_XdigitNot:
+ case Rep_Space:
+ case Rep_SpaceNot:
+ case Rep_Tab:
+ case Rep_Newline:
+ case Rep_Lower:
+ case Rep_Upper:
+ case Rep_Alnum:
+ case Rep_AlnumNot:
+ case Rep_Control:
+ case Rep_ControlNot:
+ case Rep_Bol:
+ case Rep_Eol:
+ case Rep_Bow:
+ case Rep_Eow:
+ rec_code(inf, (ReCode)pat->type);
+ break;
+ case Rep_Backref:
+ rec_code_byte(inf, Re_Backref, pat->data.chr);
+ break;
+ case Rep_Group:
+ if (pat->rep == NULL && !inf->par && rec_code(inf, Re_Open))
+ break;
+ apat = inf->apat;
+ inf->apat = pat->next;
+ rec_build_grp(inf, pat->data.grp);
+ inf->apat = apat;
+ break;
+ case Rep_StringList:
+ rec_build_stl(inf, pat->data.stl);
+ break;
+ }
+ if (pat->rep) {
+#if 0
+ if (rec_dec_spc(inf))
+ return (inf->ecode);
+#else
+ if (rec_rep_spc(inf, bas))
+ return (inf->ecode);
+#endif
+ distance = inf->len - offset;
+ if (distance > 255) {
+ if (rec_code(inf, Re_RepLongJump) ||
+ rec_byte(inf, inf->bas) ||
+ rec_byte(inf, distance & 0xff) ||
+ rec_byte(inf, (distance & 0xff00) >> 8))
+ break;
+ }
+ else if (rec_code(inf, Re_RepJump) ||
+ rec_byte(inf, inf->bas) ||
+ rec_byte(inf, distance))
+ break;
+ distance = inf->len - offset;
+ inf->cod[jump] = distance & 0xff;
+ inf->cod[jump + 1] = (distance & 0xff00) >> 8;
+ }
+ pat = pat->next;
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_build_rng(re_inf *inf, rec_rng *rng)
+{
+ if (rec_check(inf, sizeof(rng->range)) == 0) {
+ memcpy(inf->cod + inf->len, rng->range, sizeof(rng->range));
+ inf->len += sizeof(rng->range);
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_build_grp(re_inf *inf, rec_grp *grp)
+{
+ int par = inf->par;
+
+ if (!(inf->flags & RE_NOSUB)) {
+ ++inf->par;
+ if (par == 0)
+ ++inf->ref;
+ if (rec_build_alt(inf, grp->alt) == 0) {
+ if (par == 0) {
+ if (grp->comp)
+ rec_code_byte(inf, Re_Update, inf->ref - 1);
+ else
+ rec_code(inf, Re_Close);
+ }
+ }
+ --inf->par;
+ }
+ else
+ rec_build_alt(inf, grp->alt);
+
+ return (inf->ecode);
+}
+
+static int
+rec_build_stl(re_inf *inf, rec_stl *stl)
+{
+ int i, len, rlen;
+ ReCode code;
+
+ /* Calculate jump distance information */
+ rlen = stl->tlen + stl->nstrs + 4;
+ /* + code + nstrs + place-offset + data-length */
+
+ if (stl->nstrs >= LARGE_STL_COUNT) {
+ rlen += 511; /* Don't write number of strings */
+ code = stl->type == Rep_StringList ?
+ Re_LargeStringList : Re_LargeCaseStringList;
+ }
+ else
+ code = (ReCode)stl->type;
+
+ if (rlen >= 16386)
+ return (inf->ecode = RE_ESPACE);
+ if (rec_check(inf, rlen) ||
+ rec_code(inf, code))
+ return (inf->ecode);
+
+ /* Space is allocated, just write the data */
+ if (stl->nstrs < LARGE_STL_COUNT)
+ inf->cod[inf->len++] = stl->nstrs;
+
+ inf->cod[inf->len++] = rlen & 0xff;
+ inf->cod[inf->len++] = (rlen & 0xff00) >> 8;
+
+ if (stl->nstrs < LARGE_STL_COUNT) {
+ for (i = 0; i < stl->nstrs; i++)
+ inf->cod[inf->len++] = stl->lens[i];
+ for (i = 0; i < stl->nstrs; i++) {
+ len = stl->lens[i];
+ if (len > 2) {
+ memcpy(inf->cod + inf->len, stl->strs[i], len);
+ inf->len += len;
+ }
+ else {
+ if (len == 1)
+ inf->cod[inf->len++] = (long)stl->strs[i];
+ else {
+ inf->cod[inf->len++] = (long)stl->strs[i] & 0xff;
+ inf->cod[inf->len++] = ((long)stl->strs[i] & 0xff00) >> 8;
+ }
+ }
+ }
+ }
+ else {
+ /* The string length goes before the string itself */
+ int j, chl, chu;
+
+ /* Fill everything with an invalid jump address */
+ memset(inf->cod + inf->len, 0xff, 512);
+ for (i = len = 0, j = -1; i < stl->nstrs; i++) {
+ chl = stl->lens[i] > 2 ? stl->strs[i][0] : (long)stl->strs[i] & 0xff;
+ if (chl != j) {
+ inf->cod[inf->len + (chl << 1)] = len & 0xff;
+ inf->cod[inf->len + (chl << 1) + 1] = (len & 0xff00) >> 8;
+ if (code == Re_LargeCaseStringList) {
+ chu = stl->lens[i] > 2 ?
+ stl->strs[i][1] : ((long)(stl->strs[i]) & 0xff00) >> 8;
+ inf->cod[inf->len + (chu << 1)] = len & 0xff;
+ inf->cod[inf->len + (chu << 1) + 1] = (len & 0xff00) >> 8;
+ }
+ j = chl;
+ }
+ len += stl->lens[i] + 1;
+ }
+ inf->len += 512;
+
+ for (i = 0; i < stl->nstrs; i++) {
+ len = stl->lens[i];
+ inf->cod[inf->len++] = len;
+ if (len > 2) {
+ memcpy(inf->cod + inf->len, stl->strs[i], len);
+ inf->len += len;
+ }
+ else {
+ if (len == 1)
+ inf->cod[inf->len++] = (long)stl->strs[i];
+ else {
+ inf->cod[inf->len++] = (long)stl->strs[i] & 0xff;
+ inf->cod[inf->len++] = ((long)stl->strs[i] & 0xff00) >> 8;
+ }
+ }
+ }
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_build_rep(re_inf *inf, rec_rep *rep)
+{
+ if (rep) {
+ switch (rep->type) {
+ case Rer_AnyTimes:
+ case Rer_AtLeast:
+ case Rer_Maybe:
+ rec_code(inf, (ReCode)rep->type);
+ break;
+ case Rer_Exact:
+ if (rec_code(inf, Re_Exact) == 0)
+ rec_byte(inf, rep->mine);
+ break;
+ case Rer_Min:
+ if (rec_code(inf, Re_Min) == 0)
+ rec_byte(inf, rep->mine);
+ break;
+ case Rer_Max:
+ if (rec_code(inf, Re_Max) == 0)
+ rec_byte(inf, rep->maxc);
+ break;
+ case Rer_MinMax:
+ if (rec_code(inf, Re_MinMax) == 0 &&
+ rec_byte(inf, rep->mine) == 0)
+ rec_byte(inf, rep->maxc);
+ break;
+ }
+ /* It is incremented in rec_build_pat */
+ rec_byte(inf, inf->bas - 1);
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_inc_spc(re_inf *inf)
+{
+ if (++inf->bas >= MAX_DEPTH)
+ return (inf->ecode = RE_ESPACE);
+
+ return (inf->ecode);
+}
+
+static int
+rec_dec_spc(re_inf *inf)
+{
+ if (--inf->bas < 0)
+ return (inf->ecode = RE_ASSERT);
+
+ return (inf->ecode);
+}
+
+static int
+rec_add_spc(re_inf *inf, int maybe)
+{
+ if (++inf->bas >= MAX_DEPTH)
+ return (inf->ecode = RE_ESPACE);
+ inf->sp[inf->bas] = maybe + 1;
+
+ return (inf->ecode);
+}
+
+/* Could be joined with rec_rep_spc, code almost identical */
+static int
+rec_alt_spc(re_inf *inf, int top)
+{
+ int distance, i, bas = inf->bas;
+
+ while ((inf->bas > top) && inf->sp[inf->bas]) {
+ /* Jump to this repetition for cleanup */
+ distance = inf->len - inf->sr[inf->bas];
+
+ /* This will generate a jump to a jump decision opcode */
+ inf->sj[inf->bas] = inf->len;
+
+ if (distance > 255) {
+ if (rec_code(inf, Re_RepLongJump) ||
+ rec_byte(inf, inf->bas - 1) ||
+ rec_byte(inf, distance & 0xff) ||
+ rec_byte(inf, (distance & 0xff00) >> 8))
+ break;
+ }
+ else if (rec_code(inf, Re_RepJump) ||
+ rec_byte(inf, inf->bas - 1) ||
+ rec_byte(inf, distance))
+ break;
+
+ /* Top of stack value before repetition, or end condition value */
+ --inf->bas;
+ }
+
+ i = inf->bas + 1;
+
+ if (inf->ecode == 0 && i <= bas && inf->sp[i]) {
+ /* Only the repetition at the bottom jump to code after testing
+ * all possibilities */
+ distance = inf->len - inf->sr[i];
+ inf->cod[inf->sr[i] + 3] = distance & 0xff;
+ inf->cod[inf->sr[i] + 4] = (distance & 0xff00) >> 8;
+
+ /* The bottom jump is here */
+ if (rec_code(inf, inf->sp[i] == 1 ? Re_DoneIf : Re_MaybeDone))
+ return (inf->ecode);
+
+ /* Generate jumps to the previous special repetition */
+ for (++i; i <= bas; i++) {
+ if (inf->sp[i]) {
+ distance = inf->sj[i] - inf->sr[i];
+ inf->cod[inf->sr[i] + 3] = distance & 0xff;
+ inf->cod[inf->sr[i] + 4] = (distance & 0xff00) >> 8;
+ }
+ }
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_rep_spc(re_inf *inf, int top)
+{
+ int distance, i, bas = inf->bas;
+
+ while (inf->bas > top) {
+ if (inf->sp[inf->bas]) {
+ /* Jump to this repetition for cleanup */
+ distance = inf->len - inf->sr[inf->bas];
+
+ /* This will generate a jump to a jump decision opcode */
+ inf->sj[inf->bas] = inf->len;
+
+ if (distance > 255) {
+ if (rec_code(inf, Re_RepLongJump) ||
+ rec_byte(inf, inf->bas - 1) ||
+ rec_byte(inf, distance & 0xff) ||
+ rec_byte(inf, (distance & 0xff00) >> 8))
+ break;
+ }
+ else if (rec_code(inf, Re_RepJump) ||
+ rec_byte(inf, inf->bas - 1) ||
+ rec_byte(inf, distance))
+ break;
+ }
+
+ /* Top of stack value before repetition, or end condition value */
+ --inf->bas;
+ }
+
+ /* Find first special repetition offset. XXX This should be a noop */
+ for (i = 0; i < bas; i++)
+ if (inf->sp[i])
+ break;
+
+ if (inf->ecode == 0 && i <= bas && inf->sp[i]) {
+ /* Only the repetition at the bottom jump to code after testing
+ * all possibilities */
+ distance = inf->len - inf->sr[i];
+ inf->cod[inf->sr[i] + 3] = distance & 0xff;
+ inf->cod[inf->sr[i] + 4] = (distance & 0xff00) >> 8;
+
+ /* Generate jumps to the previous special repetition */
+ for (++i; i <= bas; i++) {
+ if (inf->sp[i]) {
+ distance = inf->sj[i] - inf->sr[i];
+ inf->cod[inf->sr[i] + 3] = distance & 0xff;
+ inf->cod[inf->sr[i] + 4] = (distance & 0xff00) >> 8;
+ }
+ }
+ }
+
+ return (inf->ecode);
+}
+
+static int
+rec_off_spc(re_inf *inf)
+{
+ /* The jump address before the three bytes instruction */
+ inf->sr[inf->bas] = inf->len - 3;
+ /* Don't know yet where to go after done with the special
+ * repetition, just reserve two bytes for the jump address. */
+ return (rec_byte_byte(inf, 0, 0));
+}
+
+#ifdef DEBUG
+static void
+redump(re_cod *code)
+{
+ int i, j, k;
+ unsigned char *cod = code->cod, *stl;
+
+ if (cod[0] & RE_NOSUB)
+ printf("Nosub\n");
+ if (cod[0] & RE_NEWLINE)
+ printf("Newline\n");
+ ++cod;
+ if (cod[0] != 0xff)
+ printf("%d backrefs\n", cod[0] + 1);
+ ++cod;
+ for (;;) {
+ switch (*cod++) {
+ case Re_Open:
+ printf("Open");
+ break;
+ case Re_Close:
+ printf("Close");
+ break;
+ case Re_Update:
+ printf("Update (%d)", (int)*cod++);
+ break;
+ case Re_Alt:
+ printf("Alt");
+ i = cod[0] | cod[1];
+ cod += 2;
+ printf(" %d", i);
+ break;
+ case Re_AltNext:
+ printf("Alt-next");
+ i = cod[0] | cod[1];
+ cod += 2;
+ printf(" %d", i);
+ break;
+ case Re_AltDone:
+ printf("Alt-done");
+ break;
+ case Re_AnyTimes:
+ printf("-> Anytimes %d", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_AnyEatAnyTimes:
+ printf("Any-eat-anytimes");
+ break;
+ case Re_AnyAnyTimes:
+ printf("-> Any-anytimes %d", (int)*cod++);
+ printf(" (%d)", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_AnyEatMaybe:
+ printf("Any-eat-maybe");
+ break;
+ case Re_AnyMaybe:
+ printf("-> Any-maybe %d", (int)*cod++);
+ printf(" (%d)", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_AnyAtLeast:
+ printf("-> Any-atleast %d", (int)*cod++);
+ printf(" (%d)", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_AnyEatAtLeast:
+ printf("Any-eat-atleast");
+ break;
+ case Re_Maybe:
+ printf("-> Maybe %d", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_AtLeast:
+ printf("-> Atleast %d", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_Exact:
+ printf("-> Exact ");
+ i = *cod++;
+ printf("%d", i);
+ printf(" %d", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_Min:
+ printf("-> Min ");
+ i = *cod++;
+ printf("%d", i);
+ printf(" %d", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_Max:
+ printf("-> Max ");
+ i = *cod++;
+ printf("%d", i);
+ printf(" %d", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_MinMax:
+ printf("-> Min-max ");
+ i = *cod++;
+ printf("%d ", i);
+ i = *cod++;
+ printf("%d", i);
+ printf(" %d", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ cod += 2;
+ printf(" /%d", i);
+ break;
+ case Re_RepJump:
+ printf("<- Rep-jump %d ", (int)*cod++);
+ i = *cod++;
+ printf("%d", i);
+ break;
+ case Re_RepLongJump:
+ printf("<- Rep-long-jump %d ", (int)*cod++);
+ i = cod[0] | (cod[1] << 8);
+ printf("%d", i);
+ break;
+ case Re_Any:
+ printf("Any");
+ break;
+ case Re_Odigit:
+ printf("Odigit");
+ break;
+ case Re_OdigitNot:
+ printf("Odigit-not");
+ break;
+ case Re_Digit:
+ printf("Digit");
+ break;
+ case Re_DigitNot:
+ printf("Digit-not");
+ break;
+ case Re_Xdigit:
+ printf("Xdigit");
+ break;
+ case Re_XdigitNot:
+ printf("Xdigit-not");
+ break;
+ case Re_Space:
+ printf("Space");
+ break;
+ case Re_SpaceNot:
+ printf("Space-not");
+ break;
+ case Re_Tab:
+ printf("Tab");
+ break;
+ case Re_Newline:
+ printf("Newline");
+ break;
+ case Re_Lower:
+ printf("Lower");
+ break;
+ case Re_Upper:
+ printf("Upper");
+ break;
+ case Re_Alnum:
+ printf("Alnum");
+ break;
+ case Re_AlnumNot:
+ printf("Alnum-not");
+ break;
+ case Re_Control:
+ printf("Control");
+ break;
+ case Re_ControlNot:
+ printf("Control-not");
+ break;
+ case Re_Bol:
+ printf("Bol");
+ break;
+ case Re_Eol:
+ printf("Eol");
+ break;
+ case Re_Bow:
+ printf("Bow");
+ break;
+ case Re_Eow:
+ printf("Eow");
+ break;
+ case Re_Range:
+ printf("Range ");
+ goto range;
+ case Re_RangeNot:
+ printf("Range-not ");
+range:
+ for (i = 0; i < 256; i += 32) {
+ for (j = k = 0; j < 32; j++)
+ k |= (*cod++ & 1) << (31 - j);
+ printf("%x ", k);
+ }
+ break;
+ case Re_Literal:
+ printf("Literal %c", *cod++);
+ break;
+ case Re_LiteralNot:
+ printf("Literal-not %c", *cod++);
+ break;
+ case Re_SearchLiteral:
+ printf("Search-literal %c", *cod++);
+ break;
+ case Re_CaseLiteral:
+ printf("Case-literal %c", *cod++);
+ putchar(*cod++);
+ break;
+ case Re_CaseLiteralNot:
+ printf("Case-literal-not %c", *cod++);
+ putchar(*cod++);
+ break;
+ case Re_SearchCaseLiteral:
+ printf("Search-case-literal %c", *cod++);
+ putchar(*cod++);
+ break;
+ case Re_String:
+ printf("String ");
+ goto string;
+ case Re_SearchString:
+ printf("Search-string ");
+ goto string;
+ case Re_CaseString:
+ printf("Case-string ");
+ goto string;
+ case Re_SearchCaseString:
+ printf("Search-case-string ");
+string:
+ i = *cod++;
+ if (i & 0x80)
+ i = (i & 0x7f) | (*cod++ << 7);
+ for (j = 0; j < i; j++)
+ putchar(*cod++);
+ break;
+ case Re_StringList:
+ printf("String-list");
+ goto string_list;
+ case Re_CaseStringList:
+ printf("Case-string-list");
+string_list:
+ j = *cod++;
+ cod += 2;
+ stl = cod + j;
+ for (i = 0; i < j; i++) {
+ k = *cod++;
+ putchar(i ? ',' : ' ');
+ fwrite(stl, k, 1, stdout);
+ stl += k;
+ }
+ cod = stl;
+ break;
+ case Re_LargeStringList:
+ printf("Large-string-list");
+large_string_list:
+ i = cod[0] | (cod[1] << 8);
+ stl = cod + i - 1;
+ for (i = 0, cod += 514; cod < stl; i++) {
+ k = *cod++;
+ putchar(i ? ',' : ' ');
+ fwrite(cod, k, 1, stdout);
+ cod += k;
+ }
+ cod = stl;
+ break;
+ case Re_LargeCaseStringList:
+ printf("Large-case-string-list");
+ goto large_string_list;
+ case Re_Backref:
+ printf("Backref %d", (int)*cod++);
+ break;
+ case Re_DoneIf:
+ printf("Done-if");
+ break;
+ case Re_MaybeDone:
+ printf("Maybe-done");
+ break;
+ case Re_Done:
+ printf("Done\n");
+ return;
+ }
+ putchar('\n');
+ }
+}
+#endif
diff --git a/lisp/re/re.h b/lisp/re/re.h
new file mode 100644
index 0000000..332366e
--- /dev/null
+++ b/lisp/re/re.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/re/re.h,v 1.2 2002/09/23 01:25:41 paulo Exp $ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef _re_h
+#define _re_h
+
+/*
+ * Defines
+ */
+
+ /* Compile flags options */
+#define REG_BASIC 0000 /* Not used */
+#define REG_EXTENDED 0001 /* Not used, only extended supported */
+
+#define RE_ICASE 0002
+#define RE_NOSUB 0004
+#define RE_NEWLINE 0010
+#define RE_NOSPEC 0020
+#define RE_PEND 0040
+#define RE_DUMP 0200
+
+
+
+ /* Execute flag options */
+#define RE_NOTBOL 1
+#define RE_NOTEOL 2
+#define RE_STARTEND 4
+#define RE_TRACE 00400 /* Not used/supported */
+#define RE_LARGE 01000 /* Not used/supported */
+#define RE_BACKR 02000 /* Not used/supported */
+
+ /* Value returned by reexec when match fails */
+#define RE_NOMATCH 1
+ /* Compile error values */
+#define RE_BADPAT 2
+#define RE_ECOLLATE 3
+#define RE_ECTYPE 4
+#define RE_EESCAPE 5
+#define RE_ESUBREG 6
+#define RE_EBRACK 7
+#define RE_EPAREN 8
+#define RE_EBRACE 9
+#define RE_EBADBR 10
+#define RE_ERANGE 11
+#define RE_ESPACE 12
+#define RE_BADRPT 13
+#define RE_EMPTY 14
+#define RE_ASSERT 15
+#define RE_INVARG 16
+#define RE_ATOI 255 /* Not used/supported */
+#define RE_ITOA 0400 /* Not used/supported */
+
+
+/*
+ * Types
+ */
+
+/* (re)gular expression (mat)ch result */
+typedef struct _re_mat {
+ long rm_so;
+ long rm_eo;
+} re_mat;
+
+/* (re)gular expression (cod)e */
+typedef struct _re_cod {
+ unsigned char *cod;
+ int re_nsub; /* Public member */
+ const char *re_endp; /* Support for RE_PEND */
+} re_cod;
+
+
+/*
+ * Prototypes
+ */
+ /* compile the given pattern string
+ * returns 0 on success, error code otherwise */
+int recomp(re_cod *preg, const char *pattern, int flags);
+
+ /* execute the compiled pattern on the string.
+ * returns 0 if matched, RE_NOMATCH if failed, error code otherwise */
+int reexec(const re_cod *preg, const char *string,
+ int nmat, re_mat pmat[], int flags);
+
+ /* formats an error message for the given code in ebuffer */
+int reerror(int ecode, const re_cod *preg, char *ebuffer, int ebuffer_size);
+
+ /* frees the given parameter */
+void refree(re_cod *preg);
+
+
+#endif /* _re_h */
diff --git a/lisp/re/rec.c b/lisp/re/rec.c
new file mode 100644
index 0000000..20f9fd9
--- /dev/null
+++ b/lisp/re/rec.c
@@ -0,0 +1,1015 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/re/rec.c,v 1.4 2003/01/16 06:25:52 paulo Exp $ */
+
+#include "rep.h"
+
+/*
+ * Types
+ */
+
+/* Information used while compiling the intermediate format of the re. */
+typedef struct _irec_info {
+ unsigned char *ptr; /* Pointer in the given regex pattern */
+ unsigned char *end; /* End of regex pattern */
+ int flags; /* Compile flags */
+ rec_alt *alt; /* Toplevel first/single alternative */
+
+ rec_alt *palt; /* Current alternative being compiled */
+ rec_grp *pgrp; /* Current group, if any */
+ rec_pat *ppat; /* Current pattern, if any */
+
+ /* Number of open parenthesis, for error checking */
+ int nparens;
+
+ int ngrps; /* Number of groups, for backreference */
+
+ int ecode;
+} irec_info;
+
+
+/*
+ * Prototypes
+ */
+
+ /* (i)ntermediate (r)egular (e)xpression (c)ompile
+ * Generates an intermediate stage compiled regex from
+ * the specified pattern argument. Basically builds an
+ * intermediate data structure to analyse and do syntax
+ * error checking.
+ */
+static void irec_simple_pattern(irec_info*, rec_pat_t);
+static void irec_literal_pattern(irec_info*, int);
+static void irec_case_literal_pattern(irec_info*, int);
+static void irec_open_group(irec_info*);
+static void irec_close_group(irec_info*);
+static void irec_range(irec_info*);
+static void irec_range_single(irec_info*, int);
+static void irec_range_complex(irec_info*, int, int);
+static void irec_escape(irec_info*);
+static void irec_simple_repetition(irec_info*, rec_rep_t);
+static void irec_complex_repetition(irec_info*);
+static void irec_add_repetition(irec_info*, rec_rep*);
+static void irec_free(irec_info*);
+static void irec_free_grp(rec_grp*);
+static void irec_free_pats(rec_pat*);
+
+
+/*
+ * Implementation
+ */
+rec_alt *
+irec_comp(const char *pattern, const char *endp, int flags, int *ecode)
+{
+ unsigned char *ptr;
+ rec_alt *alt;
+ irec_info inf;
+
+ if (pattern == NULL || endp < pattern) {
+ *ecode = RE_INVARG;
+ return (NULL);
+ }
+
+ if (endp == pattern) {
+ *ecode = RE_EMPTY;
+ return (NULL);
+ }
+
+ alt = calloc(1, sizeof(rec_alt));
+ if (alt == NULL) {
+ *ecode = RE_ESPACE;
+ return (NULL);
+ }
+
+ inf.ptr = (unsigned char*)pattern;
+ inf.end = (unsigned char*)endp;
+ inf.flags = flags;
+ inf.alt = inf.palt = alt;
+ inf.pgrp = NULL;
+ inf.ppat = NULL;
+ inf.nparens = inf.ngrps = 0;
+ inf.ecode = 0;
+
+ if (flags & RE_NOSPEC) {
+ /* Just searching for a character or substring */
+ for (; inf.ecode == 0 && inf.ptr < inf.end; inf.ptr++) {
+ if (!(flags & RE_ICASE) ||
+ (!isupper(*inf.ptr) && !islower(*inf.ptr)))
+ irec_literal_pattern(&inf, *inf.ptr);
+ else
+ irec_case_literal_pattern(&inf, *inf.ptr);
+ }
+ }
+ /* inf.ptr = inf.end is nul if flags & RE_NOSPEC */
+ for (; inf.ecode == 0 && inf.ptr < inf.end;) {
+ switch (*inf.ptr++) {
+ case '*':
+ irec_simple_repetition(&inf, Rer_AnyTimes);
+ break;
+ case '+':
+ irec_simple_repetition(&inf, Rer_AtLeast);
+ break;
+ case '?':
+ irec_simple_repetition(&inf, Rer_Maybe);
+ break;
+ case '.':
+ irec_simple_pattern(&inf, Rep_Any);
+ break;
+ case '^':
+ if (flags & RE_NEWLINE)
+ /* It is up to the user decide if this can match */
+ irec_simple_pattern(&inf, Rep_Bol);
+ else {
+ for (ptr = inf.ptr - 1;
+ ptr > (unsigned char*)pattern && *ptr == '('; ptr--)
+ ;
+ /* If at the start of a pattern */
+ if (ptr == (unsigned char*)pattern || *ptr == '|')
+ irec_simple_pattern(&inf, Rep_Bol);
+ else
+ /* In the middle of a pattern, treat as literal */
+ irec_literal_pattern(&inf, '^');
+ }
+ break;
+ case '$':
+ if (flags & RE_NEWLINE)
+ irec_simple_pattern(&inf, Rep_Eol);
+ else {
+ /* Look ahead to check if is the last char of a group */
+ for (ptr = inf.ptr; ptr < inf.end && *ptr == ')'; ptr++)
+ ;
+ if (*ptr == '\0' || *ptr == '|')
+ /* Last character of pattern, an EOL match */
+ irec_simple_pattern(&inf, Rep_Eol);
+ else
+ /* Normal character */
+ irec_literal_pattern(&inf, '$');
+ }
+ break;
+ case '(':
+ irec_open_group(&inf);
+ break;
+ case ')':
+ /* Look ahead to check if need to close the group now */
+ ptr = inf.ptr;
+ if (*ptr != '*' && *ptr != '+' && *ptr != '?' && *ptr != '{')
+ /* If a repetition does not follow */
+ irec_close_group(&inf);
+ else if (inf.pgrp == NULL)
+ /* A repetition follows, but current group is implicit */
+ inf.ecode = RE_EPAREN;
+ else
+ /* Can do this as next character is known */
+ inf.ppat = NULL;
+ break;
+ case '[':
+ irec_range(&inf);
+ break;
+ case ']':
+ irec_literal_pattern(&inf, ']');
+ break;
+ case '{':
+ irec_complex_repetition(&inf);
+ break;
+ case '}':
+ irec_literal_pattern(&inf, '}');
+ break;
+ case '|':
+ /* If first character in the pattern */
+ if (inf.ptr - 1 == (unsigned char*)pattern ||
+ /* If last character in the pattern */
+ inf.ptr >= inf.end ||
+ /* If empty pattern */
+ inf.ptr[0] == '|' ||
+ inf.ptr[0] == ')')
+ inf.ecode = RE_EMPTY;
+ else {
+ rec_alt *alt = calloc(1, sizeof(rec_alt));
+
+ if (alt) {
+ alt->prev = inf.palt;
+ inf.palt->next = alt;
+ inf.palt = alt;
+ inf.ppat = NULL;
+ }
+ else
+ inf.ecode = RE_ESPACE;
+ }
+ break;
+ case '\\':
+ irec_escape(&inf);
+ break;
+ default:
+ if (!(flags & RE_ICASE) ||
+ (!isupper(inf.ptr[-1]) && !islower(inf.ptr[-1])))
+ irec_literal_pattern(&inf, inf.ptr[-1]);
+ else
+ irec_case_literal_pattern(&inf, inf.ptr[-1]);
+ break;
+ }
+ }
+
+ /* Check if not all groups closed */
+ if (inf.ecode == 0 && inf.nparens)
+ inf.ecode = RE_EPAREN;
+
+ if (inf.ecode == 0)
+ inf.ecode = orec_comp(inf.alt, flags);
+
+ /* If an error generated */
+ if (inf.ecode) {
+ irec_free(&inf);
+ alt = NULL;
+ }
+
+ *ecode = inf.ecode;
+
+ return (alt);
+}
+
+void
+irec_free_alt(rec_alt *alt)
+{
+ rec_alt *next;
+
+ while (alt) {
+ next = alt->next;
+ irec_free_pats(alt->pat);
+ free(alt);
+ alt = next;
+ }
+}
+
+
+
+static void
+irec_simple_pattern(irec_info *inf, rec_pat_t type)
+{
+ rec_pat *pat;
+
+ /* Always add a new pattern to list */
+ if ((pat = calloc(1, sizeof(rec_pat))) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ pat->type = type;
+ if ((pat->prev = inf->ppat) != NULL)
+ inf->ppat->next = pat;
+ else
+ inf->palt->pat = pat;
+ inf->ppat = pat;
+}
+
+static void
+irec_literal_pattern(irec_info *inf, int value)
+{
+ int length;
+ rec_pat *pat;
+ unsigned char chr, *str;
+
+ /* If there is a current pattern */
+ if (inf->ppat && inf->ppat->rep == NULL) {
+ switch (inf->ppat->type) {
+ case Rep_Literal:
+ /* Start literal string */
+ chr = inf->ppat->data.chr;
+ if ((str = malloc(16)) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+ inf->ppat->type = Rep_String;
+ inf->ppat->data.str = str;
+ str[0] = chr;
+ str[1] = value;
+ str[2] = '\0';
+ return;
+
+ case Rep_String:
+ /* Augments literal string */
+ length = strlen((char*)inf->ppat->data.str);
+ if ((length % 16) >= 14) {
+ if ((str = realloc(inf->ppat->data.str,
+ length + 18)) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+ inf->ppat->data.str = str;
+ }
+ inf->ppat->data.str[length] = value;
+ inf->ppat->data.str[length + 1] = '\0';
+ return;
+
+ default:
+ /* Anything else is added as a new pattern list element */
+ break;
+ }
+ }
+
+ if ((pat = calloc(1, sizeof(rec_pat))) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ pat->type = Rep_Literal;
+ pat->data.chr = value;
+ if ((pat->prev = inf->ppat) != NULL)
+ inf->ppat->next = pat;
+ else
+ inf->palt->pat = pat;
+ inf->ppat = pat;
+}
+
+static void
+irec_case_literal_pattern(irec_info *inf, int value)
+{
+ int length;
+ rec_pat *pat;
+ unsigned char plower, pupper, lower, upper, *str;
+
+ lower = tolower(value);
+ upper = toupper(value);
+
+ /* If there is a current pattern */
+ if (inf->ppat && inf->ppat->rep == NULL) {
+ switch (inf->ppat->type) {
+ case Rep_CaseLiteral:
+ /* Start case literal string */
+ plower = inf->ppat->data.cse.lower;
+ pupper = inf->ppat->data.cse.upper;
+ if ((str = malloc(32)) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+ inf->ppat->type = Rep_CaseString;
+ inf->ppat->data.str = str;
+ str[0] = plower;
+ str[1] = pupper;
+ str[2] = lower;
+ str[3] = upper;
+ str[4] = '\0';
+ return;
+
+ case Rep_CaseString:
+ /* Augments case literal string */
+ length = strlen((char*)inf->ppat->data.str);
+ if (((length) % 32) >= 28) {
+ if ((str = realloc(inf->ppat->data.str,
+ length + 36)) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+ inf->ppat->data.str = str;
+ }
+ inf->ppat->data.str[length] = lower;
+ inf->ppat->data.str[length + 1] = upper;
+ inf->ppat->data.str[length + 2] = '\0';
+ return;
+
+ default:
+ /* Anything else is added as a new pattern list element */
+ break;
+ }
+ }
+
+ if ((pat = calloc(1, sizeof(rec_pat))) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ pat->type = Rep_CaseLiteral;
+ pat->data.cse.lower = lower;
+ pat->data.cse.upper = upper;
+ pat->prev = inf->ppat;
+ if ((pat->prev = inf->ppat) != NULL)
+ inf->ppat->next = pat;
+ else
+ inf->palt->pat = pat;
+ inf->ppat = pat;
+}
+
+static void
+irec_open_group(irec_info *inf)
+{
+ rec_pat *pat;
+ rec_alt *alt;
+ rec_grp *grp;
+
+ if ((grp = calloc(1, sizeof(rec_grp))) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ if ((pat = calloc(1, sizeof(rec_pat))) == NULL) {
+ free(grp);
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ if ((alt = calloc(1, sizeof(rec_alt))) == NULL) {
+ free(grp);
+ free(pat);
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ pat->type = Rep_Group;
+ pat->data.grp = grp;
+ grp->parent = pat;
+ grp->palt = inf->palt;
+ grp->pgrp = inf->pgrp;
+ grp->alt = alt;
+ grp->comp = 0;
+ if ((pat->prev = inf->ppat) != NULL)
+ inf->ppat->next = pat;
+ else
+ inf->palt->pat = pat;
+ inf->palt = alt;
+ inf->ppat = NULL;
+
+ /* Only toplevel parenthesis supported */
+ if (++inf->nparens == 1)
+ ++inf->ngrps;
+
+ inf->pgrp = grp;
+}
+
+static void
+irec_close_group(irec_info *inf)
+{
+ if (inf->pgrp == NULL) {
+ inf->ecode = RE_EPAREN;
+ return;
+ }
+
+ inf->palt = inf->pgrp->palt;
+ inf->ppat = inf->pgrp->parent;
+ inf->pgrp = inf->pgrp->pgrp;
+
+ --inf->nparens;
+}
+
+static void
+irec_range(irec_info *inf)
+{
+ int count;
+ rec_pat *pat;
+ rec_rng *rng;
+ int not = inf->ptr[0] == '^';
+
+ if (not)
+ ++inf->ptr;
+
+ pat = calloc(1, sizeof(rec_pat));
+ if (pat == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ rng = calloc(1, sizeof(rec_rng));
+ if (pat == NULL) {
+ free(pat);
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ pat->data.rng = rng;
+ pat->type = not ? Rep_RangeNot : Rep_Range;
+ if ((pat->prev = inf->ppat) != NULL)
+ inf->ppat->next = pat;
+ else
+ inf->palt->pat = pat;
+ inf->ppat = pat;
+
+ /* First pass, add everything seen */
+ for (count = 0; inf->ecode == 0; count++) {
+ /* If bracket not closed */
+ if (inf->ptr == inf->end) {
+ inf->ecode = RE_EBRACK;
+ return;
+ }
+ /* If not the first character */
+ else if (inf->ptr[0] == ']' && count)
+ break;
+ else {
+ /* If not a range of characters */
+ if (inf->ptr[1] != '-' || inf->ptr[2] == ']') {
+ irec_range_single(inf, inf->ptr[0]);
+ ++inf->ptr;
+ }
+ else {
+ if ((inf->flags & RE_NEWLINE) &&
+ inf->ptr[0] < '\n' && inf->ptr[2] > '\n') {
+ /* Unless it is forced to be a delimiter, don't allow
+ * a newline in a character range */
+ if (inf->ptr[0] == '\n' - 1)
+ irec_range_single(inf, inf->ptr[0]);
+ else
+ irec_range_complex(inf, inf->ptr[0], '\n' - 1);
+ if (inf->ptr[2] == '\n' + 1)
+ irec_range_single(inf, inf->ptr[2]);
+ else
+ irec_range_complex(inf, '\n' + 1, inf->ptr[2]);
+ }
+ else
+ irec_range_complex(inf, inf->ptr[0], inf->ptr[2]);
+ inf->ptr += 3;
+ }
+ }
+ }
+
+ /* Skip ] */
+ ++inf->ptr;
+}
+
+static void
+irec_range_single(irec_info *inf, int value)
+{
+ if (value >= 0 && value <= 255)
+ inf->ppat->data.rng->range[value] = 1;
+
+ if (inf->flags & RE_ICASE) {
+ if (islower(value)) {
+ value = toupper(value);
+ if (value >= 0 && value <= 255)
+ inf->ppat->data.rng->range[value] = 1;
+ }
+ else if (isupper(value)) {
+ value = tolower(value);
+ if (value >= 0 && value <= 255)
+ inf->ppat->data.rng->range[value] = 1;
+ }
+ }
+}
+
+static void
+irec_range_complex(irec_info *inf, int chrf, int chrt)
+{
+ if (chrf > chrt) {
+ inf->ecode = RE_ERANGE;
+ return;
+ }
+
+ for (; chrf <= chrt; chrf++)
+ irec_range_single(inf, chrf);
+}
+
+static void
+irec_escape(irec_info *inf)
+{
+ rec_pat *pat;
+ unsigned char chr = inf->ptr[0];
+
+ if (chr == 0) {
+ inf->ecode = RE_EESCAPE;
+ return;
+ }
+ ++inf->ptr;
+ switch (chr) {
+ case 'o':
+ irec_simple_pattern(inf, Rep_Odigit);
+ break;
+ case 'O':
+ irec_simple_pattern(inf, Rep_OdigitNot);
+ break;
+ case 'd':
+ irec_simple_pattern(inf, Rep_Digit);
+ break;
+ case 'D':
+ irec_simple_pattern(inf, Rep_DigitNot);
+ break;
+ case 'x':
+ irec_simple_pattern(inf, Rep_Xdigit);
+ break;
+ case 'X':
+ irec_simple_pattern(inf, Rep_XdigitNot);
+ break;
+ case 's':
+ irec_simple_pattern(inf, Rep_Space);
+ break;
+ case 'S':
+ irec_simple_pattern(inf, Rep_SpaceNot);
+ break;
+ case 't':
+ irec_simple_pattern(inf, Rep_Tab);
+ break;
+ case 'n':
+ irec_simple_pattern(inf, Rep_Newline);
+ break;
+ case 'l':
+ irec_simple_pattern(inf, Rep_Lower);
+ break;
+ case 'u':
+ irec_simple_pattern(inf, Rep_Upper);
+ break;
+ case 'w':
+ irec_simple_pattern(inf, Rep_Alnum);
+ break;
+ case 'W':
+ irec_simple_pattern(inf, Rep_AlnumNot);
+ break;
+ case 'c':
+ irec_simple_pattern(inf, Rep_Control);
+ break;
+ case 'C':
+ irec_simple_pattern(inf, Rep_ControlNot);
+ break;
+ case '<':
+ irec_simple_pattern(inf, Rep_Bow);
+ break;
+ case '>':
+ irec_simple_pattern(inf, Rep_Eow);
+ break;
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ if ((inf->flags & RE_NOSUB) || (chr -= '1') >= inf->ngrps) {
+ inf->ecode = RE_ESUBREG;
+ return;
+ }
+ if ((pat = calloc(1, sizeof(rec_pat))) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+ pat->type = Rep_Backref;
+ pat->data.chr = chr;
+ pat->prev = inf->ppat;
+ if (inf->ppat)
+ inf->ppat->next = pat;
+ else
+ inf->palt->pat = pat;
+ inf->ppat = pat;
+ break;
+
+ /* True literals */
+ case '0':
+ irec_literal_pattern(inf, '\0');
+ break;
+ case 'a':
+ irec_literal_pattern(inf, '\a');
+ break;
+ case 'b':
+ irec_literal_pattern(inf, '\b');
+ break;
+ case 'f':
+ irec_literal_pattern(inf, '\f');
+ break;
+ case 'r':
+ irec_literal_pattern(inf, '\r');
+ break;
+ case 'v':
+ irec_literal_pattern(inf, '\v');
+ break;
+
+ default:
+ /* Don't check if case insensitive regular expression */
+ irec_literal_pattern(inf, chr);
+ break;
+ }
+}
+
+static void
+irec_simple_repetition(irec_info *inf, rec_rep_t type)
+{
+ rec_rep *rep;
+
+ /* If nowhere to add repetition */
+ if ((inf->pgrp == NULL && inf->ppat == NULL) ||
+ /* If repetition already added to last/current pattern */
+ (inf->pgrp == NULL && inf->ppat->rep != NULL) ||
+ /* If repetition already added to last/current group */
+ (inf->ppat == NULL && inf->pgrp->parent->rep != NULL)) {
+ inf->ecode = RE_BADRPT;
+ return;
+ }
+
+ if ((rep = calloc(1, sizeof(rec_rep))) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ rep->type = type;
+ irec_add_repetition(inf, rep);
+}
+
+static void
+irec_complex_repetition(irec_info *inf)
+{
+ int exact;
+ rec_rep *rep;
+ long mine, maxc;
+ unsigned char *end;
+
+ /* If nowhere to add repetition */
+ if ((inf->pgrp == NULL && inf->ppat == NULL) ||
+ /* If repetition already added to last/current pattern */
+ (inf->pgrp == NULL && inf->ppat->rep != NULL) ||
+ /* If repetition already added to last/current group */
+ (inf->ppat == NULL && inf->pgrp->parent->rep != NULL)) {
+ inf->ecode = RE_EBADBR;
+ return;
+ }
+
+ exact = 0;
+ mine = maxc = -1;
+ if (inf->ptr[0] == ',')
+ /* Specify max number of ocurrences only */
+ goto domax;
+ else if (!isdigit(inf->ptr[0]))
+ goto badbr;
+
+ mine = strtol((char*)inf->ptr, (char**)&end, 10);
+ inf->ptr = end;
+ if (inf->ptr[0] == '}') {
+ exact = 1;
+ ++inf->ptr;
+ goto redone;
+ }
+ else if (inf->ptr[0] != ',')
+ goto badbr;
+
+domax:
+ /* Add one to skip comma */
+ ++inf->ptr;
+ if (inf->ptr[0] == '}') {
+ ++inf->ptr;
+ goto redone;
+ }
+ else if (!isdigit(inf->ptr[0]))
+ goto badbr;
+ maxc = strtol((char*)inf->ptr, (char**)&end, 10);
+ inf->ptr = end;
+ if (inf->ptr[0] != '}')
+ goto badbr;
+ ++inf->ptr;
+
+redone:
+ if (mine == maxc) {
+ maxc = -1;
+ exact = 1;
+ }
+
+ /* Check range and if min-max parameters are valid */
+ if (mine >= 255 || maxc >= 255 ||
+ (mine >= 0 && maxc >= 0 && mine > maxc))
+ goto badbr;
+
+ /* Check for noop */
+ if (exact && mine == 1)
+ return;
+
+ if ((rep = calloc(1, sizeof(rec_rep))) == NULL) {
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ /* Convert {0,1} to ? */
+ if (mine == 0 && maxc == 1)
+ rep->type = Rer_Maybe;
+ else if (exact) {
+ rep->type = Rer_Exact;
+ rep->mine = mine;
+ }
+ /* Convert {0,} to * */
+ else if (mine == 0 && maxc == -1)
+ rep->type = Rer_AnyTimes;
+ /* Convert {1,} to + */
+ else if (mine == 1 && maxc == -1)
+ rep->type = Rer_AtLeast;
+ else if (maxc == -1) {
+ rep->type = Rer_Min;
+ rep->mine = mine;
+ }
+ else if (mine < 1) {
+ rep->type = Rer_Max;
+ rep->maxc = maxc;
+ }
+ else {
+ rep->type = Rer_MinMax;
+ rep->mine = mine;
+ rep->maxc = maxc;
+ }
+
+ irec_add_repetition(inf, rep);
+
+ return;
+
+badbr:
+ inf->ecode = RE_EBADBR;
+}
+
+/* The rep argument is allocated and has no reference yet,
+ * if something fails it must be freed before returning.
+ */
+static void
+irec_add_repetition(irec_info *inf, rec_rep *rep)
+{
+ int length;
+ rec_pat *pat;
+ rec_grp *grp;
+ rec_rep_t rept;
+ unsigned char value, upper;
+
+ rept = rep->type;
+
+ if (inf->ppat == NULL) {
+ rec_pat *any;
+ rec_grp *grp = inf->pgrp;
+
+ if (rept == Rer_AnyTimes || rept == Rer_Maybe || rept == Re_AtLeast) {
+ /* Convert (.)* to (.*), ((.))* not handled and may not match */
+ any = NULL;
+
+ if (grp->alt && grp->alt->pat) {
+ for (any = grp->alt->pat; any->next; any = any->next)
+ ;
+ switch (any->type) {
+ case Rep_Any:
+ break;
+ case Rep_AnyAnyTimes:
+ case Rep_AnyMaybe:
+ case Rep_AnyAtLeast:
+ free(rep);
+ inf->ecode = RE_BADRPT;
+ return;
+ default:
+ any = NULL;
+ break;
+ }
+ }
+ if (any) {
+ free(rep);
+ rep = NULL;
+ any->type = (rept == Rer_AnyTimes) ? Rep_AnyAnyTimes :
+ (rept == Rer_AtLeast) ? Rep_AnyAtLeast :
+ Rep_AnyMaybe;
+ while (grp) {
+ ++grp->comp;
+ grp = grp->pgrp;
+ }
+ }
+ }
+ inf->pgrp->parent->rep = rep;
+ irec_close_group(inf);
+ return;
+ }
+
+ switch (inf->ppat->type) {
+ case Rep_Bol:
+ case Rep_Eol:
+ case Rep_Bow:
+ case Rep_Eow:
+ case Rep_AnyAnyTimes:
+ case Rep_AnyMaybe:
+ case Rep_AnyAtLeast:
+ /* Markers that cannot repeat */
+ free(rep);
+ inf->ecode = RE_BADRPT;
+ return;
+
+ case Rep_Any:
+ grp = inf->pgrp;
+ free(rep);
+ if (rept == Rer_AnyTimes ||
+ rept == Rer_Maybe ||
+ rept == Rer_AtLeast) {
+ inf->ppat->type = (rept == Rer_AnyTimes) ?
+ Rep_AnyAnyTimes :
+ (rept == Rer_Maybe) ?
+ Rep_AnyMaybe :
+ Rep_AnyAtLeast;
+ while (grp) {
+ ++grp->comp;
+ grp = grp->pgrp;
+ }
+ }
+ else
+ /* XXX Not (yet) implemented */
+ inf->ecode = RE_BADRPT;
+ rep = NULL;
+ break;
+
+ case Rep_String:
+ if ((pat = calloc(1, sizeof(rec_pat))) == NULL) {
+ free(rep);
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ length = strlen((char*)inf->ppat->data.str);
+ pat->type = Rep_Literal;
+ pat->prev = inf->ppat;
+ pat->data.chr = inf->ppat->data.str[length - 1];
+ if (length == 2) {
+ /* Must convert to two Rep_Literals */
+ value = inf->ppat->data.str[0];
+ free(inf->ppat->data.str);
+ inf->ppat->data.chr = value;
+ inf->ppat->type = Rep_Literal;
+ }
+ else
+ /* Must remove last character from string */
+ inf->ppat->data.str[length - 1] = '\0';
+ inf->ppat->next = pat;
+ inf->ppat = pat;
+ break;
+
+ case Rep_CaseString:
+ if ((pat = calloc(1, sizeof(rec_pat))) == NULL) {
+ free(rep);
+ inf->ecode = RE_ESPACE;
+ return;
+ }
+
+ length = strlen((char*)inf->ppat->data.str);
+ pat->type = Rep_CaseLiteral;
+ pat->prev = inf->ppat;
+ pat->data.cse.lower = inf->ppat->data.str[length - 2];
+ pat->data.cse.upper = inf->ppat->data.str[length - 1];
+ if (length == 4) {
+ /* Must convert to two Rep_CaseLiterals */
+ value = inf->ppat->data.str[0];
+ upper = inf->ppat->data.str[1];
+ free(inf->ppat->data.str);
+ inf->ppat->data.cse.lower = value;
+ inf->ppat->data.cse.upper = upper;
+ inf->ppat->next = pat;
+ inf->ppat->type = Rep_CaseLiteral;
+ }
+ else
+ /* Must remove last character pair from string */
+ inf->ppat->data.str[length - 2] = '\0';
+ inf->ppat->next = pat;
+ inf->ppat = pat;
+ break;
+
+ default:
+ /* Anything else does not need special handling */
+ break;
+ }
+
+ inf->ppat->rep = rep;
+}
+
+static void
+irec_free(irec_info *inf)
+{
+ irec_free_alt(inf->alt);
+}
+
+static void
+irec_free_grp(rec_grp *grp)
+{
+ if (grp->alt)
+ irec_free_alt(grp->alt);
+ free(grp);
+}
+
+static void
+irec_free_pats(rec_pat *pat)
+{
+ rec_pat *next;
+ rec_pat_t rect;
+
+ while (pat) {
+ next = pat->next;
+ if (pat->rep)
+ free(pat->rep);
+ rect = pat->type;
+ if (rect == Rep_Range || rect == Rep_RangeNot)
+ free(pat->data.rng);
+ else if (rect == Rep_Group)
+ irec_free_grp(pat->data.grp);
+ else if (rect == Rep_StringList)
+ orec_free_stl(pat->data.stl);
+ free(pat);
+ pat = next;
+ }
+}
diff --git a/lisp/re/reo.c b/lisp/re/reo.c
new file mode 100644
index 0000000..59cbf3b
--- /dev/null
+++ b/lisp/re/reo.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/re/reo.c,v 1.9 2002/11/15 07:01:33 paulo Exp $ */
+
+#include "rep.h"
+
+/*
+ * This file is a placeholder to add code to analyse and optimize the
+ * intermediate data structure generated in rep.c.
+ * Character ranges are optimized while being generated.
+ */
+
+/*
+ * Types
+ */
+typedef struct _orec_inf {
+ rec_alt *alt; /* Main alternatives list */
+ rec_grp *grp; /* Current group pointer */
+ int flags;
+ int ecode;
+} orec_inf;
+
+/*
+ * Prototypes
+ */
+static int orec_alt(orec_inf*, rec_alt*);
+static int orec_pat(orec_inf*, rec_pat*);
+static int orec_grp(orec_inf*, rec_grp*);
+static int orec_pat_bad_rpt(orec_inf*, rec_pat*);
+static int orec_pat_bad_forward_rpt(orec_inf*, rec_pat*);
+static int orec_pat_rng(orec_inf*, rec_pat*);
+static int orec_pat_cse(orec_inf*, rec_pat*);
+static int orec_pat_cse_can(orec_inf*, rec_pat*);
+static int orec_str_list(orec_inf*, rec_alt*, int, int);
+
+/*
+ * Initialization
+ */
+extern unsigned char re__alnum[256];
+extern unsigned char re__odigit[256];
+extern unsigned char re__ddigit[256];
+extern unsigned char re__xdigit[256];
+extern unsigned char re__control[256];
+
+/*
+ * Implementation
+ */
+int
+orec_comp(rec_alt *alt, int flags)
+{
+ orec_inf inf;
+
+ inf.alt = alt;
+ inf.grp = NULL;
+ inf.flags = flags;
+ inf.ecode = 0;
+
+ orec_alt(&inf, alt);
+
+ return (inf.ecode);
+}
+
+void
+orec_free_stl(rec_stl *stl)
+{
+ int i;
+
+ for (i = 0; i < stl->nstrs; i++) {
+ if (stl->lens[i] > 2)
+ free(stl->strs[i]);
+ }
+
+ free(stl->lens);
+ free(stl->strs);
+ free(stl);
+}
+
+
+static int
+orec_alt(orec_inf *inf, rec_alt *alt)
+{
+ if (alt) {
+ rec_alt *ptr = alt;
+ int ret, count = 0, str = 1, cstr = 1, lits = 0, clits = 0;
+
+ /* Check if can build a string list */
+ if (ptr->next) {
+ /* If more than one alternative */
+ while (ptr && (str || cstr)) {
+ if (ptr->pat == NULL || ptr->pat->rep != NULL) {
+ cstr = str = 0;
+ break;
+ }
+ if ((inf->flags & RE_ICASE)) {
+ if (!(ret = orec_pat_cse_can(inf, ptr->pat))) {
+ cstr = str = 0;
+ break;
+ }
+ if (ret == 1)
+ ++lits;
+ else if (ret == 2)
+ ++clits;
+ }
+ else if (ptr->pat->next == NULL) {
+ if (ptr->pat->type != Rep_String) {
+ if (ptr->pat->type != Rep_Literal) {
+ str = 0;
+ if (ptr->pat->type != Rep_CaseString) {
+ if (ptr->pat->type != Rep_CaseLiteral)
+ cstr = 0;
+ else
+ ++clits;
+ }
+ else if (strlen((char*)ptr->pat->data.str) >= 255)
+ str = cstr = 0;
+ }
+ else
+ ++lits;
+ }
+ else if (strlen((char*)ptr->pat->data.str) >= 255)
+ str = cstr = 0;
+ }
+ else {
+ str = cstr = 0;
+ break;
+ }
+ if (++count >= 255)
+ str = cstr = 0;
+ ptr = ptr->next;
+ }
+
+ if (str || cstr) {
+ if (inf->flags & RE_ICASE) {
+ for (ptr = alt; ptr; ptr = ptr->next) {
+ if (orec_pat_cse(inf, ptr->pat))
+ return (inf->ecode);
+ }
+ str = 0;
+ }
+ return (orec_str_list(inf, alt, str, count));
+ }
+ }
+ else if (alt == inf->alt && alt->pat && alt->pat->rep == NULL) {
+ /* If the toplevel single alternative */
+ switch (alt->pat->type) {
+ /* One of these will always be true for RE_NOSPEC,
+ * but can also be optimized for simple patterns */
+ case Rep_Literal:
+ alt->pat->type = Rep_SearchLiteral;
+ break;
+ case Rep_CaseLiteral:
+ alt->pat->type = Rep_SearchCaseLiteral;
+ break;
+ case Rep_String:
+ alt->pat->type = Rep_SearchString;
+ break;
+ case Rep_CaseString:
+ alt->pat->type = Rep_SearchCaseString;
+ break;
+ default:
+ break;
+ }
+ }
+
+ while (alt) {
+ orec_pat(inf, alt->pat);
+ alt = alt->next;
+ }
+ }
+
+ return (inf->ecode);
+}
+
+static int
+orec_pat(orec_inf *inf, rec_pat *pat)
+{
+ rec_pat *next;
+
+ while (pat) {
+ switch (pat->type) {
+ case Rep_AnyAnyTimes:
+ if (pat->next == NULL) {
+ rec_grp *grp = inf->grp;
+
+ next = NULL;
+ while (grp) {
+ next = grp->parent->next;
+ /* Cannot check if is .*$ as the input
+ * may be a substring */
+ if (next)
+ break;
+ grp = grp->pgrp;
+ }
+ if (next == NULL) {
+ /* <re>.* */
+ pat->type = Rep_AnyEatAnyTimes;
+ grp = inf->grp;
+ while (grp) {
+ --grp->comp;
+ next = grp->parent->next;
+ if (next)
+ break;
+ grp = grp->pgrp;
+ }
+ }
+ else if (orec_pat_bad_rpt(inf, next))
+ return (inf->ecode);
+ }
+ else if (orec_pat_bad_rpt(inf, pat->next))
+ return (inf->ecode);
+ break;
+ case Rep_AnyMaybe:
+ if (pat->next == NULL) {
+ rec_grp *grp = inf->grp;
+
+ next = NULL;
+ while (grp) {
+ next = grp->parent->next;
+ if (next)
+ break;
+ grp = grp->pgrp;
+ }
+ if (next == NULL) {
+ /* <re>.? */
+ pat->type = Rep_AnyEatMaybe;
+ grp = inf->grp;
+ while (grp) {
+ --grp->comp;
+ next = grp->parent->next;
+ if (next)
+ break;
+ grp = grp->pgrp;
+ }
+ }
+ else if (orec_pat_bad_rpt(inf, next))
+ return (inf->ecode);
+ }
+ else if (orec_pat_bad_rpt(inf, pat->next))
+ return (inf->ecode);
+ break;
+ case Rep_AnyAtLeast:
+ if (pat->next == NULL) {
+ rec_grp *grp = inf->grp;
+
+ next = NULL;
+ while (grp) {
+ next = grp->parent->next;
+ if (next)
+ break;
+ grp = grp->pgrp;
+ }
+ if (next == NULL) {
+ /* <re>.+ */
+ pat->type = Rep_AnyEatAtLeast;
+ grp = inf->grp;
+ while (grp) {
+ --grp->comp;
+ next = grp->parent->next;
+ if (next)
+ break;
+ grp = grp->pgrp;
+ }
+ }
+ else if (orec_pat_bad_rpt(inf, next))
+ return (inf->ecode);
+ }
+ else if (orec_pat_bad_rpt(inf, pat->next))
+ return (inf->ecode);
+ break;
+ case Rep_Range:
+ case Rep_RangeNot:
+ orec_pat_rng(inf, pat);
+ break;
+ case Rep_Group:
+ orec_grp(inf, pat->data.grp);
+ break;
+ default:
+ break;
+ }
+ pat = pat->next;
+ }
+
+ return (inf->ecode);
+}
+
+static int
+orec_pat_bad_rpt(orec_inf *inf, rec_pat *pat)
+{
+ switch (pat->type) {
+ /* Not really an error, but aren't supported by the library.
+ * Includes: .*.*, .+<re>? .*<re>*, (.*)(<re>*), etc.
+ */
+
+ /* Not a repetition, but mathes anything... */
+ case Rep_Any:
+
+ /* Zero length matches */
+ case Rep_Eol:
+ if (!(inf->flags & RE_NEWLINE))
+ break;
+ case Rep_Bol:
+ case Rep_Bow:
+ case Rep_Eow:
+
+ /* Repetitions */
+ case Rep_AnyAnyTimes:
+ case Rep_AnyMaybe:
+ case Rep_AnyAtLeast:
+ inf->ecode = RE_BADRPT;
+ break;
+
+ /* Check if the first group element is a complex pattern */
+ case Rep_Group:
+ if (pat->rep == NULL) {
+ if (pat->data.grp->alt) {
+ for (pat = pat->data.grp->alt->pat; pat; pat = pat->next) {
+ if (orec_pat_bad_rpt(inf, pat))
+ break;
+ }
+ }
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ if (pat->rep)
+ inf->ecode = RE_BADRPT;
+ break;
+ }
+
+ if (!inf->ecode && pat && pat->next)
+ orec_pat_bad_forward_rpt(inf, pat->next);
+
+ return (inf->ecode);
+}
+
+static int
+orec_pat_bad_forward_rpt(orec_inf *inf, rec_pat *pat)
+{
+ if (pat->rep) {
+ switch (pat->rep->type) {
+ case Rer_MinMax:
+ if (pat->rep->mine > 0)
+ break;
+ case Rer_AnyTimes:
+ case Rer_Maybe:
+ case Rer_Max:
+ inf->ecode = RE_BADRPT;
+ default:
+ break;
+ }
+ }
+ else if (pat->type == Rep_Group &&
+ pat->data.grp->alt &&
+ pat->data.grp->alt->pat)
+ orec_pat_bad_forward_rpt(inf, pat->data.grp->alt->pat);
+
+ return (inf->ecode);
+}
+
+static int
+orec_grp(orec_inf *inf, rec_grp *grp)
+{
+ rec_grp *prev = inf->grp;
+
+ inf->grp = grp;
+ orec_alt(inf, grp->alt);
+ /* Could also just say: inf->grp = grp->gparent */
+ inf->grp = prev;
+
+ return (inf->ecode);
+}
+
+static int
+orec_pat_rng(orec_inf *inf, rec_pat *pat)
+{
+ int i, j[2], count;
+ rec_pat_t type = pat->type;
+ unsigned char *range = pat->data.rng->range;
+
+ for (i = count = j[0] = j[1] = 0; i < 256; i++) {
+ if (range[i]) {
+ if (count == 2) {
+ ++count;
+ break;
+ }
+ j[count++] = i;
+ }
+ }
+
+ if (count == 1 ||
+ (count == 2 &&
+ ((islower(j[0]) && toupper(j[0]) == j[1]) ||
+ (isupper(j[0]) && tolower(j[0]) == j[1])))) {
+ free(pat->data.rng);
+ if (count == 1) {
+ pat->data.chr = j[0];
+ pat->type = type == Rep_Range ? Rep_Literal : Rep_LiteralNot;
+ }
+ else {
+ pat->data.cse.upper = j[0];
+ pat->data.cse.lower = j[1];
+ pat->type = type == Rep_Range ? Rep_CaseLiteral : Rep_CaseLiteralNot;
+ }
+ }
+ else {
+ if (memcmp(re__alnum, range, 256) == 0)
+ type = type == Rep_Range ? Rep_Alnum : Rep_AlnumNot;
+ else if (memcmp(re__odigit, range, 256) == 0)
+ type = type == Rep_Range ? Rep_Odigit : Rep_OdigitNot;
+ else if (memcmp(re__ddigit, range, 256) == 0)
+ type = type == Rep_Range ? Rep_Digit : Rep_DigitNot;
+ else if (memcmp(re__xdigit, range, 256) == 0)
+ type = type == Rep_Range ? Rep_Xdigit : Rep_XdigitNot;
+ else if (memcmp(re__control, range, 256) == 0)
+ type = type == Rep_Range ? Rep_Control : Rep_ControlNot;
+
+ if (type != pat->type) {
+ free(pat->data.rng);
+ pat->type = type;
+ }
+ }
+
+ return (inf->ecode);
+}
+
+/* Join patterns if required, will only fail on memory allocation failure:
+ */
+static int
+orec_pat_cse(orec_inf *inf, rec_pat *pat)
+{
+ rec_pat_t type;
+ int i, len, length;
+ rec_pat *ptr, *next;
+ unsigned char *str, *tofree;
+
+ if (pat->next == NULL && pat->type == Rep_CaseString)
+ return (inf->ecode);
+
+ type = Rep_CaseString;
+
+ /* First calculate how many bytes will be required */
+ for (ptr = pat, length = 1; ptr; ptr = ptr->next) {
+ switch (ptr->type) {
+ case Rep_Literal:
+ length += 2;
+ break;
+ case Rep_String:
+ length += strlen((char*)ptr->data.str) << 1;
+ break;
+ case Rep_CaseLiteral:
+ length += 2;
+ break;
+ case Rep_CaseString:
+ length += strlen((char*)ptr->data.str);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((str = malloc(length)) == NULL)
+ return (inf->ecode = RE_ESPACE);
+
+ for (ptr = pat, length = 0; ptr; ptr = next) {
+ tofree = NULL;
+ next = ptr->next;
+ switch (ptr->type) {
+ case Rep_Literal:
+ str[length++] = ptr->data.chr;
+ str[length++] = ptr->data.chr;
+ break;
+ case Rep_String:
+ tofree = ptr->data.str;
+ len = strlen((char*)tofree);
+ for (i = 0; i < len; i++) {
+ str[length++] = tofree[i];
+ str[length++] = tofree[i];
+ }
+ break;
+ case Rep_CaseLiteral:
+ str[length++] = ptr->data.cse.lower;
+ str[length++] = ptr->data.cse.upper;
+ break;
+ case Rep_CaseString:
+ tofree = ptr->data.str;
+ len = strlen((char*)tofree);
+ memcpy(str + length, tofree, len);
+ length += len;
+ break;
+ default:
+ break;
+ }
+ if (tofree)
+ free(tofree);
+ if (ptr != pat)
+ free(ptr);
+ }
+ str[length] = '\0';
+
+ pat->type = type;
+ pat->data.str = str;
+ pat->next = NULL;
+
+ return (inf->ecode);
+}
+
+/* Return 0 if the patterns in the list cannot be merged, 1 if will
+ * be a simple string, 2 if a case string.
+ * This is useful when building an alternative list that is composed
+ * only of strings, but the regex is case insensitive, in wich case
+ * the first pass may have splited some patterns, but if it is a member
+ * of an alternatives list, the cost of using a string list is smaller */
+static int
+orec_pat_cse_can(orec_inf *inf, rec_pat *pat)
+{
+ int ret;
+
+ if (pat == NULL)
+ return (0);
+
+ for (ret = 1; pat; pat = pat->next) {
+ if (pat->rep)
+ return (0);
+ switch (pat->type) {
+ case Rep_Literal:
+ case Rep_String:
+ break;
+ case Rep_CaseLiteral:
+ case Rep_CaseString:
+ ret = 2;
+ break;
+ default:
+ return (0);
+ }
+ }
+
+ return (ret);
+}
+
+
+/* XXX If everything is a (case) byte, the pattern should be
+ * [abcde] instead of a|b|c|d|e (or [aAbBcCdDeE] instead of aA|bB|cC|dD|eE)
+ * as a string list works fine, but as a character range
+ * should be faster, and maybe could be converted here. But not
+ * very important, if performance is required, it should have already
+ * been done in the pattern.
+ */
+static int
+orec_str_list(orec_inf *inf, rec_alt *alt, int str, int count)
+{
+ rec_stl *stl;
+ rec_pat *pat;
+ rec_alt *ptr, *next;
+ int i, j, tlen, len, is;
+
+ if ((stl = calloc(1, sizeof(rec_stl))) == NULL)
+ return (inf->ecode = RE_ESPACE);
+
+ if ((stl->lens = malloc(sizeof(unsigned char) * count)) == NULL) {
+ free(stl);
+ return (inf->ecode = RE_ESPACE);
+ }
+
+ if ((stl->strs = malloc(sizeof(char*) * count)) == NULL) {
+ free(stl->lens);
+ free(stl);
+ return (inf->ecode = RE_ESPACE);
+ }
+
+ if ((pat = calloc(1, sizeof(rec_pat))) == NULL) {
+ free(stl->strs);
+ free(stl->lens);
+ free(stl);
+ return (inf->ecode = RE_ESPACE);
+ }
+
+ pat->data.stl = stl;
+ pat->type = Rep_StringList;
+ stl->type = str ? Resl_StringList : Resl_CaseStringList;
+ for (i = tlen = 0, ptr = alt; i < count; i++) {
+ next = ptr->next;
+ switch (ptr->pat->type) {
+ case Rep_Literal:
+ is = len = 1;
+ break;
+ case Rep_CaseLiteral:
+ is = len = 2;
+ break;
+ default:
+ is = 0;
+ len = strlen((char*)ptr->pat->data.str);
+ break;
+ }
+ tlen += len;
+ stl->lens[i] = len;
+ if (!is) {
+ if (len > 2)
+ stl->strs[i] = ptr->pat->data.str;
+ else {
+ if (len == 1)
+ stl->strs[i] = (void*)(long)(ptr->pat->data.str[0]);
+ else
+ stl->strs[i] = (void*)(long)
+ (ptr->pat->data.str[0] |
+ ((int)ptr->pat->data.str[1] << 8));
+ free(ptr->pat->data.str);
+ }
+ }
+ else {
+ if (is == 1)
+ stl->strs[i] = (void*)(long)ptr->pat->data.chr;
+ else
+ stl->strs[i] = (void*)(long)
+ (ptr->pat->data.cse.lower |
+ (ptr->pat->data.cse.upper << 8));
+ }
+ free(ptr->pat);
+ if (i)
+ free(ptr);
+ ptr = next;
+ }
+ stl->tlen = tlen;
+ stl->nstrs = count;
+
+ alt->pat = pat;
+ alt->next = NULL;
+
+ {
+ int li, lj;
+ unsigned char ci, cj, *str;
+
+ /* Don't need a stable sort, there shouldn't be duplicated strings,
+ * but don't check for it either. Only need to make sure that all
+ * strings that start with the same byte are together */
+ for (i = 0; i < count; i++) {
+ li = stl->lens[i];
+ ci = li > 2 ? stl->strs[i][0] : (long)stl->strs[i] & 0xff;
+ for (j = i + 1; j < count; j++) {
+ lj = stl->lens[j];
+ cj = lj > 2 ? stl->strs[j][0] : (long)stl->strs[j] & 0xff;
+ if ((count >= LARGE_STL_COUNT && cj < ci) ||
+ (cj == ci && lj > li)) {
+ /* If both strings start with the same byte,
+ * put the longer first */
+ str = stl->strs[j];
+ stl->strs[j] = stl->strs[i];
+ stl->strs[i] = str;
+ stl->lens[j] = li;
+ stl->lens[i] = lj;
+ li ^= lj; lj ^= li; li ^= lj;
+ ci ^= cj; cj ^= ci; ci ^= cj;
+ }
+ }
+ }
+ }
+
+ return (inf->ecode);
+}
diff --git a/lisp/re/rep.h b/lisp/re/rep.h
new file mode 100644
index 0000000..5e4d5d5
--- /dev/null
+++ b/lisp/re/rep.h
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/re/rep.h,v 1.3 2002/11/25 02:35:32 paulo Exp $ */
+
+#include "re.h"
+
+#ifndef _rep_h
+#define _rep_h
+
+/*
+ * Local defines
+ */
+
+#ifdef MIN
+#undef MIN
+#endif
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#ifdef MAX
+#undef MAX
+#endif
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+/* This value can not be larger than 255, a depth value is the nesting of
+ * repetition operations and alternatives. The number of nested parenthesis
+ * does not matter, but a repetition on the pattern inside the parenthesis
+ * does. Note also that you cannot have more than 9 parenthesis pairs in
+ * an expression.
+ * Depth is always at least 1. So for MAX_DEPTH 8, it is only allowed
+ * 7 complex repetitions. A complex repetition is a dot followed by an
+ * repetition operator. It is called a complex repetition because dot
+ * matches anything but the empty string, so the engine needs to test
+ * all possible combinations until the end of the string is found.
+ * Repetitions like .* use one depth until the end of the string is found,
+ * for example a.*b.*c.*d has depth 4, while a*b*c*d has depth 2.
+ */
+#define MAX_DEPTH 8
+
+/* Minimum number of strings to generate a "large" string list, that is,
+ * sort the strings and allocate 512 extra bytes to map the first string
+ * with a given initial byte. */
+#define LARGE_STL_COUNT 16
+
+/*
+ * Local types
+ */
+/* Intermediate compilation types declaration */
+ /* (r)egular (e)xpression (c)ompile (c)a(se) */
+typedef struct _rec_cse rec_cse;
+
+ /* (r)egular (e)xpression (c)ompile (r)a(ng)e */
+typedef struct _rec_rng rec_rng;
+
+ /* (r)egular (e)xpression (c)ompile (pat)tern */
+typedef struct _rec_pat rec_pat;
+
+ /* (r)egular (e)xpression (c)ompile (rep)etition */
+typedef struct _rec_rep rec_rep;
+
+ /* (r)egular (e)xpression (c)ompile (gr)ou(p) */
+typedef struct _rec_grp rec_grp;
+
+ /* (r)egular (e)xpression (c)ompile (alt)ernatives */
+typedef struct _rec_alt rec_alt;
+
+
+/* Optimization types */
+ /* (r)egular (e)xpression (c)ompile (st)ring (l)ist */
+typedef struct _rec_stl rec_stl;
+
+/* Final compilation and execution types */
+ /* (re)gular expression (inf)ormation */
+typedef struct _re_inf re_inf;
+
+ /* (re)gular expression (eng)ine */
+typedef struct _re_eng re_eng;
+
+
+/* Codes used by the engine */
+typedef enum {
+ /* Grouping */
+ Re_Open, /* ( */
+ Re_Close, /* ) */
+ Re_Update, /* Like Re_Close, but is inside a loop */
+
+ /* Alternatives */
+ Re_Alt, /* Start alternative list, + next offset */
+ Re_AltNext, /* Next alternative, + next offset */
+ Re_AltDone, /* Finish alternative list */
+
+ /* Repetition */
+ Re_AnyTimes, /* * */
+ Re_Maybe, /* ? */
+ Re_AtLeast, /* +, at least one */
+
+ /* Repetition like */
+ Re_AnyAnyTimes, /* .*<re> */
+ Re_AnyMaybe, /* .?<re> */
+ Re_AnyAtLeast, /* .+<re> */
+
+ Re_AnyEatAnyTimes, /* Expression ends with .* */
+ Re_AnyEatMaybe, /* Expression ends with .? */
+ Re_AnyEatAtLeast, /* Expression ends with .+ */
+
+ /* Repetition with arguments */
+ Re_Exact, /* {e} */
+ Re_Min, /* {n,} */
+ Re_Max, /* {,m} */
+ Re_MinMax, /* {n,m} */
+
+ /* Repetition helper instruction */
+ Re_RepJump, /* Special code, go back to repetition */
+ Re_RepLongJump, /* Jump needs two bytes */
+ /* After the repetition data, all repetitions have an offset
+ * to the code after the repetition */
+
+ /* Matching */
+ Re_Any, /* . */
+ Re_Odigit, /* \o */
+ Re_OdigitNot, /* \O */
+ Re_Digit, /* \d */
+ Re_DigitNot, /* \D */
+ Re_Xdigit, /* \x */
+ Re_XdigitNot, /* \x */
+ Re_Space, /* \s */
+ Re_SpaceNot, /* \S */
+ Re_Tab, /* \t */
+ Re_Newline, /* \n */
+ Re_Lower, /* \l */
+ Re_Upper, /* \u */
+ Re_Alnum, /* \w */
+ Re_AlnumNot, /* \W */
+ Re_Control, /* \c */
+ Re_ControlNot, /* \C */
+ Re_Bol, /* ^ */
+ Re_Eol, /* $ */
+ Re_Bow, /* \< */
+ Re_Eow, /* \> */
+
+ /* Range matching information */
+ Re_Range, /* + 256 bytes */
+ Re_RangeNot, /* + 256 bytes */
+
+ /* Matching with arguments */
+ Re_Literal, /* + character */
+ Re_CaseLiteral, /* + lower + upper */
+ Re_LiteralNot, /* + character */
+ Re_CaseLiteralNot, /* + lower + upper */
+ Re_String, /* + length + string */
+ Re_CaseString, /* + length + string in format lower-upper */
+
+ /* These are useful to start matching, or when RE_NOSPEC is used. */
+ Re_SearchLiteral,
+ Re_SearchCaseLiteral,
+ Re_SearchString,
+ Re_SearchCaseString,
+
+ Re_StringList, /* + total-length + lengths + strings */
+ Re_CaseStringList, /* + total-length + lengths + strings */
+
+ Re_LargeStringList, /* + total-length + lengths + map + strings */
+ Re_LargeCaseStringList, /* + total-length + lengths + map + strings */
+
+ /* Backreference */
+ Re_Backref, /* + reference number */
+
+ /* The last codes */
+ Re_DoneIf, /* Done if at end of input */
+ Re_MaybeDone, /* Done */
+ Re_Done /* If this code found, finished execution */
+} ReCode;
+
+
+/* (r)egular (e)xpresssion (pat)rern (t)ype */
+typedef enum _rec_pat_t {
+ Rep_Literal = Re_Literal,
+ Rep_CaseLiteral = Re_CaseLiteral,
+ Rep_LiteralNot = Re_LiteralNot,
+ Rep_CaseLiteralNot = Re_CaseLiteralNot,
+ Rep_Range = Re_Range,
+ Rep_RangeNot = Re_RangeNot,
+ Rep_String = Re_String,
+ Rep_CaseString = Re_CaseString,
+ Rep_SearchLiteral = Re_SearchLiteral,
+ Rep_SearchCaseLiteral = Re_SearchCaseLiteral,
+ Rep_SearchString = Re_SearchString,
+ Rep_SearchCaseString = Re_SearchCaseString,
+ Rep_Any = Re_Any,
+ Rep_AnyAnyTimes = Re_AnyAnyTimes,
+ Rep_AnyEatAnyTimes = Re_AnyEatAnyTimes,
+ Rep_AnyMaybe = Re_AnyMaybe,
+ Rep_AnyEatMaybe = Re_AnyEatMaybe,
+ Rep_AnyAtLeast = Re_AnyAtLeast,
+ Rep_AnyEatAtLeast = Re_AnyEatAtLeast,
+ Rep_Odigit = Re_Odigit,
+ Rep_OdigitNot = Re_OdigitNot,
+ Rep_Digit = Re_Digit,
+ Rep_DigitNot = Re_DigitNot,
+ Rep_Xdigit = Re_Xdigit,
+ Rep_XdigitNot = Re_XdigitNot,
+ Rep_Space = Re_Space,
+ Rep_SpaceNot = Re_SpaceNot,
+ Rep_Tab = Re_Tab,
+ Rep_Newline = Re_Newline,
+ Rep_Lower = Re_Lower,
+ Rep_Upper = Re_Upper,
+ Rep_Alnum = Re_Alnum,
+ Rep_AlnumNot = Re_AlnumNot,
+ Rep_Control = Re_Control,
+ Rep_ControlNot = Re_ControlNot,
+ Rep_Bol = Re_Bol,
+ Rep_Eol = Re_Eol,
+ Rep_Bow = Re_Bow,
+ Rep_Eow = Re_Eow,
+ Rep_Backref = Re_Backref,
+ Rep_StringList = Re_StringList,
+ Rep_Group = Re_Open
+} rec_pat_t;
+
+
+/* (r)egular (e)xpression (rep)etition (t)ype */
+typedef enum _rec_rep_t {
+ Rer_AnyTimes = Re_AnyTimes,
+ Rer_AtLeast = Re_AtLeast,
+ Rer_Maybe = Re_Maybe,
+ Rer_Exact = Re_Exact,
+ Rer_Min = Re_Min,
+ Rer_Max = Re_Max,
+ Rer_MinMax = Re_MinMax
+} rec_rep_t;
+
+
+/* Decide at re compilation time what is lowercase and what is uppercase */
+struct _rec_cse {
+ unsigned char lower;
+ unsigned char upper;
+};
+
+
+/* A rec_rng is used only during compilation, just a character map */
+struct _rec_rng {
+ unsigned char range[256];
+};
+
+
+/* A rec_pat is used only during compilation, and can be viewed as
+ * a regular expression element like a match to any character, a match
+ * to the beginning or end of the line, etc.
+ * It is implemented as a linked list, and does not have nesting.
+ * The data field can contain:
+ * chr: the value of a single character to match.
+ * cse: the upper and lower case value of a character to match.
+ * rng: a character map to match or not match.
+ * str: a simple string or a string where every two bytes
+ * represents the character to match, in lower/upper
+ * case sequence.
+ * The rep field is not used for strings, strings are broken in the
+ * last character in this case. That is, strings are just a concatenation
+ * of several character matches.
+ */
+struct _rec_pat {
+ rec_pat_t type;
+ rec_pat *next, *prev; /* Linked list information */
+ union {
+ unsigned char chr;
+ rec_cse cse;
+ rec_rng *rng;
+ rec_grp *grp;
+ unsigned char *str;
+ rec_stl *stl;
+ } data;
+ rec_rep *rep; /* Pattern repetition information */
+};
+
+
+/* A rec_rep is used only during compilation, and can be viewed as:
+ *
+ * ? or * or + or {<e>} or {<m>,} or {,<M>} or {<m>,<M>}
+ *
+ * where <e> is "exact", <m> is "minimum" and <M> is "maximum".
+ * In the compiled step it can also be just a NULL pointer, that
+ * is actually equivalent to {1}.
+ */
+struct _rec_rep {
+ rec_rep_t type;
+ short mine; /* minimum or exact number of matches */
+ short maxc; /* maximum number of matches */
+};
+
+
+/* A rec_alt is used only during compilation, and can be viewed as:
+ *
+ * <re>|<re>
+ *
+ * where <re> is any regular expression. The expressions are nested
+ * using the grp field of the rec_pat structure.
+ */
+struct _rec_alt {
+ rec_alt *next, *prev; /* Linked list information */
+ rec_pat *pat;
+};
+
+
+/* A rec_grp is a place holder for expressions enclosed in parenthesis
+ * and is linked to the compilation data by an rec_pat structure. */
+struct _rec_grp {
+ rec_pat *parent; /* Reference to parent pattern */
+ rec_alt *alt; /* The pattern information */
+ rec_alt *palt; /* Parent alternative */
+ rec_grp *pgrp; /* Nested groups */
+ int comp; /* (comp)lex repetition pattern inside group */
+};
+
+
+/* Optimization compilation types definition */
+ /* (r)egular (e)xpression (c)ompile (st)ring (l)ist (t)ype */
+typedef enum {
+ Resl_StringList = Re_StringList,
+ Resl_CaseStringList = Re_CaseStringList
+} rec_stl_t;
+
+struct _rec_stl {
+ rec_stl_t type;
+ int nstrs; /* Number of strings in list */
+ int tlen; /* Total length of all strings */
+ unsigned char *lens; /* Vector of string lengths */
+ unsigned char **strs; /* The strings */
+};
+
+
+/*
+ * Prototypes
+ */
+ /* rep.c */
+rec_alt *irec_comp(const char*, const char*, int, int*);
+void irec_free_alt(rec_alt*);
+
+ /* reo.c */
+int orec_comp(rec_alt*, int);
+void orec_free_stl(rec_stl*);
+
+#endif /* _rep_h */
diff --git a/lisp/re/tests.c b/lisp/re/tests.c
new file mode 100644
index 0000000..bd5c55d
--- /dev/null
+++ b/lisp/re/tests.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/re/tests.c,v 1.1 2002/09/08 02:29:50 paulo Exp $ */
+
+/*
+ * Compile with: cc -o tests tests.c -L. -lre
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "re.h"
+
+int
+main(int argc, char *argv[])
+{
+ re_cod cod;
+ re_mat mat[10];
+ int line, ecode, i, len, group, failed;
+ long eo, so;
+ char buf[8192];
+ char str[8192];
+ FILE *fp = fopen("tests.txt", "r");
+
+ if (fp == NULL) {
+ fprintf(stderr, "failed to open tests.txt\n");
+ exit(1);
+ }
+
+ ecode = line = group = failed = 0;
+ cod.cod = NULL;
+ while (fgets(buf, sizeof(buf), fp)) {
+ ++line;
+ if (buf[0] == '#' || buf[0] == '\n')
+ continue;
+ else if (buf[0] == '/') {
+ char *ptr = strrchr(buf, '/');
+
+ if (ptr == buf) {
+ fprintf(stderr, "syntax error at line %d\n", line);
+ break;
+ }
+ else {
+ int flags = 0;
+
+ refree(&cod);
+ for (*ptr++ = '\0'; *ptr; ptr++) {
+ if (*ptr == 'i')
+ flags |= RE_ICASE;
+ else if (*ptr == 'n')
+ flags |= RE_NEWLINE;
+ }
+ ecode = recomp(&cod, buf + 1, flags);
+ failed = ecode;
+ }
+ }
+ else if (buf[0] == '>') {
+ if (cod.cod == NULL) {
+ fprintf(stderr, "no previous pattern at line %d\n", line);
+ break;
+ }
+ len = strlen(buf) - 1;
+ buf[len] = '\0';
+ strcpy(str, buf + 1);
+ for (i = 0, --len; i < len - 1; i++) {
+ if (str[i] == '\\') {
+ memmove(str + i, str + i + 1, len);
+ --len;
+ switch (str[i]) {
+ case 'a':
+ str[i] = '\a';
+ break;
+ case 'b':
+ str[i] = '\b';
+ break;
+ case 'f':
+ str[i] = '\f';
+ break;
+ case 'n':
+ str[i] = '\n';
+ break;
+ case 'r':
+ str[i] = '\r';
+ break;
+ case 't':
+ str[i] = '\t';
+ break;
+ case 'v':
+ str[i] = '\v';
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ group = 0;
+ ecode = reexec(&cod, str, 10, &mat[0], 0);
+ if (ecode && ecode != RE_NOMATCH) {
+ reerror(failed, &cod, buf, sizeof(buf));
+ fprintf(stderr, "%s, at line %d\n", buf, line);
+ break;
+ }
+ }
+ else if (buf[0] == ':') {
+ if (failed) {
+ len = strlen(buf) - 1;
+ buf[len] = '\0';
+ if (failed == RE_EESCAPE && strcmp(buf, ":EESCAPE") == 0)
+ continue;
+ if (failed == RE_ESUBREG && strcmp(buf, ":ESUBREG") == 0)
+ continue;
+ if (failed == RE_EBRACK && strcmp(buf, ":EBRACK") == 0)
+ continue;
+ if (failed == RE_EPAREN && strcmp(buf, ":EPAREN") == 0)
+ continue;
+ if (failed == RE_EBRACE && strcmp(buf, ":EBRACE") == 0)
+ continue;
+ if (failed == RE_EBADBR && strcmp(buf, ":EBADBR") == 0)
+ continue;
+ if (failed == RE_ERANGE && strcmp(buf, ":ERANGE") == 0)
+ continue;
+ if (failed == RE_ESPACE && strcmp(buf, ":ESPACE") == 0)
+ continue;
+ if (failed == RE_BADRPT && strcmp(buf, ":BADRPT") == 0)
+ continue;
+ if (failed == RE_EMPTY && strcmp(buf, ":EMPTY") == 0)
+ continue;
+ reerror(failed, &cod, buf, sizeof(buf));
+ fprintf(stderr, "Error value %d doesn't match: %s, at line %d\n",
+ failed, buf, line);
+ break;
+ }
+ else if (!ecode) {
+ fprintf(stderr, "found match when shoudn't, at line %d\n", line);
+ break;
+ }
+ }
+ else {
+ if (failed) {
+ reerror(failed, &cod, buf, sizeof(buf));
+ fprintf(stderr, "%s, at line %d\n", line);
+ break;
+ }
+ if (sscanf(buf, "%ld,%ld:", &so, &eo) != 2) {
+ fprintf(stderr, "expecting match offsets at line %d\n", line);
+ break;
+ }
+ else if (ecode) {
+ fprintf(stderr, "didn't match, at line %d\n", line);
+ break;
+ }
+ else if (group >= 10) {
+ fprintf(stderr, "syntax error at line %d (too many groups)\n",
+ line);
+ break;
+ }
+ else if (so != mat[group].rm_so || eo != mat[group].rm_eo) {
+ fprintf(stderr, "match failed at line %d, got %ld,%ld: ",
+ line, mat[group].rm_so, mat[group].rm_eo);
+ if (mat[group].rm_so < mat[group].rm_eo)
+ fwrite(str + mat[group].rm_so,
+ mat[group].rm_eo - mat[group].rm_so, 1, stderr);
+ fputc('\n', stderr);
+ break;
+ }
+ ++group;
+ }
+ }
+
+ fclose(fp);
+
+ return (ecode);
+}
diff --git a/lisp/re/tests.txt b/lisp/re/tests.txt
new file mode 100644
index 0000000..e3da032
--- /dev/null
+++ b/lisp/re/tests.txt
@@ -0,0 +1,461 @@
+#
+# Copyright (c) 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.
+#
+# Author: Paulo César Pereira de Andrade
+#
+#
+# $XFree86: xc/programs/xedit/lisp/re/tests.txt,v 1.3 2002/11/08 08:01:00 paulo Exp $
+
+# Some tests for the library:
+# lines starting with # are comments
+# lines starting with / are a regular expression pattern
+# The pattern must end with / and may be followed by:
+# i -> ignore case
+# n -> create newline sensitive regex
+# lines starting with > are a string input to the last pattern
+# To test newline sensitive matching, add \n to the string.
+# lines starting with a number are the expected result
+# If more than one line, every subsequent line is the
+# value of an "subresult".
+# :NOMATCH means that the string input should not match
+
+# Simple string
+/abc/
+>abc
+0,3: abc
+>aaaaaaaaaaaaaaabc
+14,17: abc
+>xxxxxxxxxxxxxxaaaaaaaaaaaaaaaaabcxx
+30,33: abc
+
+# String list
+/abc|bcd|cde/
+>abc
+0,3: abc
+>aabc
+1,4: abc
+>xxxbcdef
+3,6: bcd
+>abdzzzcdabcde
+8,11: abc
+>xxxxabdecdabdcde
+13,16: cde
+
+# Complex string
+/a?bc|ab?c|abc?/
+>abc
+0,3: abc
+>xxxb
+:NOMATCH
+>xxxbc
+3,5: bc
+>sssssab
+5,7: ab
+
+# Another complex string
+/a*bc|ab*c|abc*/
+>aaaaaaabc
+0,9: aaaaaaabc
+>xaaaaaaabc
+1,10: aaaaaaabc
+>xyzaaaaaaabc
+3,12: aaaaaaabc
+>abbc
+0,4: abbc
+>xxabbbbbc
+2,9: abbbbbc
+>abcccccccccc
+0,12: abcccccccccc
+>abccccccccccd
+0,12: abcccccccccc
+>xxxxxxxaaaaaaaaaabbbbbbbbbbbccccccccccc
+16,29: abbbbbbbbbbbc
+>xxxbbbbbbbbbc
+11,13: bc
+
+# Another complex string
+/a+bc|ab+c|abc+/
+>xxxbc
+:NOMATCH
+>xaaabc
+1,6: aaabc
+>zzzzaaaaabbc
+8,12: abbc
+>zzzzaaaabbbbbbcccc
+7,15: abbbbbbc
+
+# Simple pattern
+/a.c/
+>abc
+0,3: abc
+>aaac
+1,4: aac
+>xac
+:NOMATCH
+>xaac
+1,4: aac
+>xxabc
+2,5: abc
+>xxxaxc
+3,6: axc
+
+# Another simple pattern
+/a*c/
+>c
+0,1: c
+>xxxxxxxxc
+8,9: c
+>xxxxxxxcc
+7,8: c
+>ac
+0,2: ac
+>aaaac
+0,5: aaaac
+>xac
+1,3: ac
+>xxxaac
+3,6: aac
+>xxac
+2,4: ac
+>xxxxac
+4,6: ac
+
+# Another simple pattern
+/a+c/
+>xxaac
+2,5: aac
+>xxxaaaac
+3,8: aaaac
+>xaaaabac
+6,8: ac
+>xxxc
+:NOMATCH
+>xxxxaaaaccc
+4,9: aaaac
+
+# Another simple pattern
+/a{4}b/
+>xabxxaabxxxaaabxxxxaaaab
+19,24: aaaab
+>aaabaaaab
+4,9: aaaab
+
+# Another simple pattern
+/a{4,}b/
+>xxxaaaab
+3,8: aaaab
+>zaaabzzzaaaaaaaaaaaaaaaab
+8,25: aaaaaaaaaaaaaaaab
+
+# Another simple pattern
+/a{,4}b/
+>b
+0,1: b
+>xxxxxxxxb
+8,9: b
+>xaaaaaaaaab
+6,11: aaaab
+>xxxab
+3,5: ab
+>aaaaaxaaab
+6,10: aaab
+
+# Another simple pattern
+/a{2,4}b/
+>xab
+:NOMATCH
+>xaab
+1,4: aab
+>xaaab
+1,5: aaab
+>xxaaaab
+2,7: aaaab
+>xxxaaaaab
+4,9: aaaab
+
+# Some simple grouping tests
+/foo(bar|baz)fee/
+>feebarbazfoobarfee
+9,18: foobarfee
+12,15: bar
+>foofooobazfeefoobazfee
+13,22: foobazfee
+/f(oo|ee)ba[rz]/
+>barfoebaz
+:NOMATCH
+>bazfoobar
+3,9: foobar
+4,6: oo
+>barfeebaz
+3,9: feebaz
+4,6: ee
+/\<(int|char)\>/
+>aint character int foo
+15,18: int
+15,18: int
+
+# Some complex repetitions
+/foo.*bar/
+>barfoblaboofoobarfoobarfoobar
+11,17: foobar
+/foo.+bar/
+>foobar
+:NOMATCH
+>fobbarfooxbarfooybar
+6,13: fooxbar
+/foo.?bar/
+>xfoobar
+1,7: foobar
+>xxfooxxbar
+:NOMATCH
+>yyyfootbar
+3,10: footbar
+
+# Some nested complex repetitions
+/a.*b.*c/
+>abc
+0,3: abc
+>xxxxxxxxxabbbbbbbccaaaaabbbc
+9,18: abbbbbbbc
+/a.+b.*c/
+>xxxabc
+:NOMATCH
+>xxaxbbc
+2,7: axbbc
+/a.+b.?c/
+>xaabc
+1,5: aabc
+>xxaabbc
+2,7: aabbc
+
+# Very complex repetitions
+/(foo.*|bar)fee/
+# XXX NOTE
+# This pattern does not return the correct offset for the group.
+# Support for this may and may not be added.
+
+>barfoofee
+3,9: foofee
+>foobarfee
+0,9: foobarfee
+>xxfobarfee
+4,10: barfee
+>barfooooooobarfee
+3,17: fooooooobarfee
+>xxfobarfeefoobar
+4,10: barfee
+/(foo.+|bar)fee/
+>barfoofee
+:NOMATCH
+>barfooxfee
+3,10: fooxfee
+/(foo.?|bar)fee/
+>foobar
+:NOMATCH
+>bafoofee
+2,8:foofee
+>bafooofeebarfee
+2,9: fooofee
+>bafoofeebarfee
+2,8: foofee
+
+# Simple backreference
+/(a|b|c)\1/
+>aa
+0,2: aa
+0,1: a
+/(a|b|c)(a|b|c)\1\2/
+>acac
+0,4: acac
+0,1: a
+1,2: c
+>xxxxacac
+4,8: acac
+4,5: a
+5,6: c
+>xxacabacbcacbbacbcaaccabcaca
+24,28: caca
+24,25: c
+25,26: a
+>xyabcccc
+4,8: cccc
+4,5: c
+5,6: c
+
+# Complex backreference
+/(a*b)\1/
+>xxxaaaaabaaaaab
+3,15: aaaaabaaaaab
+3,9: aaaaab
+/(ab+c)\1/
+>xaaabbbcabbbc
+3,13: abbbcabbbc
+3,8: abbbc
+/(ab?c)\1/
+>abcac
+:NOMATCH
+>abcacabcabc
+5,11: abcabc
+5,8: abc
+>abcacac
+3,7: acac
+3,5: acac
+
+# Very complex backreference
+/a(.*)b\1/
+>xxxab
+3,5: ab
+4,4:
+>xxxxazzzbzzz
+4,12: azzzbzzz
+5,8: zzz
+
+# Case testing
+/abc/i
+>AbC
+0,3: AbC
+/[0-9][a-z]+/i
+>xxx0aaZxYT9
+3,10: 0aaZxYT
+/a.b/i
+>aaaaaaaaaaaxB
+10,13: axB
+/a.*z/i
+>xxxAaaaaZ
+3,9: AaaaaZ
+>xxaaaZaaa
+2,6: aaaZ
+/\<(lambda|defun|defmacro)\>/i
+> (lambda
+5,11: lambda
+5,11: lambda
+/\<(nil|t)\>/i
+>it Nil
+3,6: Nil
+3,6: Nil
+/\<(begin|end)\>/i
+>beginning the ending EnD
+21,24: EnD
+21,24: EnD
+
+# Some newline tests
+/a.*/n
+>a\naaa
+0,1:a
+>xyza\naa
+3,4: a
+/a.+/n
+>a\naaa
+2,5: aaa
+>xyza\naa
+5,7: aa
+/a.?/n
+>a\naaa
+0,1: a
+>xyza\naa
+3,4: a
+
+# Newline tests envolving complex patterns
+/a.*b.*c/n
+>xxaa\nzyacb\nabc
+11,14: abc
+>xxxab\nabc\nc
+6,9: abc
+/a.+b.*c/n
+>ab\nbc\nabbc
+6,10: abbc
+/a.?b.*c/n
+>ab\ncabbc\ncc
+4,8: abbc
+/^foo$/n
+>bar\nfoobar\nfoo
+11,14: foo
+
+# Not so complex test involving a newline...
+/^\s*#\s*(define|include)\s+.+/n
+>#define\n#include x
+8,18: #include x
+9,16: include
+
+# Check if large strings are working
+/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/
+>zzzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzz
+3,259: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~/
+>String here: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~/
+13,333: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~
+
+
+# Some complex repetitions not supported
+# Listed here only to make sure the library is not crashing on these
+# Repetitions that match an empty match, or an empty string cannot follow
+# a complex repetition. A complex repetition is:
+# .* or .+ or .?
+# .{...} is not supported.
+/(.*)(\d*)/
+:BADRPT
+/(.*).(\d*)/
+:BADRPT
+/(.*)\<(\d*)/
+:BADRPT
+/(.*)\s(\d*)/
+:BADRPT
+/(.*)\D(\d*)/
+:BADRPT
+
+# This is a more clear pattern and partially works
+/(.*)\D(\d+)/
+>abcW12
+0,6: abcW12
+0,3: abc
+4,6: 12
+>abcW12abcW12
+0,6: abcW12
+0,3: abc
+4,6: 12
+# This wasn't working in the previous version, but now with only minimal
+# matches supported, it works.
+>abcW12abcW12a
+0,6: abcW12
+0,3: abc
+4,6: 12
+
+# Note the minimal match
+/.*\d/
+>a1a1a1aaaaaaa
+0,2: a1
+# Check match offsets
+/(.*)\d/
+>a1a1a1aaaaaaa
+0,2: a1
+0,1: a
+/.*(\d)/
+>a1a1a1aaaaaaa
+0,2: a1
+1,2: 1
+
+/.*(\d+)/
+:BADRPT
diff --git a/lisp/read.c b/lisp/read.c
new file mode 100644
index 0000000..b8872a2
--- /dev/null
+++ b/lisp/read.c
@@ -0,0 +1,2058 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/read.c,v 1.34 2003/01/13 03:57:58 paulo Exp $ */
+
+#include <errno.h>
+#include "read.h"
+#include "package.h"
+#include "write.h"
+#include <fcntl.h>
+#include <stdarg.h>
+
+/* This should be visible only in read.c, but if an error is generated,
+ * the current code in write.c will print it as #<ERROR> */
+#define LABEL_BIT_COUNT 8
+#define LABEL_BIT_MASK 0xff
+#define MAX_LABEL_VALUE ((1L << (sizeof(long) * 8 - 9)) - 1)
+#define READLABEL(label) \
+ (LispObj*)(((label) << LABEL_BIT_COUNT) | READLABEL_MASK)
+#define READLABELP(object) \
+ (((unsigned long)(object) & LABEL_BIT_MASK) == READLABEL_MASK)
+#define READLABEL_VALUE(object) \
+ ((long)(object) >> LABEL_BIT_COUNT)
+
+#define READ_ENTER() \
+ LispObj *read__stream = SINPUT; \
+ int read__line = LispGetLine(read__stream)
+#define READ_ERROR0(format) \
+ LispReadError(read__stream, read__line, format)
+#define READ_ERROR1(format, arg1) \
+ LispReadError(read__stream, read__line, format, arg1)
+#define READ_ERROR2(format, arg1, arg2) \
+ LispReadError(read__stream, read__line, format, arg1, arg2)
+
+#define READ_ERROR_EOF() READ_ERROR0("unexpected end of input")
+#define READ_ERROR_FIXNUM() READ_ERROR0("number is not a fixnum")
+#define READ_ERROR_INVARG() READ_ERROR0("invalid argument")
+
+/*
+ * Types
+ */
+typedef struct _object_info {
+ long label; /* the read label of this object */
+ LispObj *object; /* the resulting object */
+ long num_circles; /* references to object before it was completely read */
+} object_info;
+
+typedef struct _read_info {
+ int level; /* level of open parentheses */
+
+ int nodot; /* flag set when reading a "special" list */
+
+ int discard; /* flag used when reading an unavailable feature */
+
+ long circle_count; /* if non zero, must resolve some labels */
+
+ /* information for #<number>= and #<number># */
+ object_info *objects;
+ long num_objects;
+
+ /* could use only the objects field as all circular data is known,
+ * but check every object so that circular/shared references generated
+ * by evaluations would not cause an infinite loop at read time */
+ LispObj **circles;
+ long num_circles;
+} read_info;
+
+/*
+ * Protypes
+ */
+static LispObj *LispReadChar(LispBuiltin*, int);
+
+static int LispGetLine(LispObj*);
+#ifdef __GNUC__
+#define PRINTF_FORMAT __attribute__ ((format (printf, 3, 4)))
+#else
+#define PRINTF_FORMAT /**/
+#endif
+static void LispReadError(LispObj*, int, char*, ...);
+#undef PRINTF_FORMAT
+static void LispReadFixCircle(LispObj*, read_info*);
+static LispObj *LispReadLabelCircle(LispObj*, read_info*);
+static int LispReadCheckCircle(LispObj*, read_info*);
+static LispObj *LispDoRead(read_info*);
+static int LispSkipWhiteSpace(void);
+static LispObj *LispReadList(read_info*);
+static LispObj *LispReadQuote(read_info*);
+static LispObj *LispReadBackquote(read_info*);
+static LispObj *LispReadCommaquote(read_info*);
+static LispObj *LispReadObject(int, read_info*);
+static LispObj *LispParseAtom(char*, char*, int, int, LispObj*, int);
+static LispObj *LispParseNumber(char*, int, LispObj*, int);
+static int StringInRadix(char*, int, int);
+static int AtomSeparator(int, int, int);
+static LispObj *LispReadVector(read_info*);
+static LispObj *LispReadMacro(read_info*);
+static LispObj *LispReadFunction(read_info*);
+static LispObj *LispReadRational(int, read_info*);
+static LispObj *LispReadCharacter(read_info*);
+static void LispSkipComment(void);
+static LispObj *LispReadEval(read_info*);
+static LispObj *LispReadComplex(read_info*);
+static LispObj *LispReadPathname(read_info*);
+static LispObj *LispReadStruct(read_info*);
+static LispObj *LispReadMacroArg(read_info*);
+static LispObj *LispReadArray(long, read_info*);
+static LispObj *LispReadFeature(int, read_info*);
+static LispObj *LispEvalFeature(LispObj*);
+
+/*
+ * Initialization
+ */
+static char *Char_Nul[] = {"Null", "Nul", NULL};
+static char *Char_Soh[] = {"Soh", NULL};
+static char *Char_Stx[] = {"Stx", NULL};
+static char *Char_Etx[] = {"Etx", NULL};
+static char *Char_Eot[] = {"Eot", NULL};
+static char *Char_Enq[] = {"Enq", NULL};
+static char *Char_Ack[] = {"Ack", NULL};
+static char *Char_Bel[] = {"Bell", "Bel", NULL};
+static char *Char_Bs[] = {"Backspace", "Bs", NULL};
+static char *Char_Tab[] = {"Tab", NULL};
+static char *Char_Nl[] = {"Newline", "Nl", "Lf", "Linefeed", NULL};
+static char *Char_Vt[] = {"Vt", NULL};
+static char *Char_Np[] = {"Page", "Np", NULL};
+static char *Char_Cr[] = {"Return", "Cr", NULL};
+static char *Char_Ff[] = {"So", "Ff", NULL};
+static char *Char_Si[] = {"Si", NULL};
+static char *Char_Dle[] = {"Dle", NULL};
+static char *Char_Dc1[] = {"Dc1", NULL};
+static char *Char_Dc2[] = {"Dc2", NULL};
+static char *Char_Dc3[] = {"Dc3", NULL};
+static char *Char_Dc4[] = {"Dc4", NULL};
+static char *Char_Nak[] = {"Nak", NULL};
+static char *Char_Syn[] = {"Syn", NULL};
+static char *Char_Etb[] = {"Etb", NULL};
+static char *Char_Can[] = {"Can", NULL};
+static char *Char_Em[] = {"Em", NULL};
+static char *Char_Sub[] = {"Sub", NULL};
+static char *Char_Esc[] = {"Escape", "Esc", NULL};
+static char *Char_Fs[] = {"Fs", NULL};
+static char *Char_Gs[] = {"Gs", NULL};
+static char *Char_Rs[] = {"Rs", NULL};
+static char *Char_Us[] = {"Us", NULL};
+static char *Char_Sp[] = {"Space", "Sp", NULL};
+static char *Char_Del[] = {"Rubout", "Del", "Delete", NULL};
+
+LispCharInfo LispChars[256] = {
+ {Char_Nul},
+ {Char_Soh},
+ {Char_Stx},
+ {Char_Etx},
+ {Char_Eot},
+ {Char_Enq},
+ {Char_Ack},
+ {Char_Bel},
+ {Char_Bs},
+ {Char_Tab},
+ {Char_Nl},
+ {Char_Vt},
+ {Char_Np},
+ {Char_Cr},
+ {Char_Ff},
+ {Char_Si},
+ {Char_Dle},
+ {Char_Dc1},
+ {Char_Dc2},
+ {Char_Dc3},
+ {Char_Dc4},
+ {Char_Nak},
+ {Char_Syn},
+ {Char_Etb},
+ {Char_Can},
+ {Char_Em},
+ {Char_Sub},
+ {Char_Esc},
+ {Char_Fs},
+ {Char_Gs},
+ {Char_Rs},
+ {Char_Us},
+ {Char_Sp},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {Char_Del},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
+ {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL}
+
+};
+
+Atom_id Sand, Sor, Snot;
+
+
+/*
+ * Implementation
+ */
+LispObj *
+Lisp_Read(LispBuiltin *builtin)
+/*
+ read &optional input-stream eof-error-p eof-value recursive-p
+ */
+{
+ LispObj *result;
+
+ LispObj *input_stream, *eof_error_p, *eof_value, *recursive_p;
+
+ recursive_p = ARGUMENT(3);
+ eof_value = ARGUMENT(2);
+ eof_error_p = ARGUMENT(1);
+ input_stream = ARGUMENT(0);
+
+ if (input_stream == UNSPEC)
+ input_stream = NIL;
+ else if (input_stream != NIL) {
+ CHECK_STREAM(input_stream);
+ else if (!input_stream->data.stream.readable)
+ LispDestroy("%s: stream %s is not readable",
+ STRFUN(builtin), STROBJ(input_stream));
+ LispPushInput(input_stream);
+ }
+ else if (CONSP(lisp__data.input_list)) {
+ input_stream = STANDARD_INPUT;
+ LispPushInput(input_stream);
+ }
+
+ if (eof_value == UNSPEC)
+ eof_value = NIL;
+
+ result = LispRead();
+ if (input_stream != NIL)
+ LispPopInput(input_stream);
+
+ if (result == NULL) {
+ if (eof_error_p != NIL)
+ LispDestroy("%s: EOF reading stream %s",
+ STRFUN(builtin), STROBJ(input_stream));
+ else
+ result = eof_value;
+ }
+
+ return (result);
+}
+
+static LispObj *
+LispReadChar(LispBuiltin *builtin, int nohang)
+{
+ int character;
+ LispObj *result;
+
+ LispObj *input_stream, *eof_error_p, *eof_value, *recursive_p;
+
+ recursive_p = ARGUMENT(3);
+ eof_value = ARGUMENT(2);
+ eof_error_p = ARGUMENT(1);
+ input_stream = ARGUMENT(0);
+
+ if (input_stream == UNSPEC)
+ input_stream = NIL;
+ else if (input_stream != NIL) {
+ CHECK_STREAM(input_stream);
+ }
+ else
+ input_stream = lisp__data.input;
+
+ if (eof_value == UNSPEC)
+ eof_value = NIL;
+
+ result = NIL;
+ character = EOF;
+
+ if (input_stream->data.stream.readable) {
+ LispFile *file = NULL;
+
+ switch (input_stream->data.stream.type) {
+ case LispStreamStandard:
+ case LispStreamFile:
+ file = FSTREAMP(input_stream);
+ break;
+ case LispStreamPipe:
+ file = IPSTREAMP(input_stream);
+ break;
+ case LispStreamString:
+ character = LispSgetc(SSTREAMP(input_stream));
+ break;
+ default:
+ break;
+ }
+ if (file != NULL) {
+ if (file->available || file->offset < file->length)
+ character = LispFgetc(file);
+ else {
+ if (nohang && !file->nonblock) {
+ if (fcntl(file->descriptor, F_SETFL, O_NONBLOCK) < 0)
+ LispDestroy("%s: fcntl(%d): %s",
+ STRFUN(builtin), file->descriptor,
+ strerror(errno));
+ file->nonblock = 1;
+ }
+ else if (!nohang && file->nonblock) {
+ if (fcntl(file->descriptor, F_SETFL, 0) < 0)
+ LispDestroy("%s: fcntl(%d): %s",
+ STRFUN(builtin), file->descriptor,
+ strerror(errno));
+ file->nonblock = 0;
+ }
+ if (nohang) {
+ unsigned char ch;
+
+ if (read(file->descriptor, &ch, 1) == 1)
+ character = ch;
+ else if (errno == EAGAIN)
+ return (NIL); /* XXX no character available */
+ else
+ character = EOF;
+ }
+ else
+ character = LispFgetc(file);
+ }
+ }
+ }
+ else
+ LispDestroy("%s: stream %s is unreadable",
+ STRFUN(builtin), STROBJ(input_stream));
+
+ if (character == EOF) {
+ if (eof_error_p != NIL)
+ LispDestroy("%s: EOF reading stream %s",
+ STRFUN(builtin), STROBJ(input_stream));
+
+ return (eof_value);
+ }
+
+ return (SCHAR(character));
+}
+
+LispObj *
+Lisp_ReadChar(LispBuiltin *builtin)
+/*
+ read-char &optional input-stream eof-error-p eof-value recursive-p
+ */
+{
+ return (LispReadChar(builtin, 0));
+}
+
+LispObj *
+Lisp_ReadCharNoHang(LispBuiltin *builtin)
+/*
+ read-char-no-hang &optional input-stream eof-error-p eof-value recursive-p
+ */
+{
+ return (LispReadChar(builtin, 1));
+}
+
+LispObj *
+Lisp_ReadLine(LispBuiltin *builtin)
+/*
+ read-line &optional input-stream eof-error-p eof-value recursive-p
+ */
+{
+ char *string;
+ int ch, length;
+ LispObj *result, *status = NIL;
+
+ LispObj *input_stream, *eof_error_p, *eof_value, *recursive_p;
+
+ recursive_p = ARGUMENT(3);
+ eof_value = ARGUMENT(2);
+ eof_error_p = ARGUMENT(1);
+ input_stream = ARGUMENT(0);
+
+ if (input_stream == UNSPEC)
+ input_stream = NIL;
+ else if (input_stream == NIL)
+ input_stream = STANDARD_INPUT;
+ else {
+ CHECK_STREAM(input_stream);
+ }
+
+ if (eof_value == UNSPEC)
+ eof_value = NIL;
+
+ result = NIL;
+ string = NULL;
+ length = 0;
+
+ if (!input_stream->data.stream.readable)
+ LispDestroy("%s: stream %s is unreadable",
+ STRFUN(builtin), STROBJ(input_stream));
+ if (input_stream->data.stream.type == LispStreamString) {
+ char *start, *end, *ptr;
+
+ if (SSTREAMP(input_stream)->input >=
+ SSTREAMP(input_stream)->length) {
+ if (eof_error_p != NIL)
+ LispDestroy("%s: EOS found reading %s",
+ STRFUN(builtin), STROBJ(input_stream));
+
+ status = T;
+ result = eof_value;
+ goto read_line_done;
+ }
+
+ start = SSTREAMP(input_stream)->string +
+ SSTREAMP(input_stream)->input;
+ end = SSTREAMP(input_stream)->string +
+ SSTREAMP(input_stream)->length;
+ /* Search for a newline */
+ for (ptr = start; *ptr != '\n' && ptr < end; ptr++)
+ ;
+ if (ptr == end)
+ status = T;
+ else if (!SSTREAMP(input_stream)->binary)
+ ++SSTREAMP(input_stream)->line;
+ length = ptr - start;
+ string = LispMalloc(length + 1);
+ memcpy(string, start, length);
+ string[length] = '\0';
+ result = LSTRING2(string, length);
+ /* macro LSTRING2 does not make a copy of it's arguments, and
+ * calls LispMused on it. */
+ SSTREAMP(input_stream)->input += length + (status == NIL);
+ }
+ else /*if (input_stream->data.stream.type == LispStreamFile ||
+ input_stream->data.stream.type == LispStreamStandard ||
+ input_stream->data.stream.type == LispStreamPipe)*/ {
+ LispFile *file;
+
+ if (input_stream->data.stream.type == LispStreamPipe)
+ file = IPSTREAMP(input_stream);
+ else
+ file = FSTREAMP(input_stream);
+
+ if (file->nonblock) {
+ if (fcntl(file->descriptor, F_SETFL, 0) < 0)
+ LispDestroy("%s: fcntl: %s",
+ STRFUN(builtin), strerror(errno));
+ file->nonblock = 0;
+ }
+
+ while (1) {
+ ch = LispFgetc(file);
+ if (ch == EOF) {
+ if (length)
+ break;
+ if (eof_error_p != NIL)
+ LispDestroy("%s: EOF found reading %s",
+ STRFUN(builtin), STROBJ(input_stream));
+ if (string)
+ LispFree(string);
+
+ status = T;
+ result = eof_value;
+ goto read_line_done;
+ }
+ else if (ch == '\n')
+ break;
+ else if ((length % 64) == 0)
+ string = LispRealloc(string, length + 64);
+ string[length++] = ch;
+ }
+ if (string) {
+ if ((length % 64) == 0)
+ string = LispRealloc(string, length + 1);
+ string[length] = '\0';
+ result = LSTRING2(string, length);
+ }
+ else
+ result = STRING("");
+ }
+
+read_line_done:
+ RETURN(0) = status;
+ RETURN_COUNT = 1;
+
+ return (result);
+}
+
+LispObj *
+LispRead(void)
+{
+ READ_ENTER();
+ read_info info;
+ LispObj *result, *code = COD;
+
+ info.level = info.nodot = info.discard = 0;
+ info.circle_count = 0;
+ info.objects = NULL;
+ info.num_objects = 0;
+
+ result = LispDoRead(&info);
+
+ /* fix circular/shared lists, note that this is done when returning to
+ * the toplevel, so, if some circular/shared reference was evaluated,
+ * it should have generated an expected error */
+ if (info.num_objects) {
+ if (info.circle_count) {
+ info.circles = NULL;
+ info.num_circles = 0;
+ LispReadFixCircle(result, &info);
+ if (info.num_circles)
+ LispFree(info.circles);
+ }
+ LispFree(info.objects);
+ }
+
+ if (result == EOLIST)
+ READ_ERROR0("object cannot start with #\\)");
+ else if (result == DOT)
+ READ_ERROR0("dot allowed only on lists");
+
+ if (result != NULL && POINTERP(result)) {
+ if (code == NIL)
+ COD = result;
+ else
+ COD = CONS(COD, result);
+ }
+
+ return (result);
+}
+
+static int
+LispGetLine(LispObj *stream)
+{
+ int line = -1;
+
+ if (STREAMP(stream)) {
+ switch (stream->data.stream.type) {
+ case LispStreamStandard:
+ case LispStreamFile:
+ if (!FSTREAMP(stream)->binary)
+ line = FSTREAMP(stream)->line;
+ break;
+ case LispStreamPipe:
+ if (!IPSTREAMP(stream)->binary)
+ line = IPSTREAMP(stream)->line;
+ break;
+ case LispStreamString:
+ if (!SSTREAMP(stream)->binary)
+ line = SSTREAMP(stream)->line;
+ break;
+ default:
+ break;
+ }
+ }
+ else if (stream == NIL && !Stdin->binary)
+ line = Stdin->line;
+
+ return (line);
+}
+
+static void
+LispReadError(LispObj *stream, int line, char *fmt, ...)
+{
+ char string[128], *buffer_string;
+ LispObj *buffer = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
+ int length;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(string, sizeof(string), fmt, ap);
+ va_end(ap);
+
+ LispFwrite(Stderr, "*** Reading ", 12);
+ LispWriteObject(buffer, stream);
+ buffer_string = LispGetSstring(SSTREAMP(buffer), &length);
+ LispFwrite(Stderr, buffer_string, length);
+ LispFwrite(Stderr, " at line ", 9);
+ if (line < 0)
+ LispFwrite(Stderr, "?\n", 2);
+ else {
+ char str[32];
+
+ sprintf(str, "%d\n", line);
+ LispFputs(Stderr, str);
+ }
+
+ LispDestroy("READ: %s", string);
+}
+
+static void
+LispReadFixCircle(LispObj *object, read_info *info)
+{
+ LispObj *cons;
+
+fix_again:
+ switch (OBJECT_TYPE(object)) {
+ case LispCons_t:
+ for (cons = object;
+ CONSP(object);
+ cons = object, object = CDR(object)) {
+ if (READLABELP(CAR(object)))
+ CAR(object) = LispReadLabelCircle(CAR(object), info);
+ else if (LispReadCheckCircle(object, info))
+ return;
+ else
+ LispReadFixCircle(CAR(object), info);
+ }
+ if (READLABELP(object))
+ CDR(cons) = LispReadLabelCircle(object, info);
+ else
+ goto fix_again;
+ break;
+ case LispArray_t:
+ if (READLABELP(object->data.array.list))
+ object->data.array.list =
+ LispReadLabelCircle(object->data.array.list, info);
+ else if (!LispReadCheckCircle(object, info)) {
+ object = object->data.array.list;
+ goto fix_again;
+ }
+ break;
+ case LispStruct_t:
+ if (READLABELP(object->data.struc.fields))
+ object->data.struc.fields =
+ LispReadLabelCircle(object->data.struc.fields, info);
+ else if (!LispReadCheckCircle(object, info)) {
+ object = object->data.struc.fields;
+ goto fix_again;
+ }
+ break;
+ case LispQuote_t:
+ case LispBackquote_t:
+ case LispFunctionQuote_t:
+ if (READLABELP(object->data.quote))
+ object->data.quote =
+ LispReadLabelCircle(object->data.quote, info);
+ else {
+ object = object->data.quote;
+ goto fix_again;
+ }
+ break;
+ case LispComma_t:
+ if (READLABELP(object->data.comma.eval))
+ object->data.comma.eval =
+ LispReadLabelCircle(object->data.comma.eval, info);
+ else {
+ object = object->data.comma.eval;
+ goto fix_again;
+ }
+ break;
+ case LispLambda_t:
+ if (READLABELP(object->data.lambda.code))
+ object->data.lambda.code =
+ LispReadLabelCircle(object->data.lambda.code, info);
+ else if (!LispReadCheckCircle(object, info)) {
+ object = object->data.lambda.code;
+ goto fix_again;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static LispObj *
+LispReadLabelCircle(LispObj *label, read_info *info)
+{
+ long i, value = READLABEL_VALUE(label);
+
+ for (i = 0; i < info->num_objects; i++)
+ if (info->objects[i].label == value)
+ return (info->objects[i].object);
+
+ LispDestroy("READ: internal error");
+ /*NOTREACHED*/
+ return (label);
+}
+
+static int
+LispReadCheckCircle(LispObj *object, read_info *info)
+{
+ long i;
+
+ for (i = 0; i < info->num_circles; i++)
+ if (info->circles[i] == object)
+ return (1);
+
+ if ((info->num_circles % 16) == 0)
+ info->circles = LispRealloc(info->circles, sizeof(LispObj*) *
+ (info->num_circles + 16));
+ info->circles[info->num_circles++] = object;
+
+ return (0);
+}
+
+static LispObj *
+LispDoRead(read_info *info)
+{
+ LispObj *object;
+ int ch = LispSkipWhiteSpace();
+
+ switch (ch) {
+ case '(':
+ object = LispReadList(info);
+ break;
+ case ')':
+ for (ch = LispGet(); ch != EOF && ch != '\n'; ch = LispGet()) {
+ if (!isspace(ch)) {
+ LispUnget(ch);
+ break;
+ }
+ }
+ return (EOLIST);
+ case EOF:
+ return (NULL);
+ case '\'':
+ object = LispReadQuote(info);
+ break;
+ case '`':
+ object = LispReadBackquote(info);
+ break;
+ case ',':
+ object = LispReadCommaquote(info);
+ break;
+ case '#':
+ object = LispReadMacro(info);
+ break;
+ default:
+ LispUnget(ch);
+ object = LispReadObject(0, info);
+ break;
+ }
+
+ return (object);
+}
+
+static LispObj *
+LispReadMacro(read_info *info)
+{
+ READ_ENTER();
+ LispObj *result = NULL;
+ int ch = LispGet();
+
+ switch (ch) {
+ case '(':
+ result = LispReadVector(info);
+ break;
+ case '\'':
+ result = LispReadFunction(info);
+ break;
+ case 'b':
+ case 'B':
+ result = LispReadRational(2, info);
+ break;
+ case 'o':
+ case 'O':
+ result = LispReadRational(8, info);
+ break;
+ case 'x':
+ case 'X':
+ result = LispReadRational(16, info);
+ break;
+ case '\\':
+ result = LispReadCharacter(info);
+ break;
+ case '|':
+ LispSkipComment();
+ result = LispDoRead(info);
+ break;
+ case '.': /* eval when compiling */
+ case ',': /* eval when loading */
+ result = LispReadEval(info);
+ break;
+ case 'c':
+ case 'C':
+ result = LispReadComplex(info);
+ break;
+ case 'p':
+ case 'P':
+ result = LispReadPathname(info);
+ break;
+ case 's':
+ case 'S':
+ result = LispReadStruct(info);
+ break;
+ case '+':
+ result = LispReadFeature(1, info);
+ break;
+ case '-':
+ result = LispReadFeature(0, info);
+ break;
+ case ':':
+ /* Uninterned symbol */
+ result = LispReadObject(1, info);
+ break;
+ default:
+ if (isdigit(ch)) {
+ LispUnget(ch);
+ result = LispReadMacroArg(info);
+ }
+ else if (!info->discard)
+ READ_ERROR1("undefined dispatch macro character #%c", ch);
+ break;
+ }
+
+ return (result);
+}
+
+static LispObj *
+LispReadMacroArg(read_info *info)
+{
+ READ_ENTER();
+ LispObj *result = NIL;
+ long i, integer;
+ int ch;
+
+ /* skip leading zeros */
+ while (ch = LispGet(), ch != EOF && isdigit(ch) && ch == '0')
+ ;
+
+ if (ch == EOF)
+ READ_ERROR_EOF();
+
+ /* if ch is not a number the argument was zero */
+ if (isdigit(ch)) {
+ char stk[32], *str;
+ int len = 1;
+
+ stk[0] = ch;
+ for (;;) {
+ ch = LispGet();
+ if (!isdigit(ch))
+ break;
+ if (len + 1 >= sizeof(stk))
+ READ_ERROR_FIXNUM();
+ stk[len++] = ch;
+ }
+ stk[len] = '\0';
+ errno = 0;
+ integer = strtol(stk, &str, 10);
+ /* number is positive because sign is not processed here */
+ if (*str || errno == ERANGE || integer > MOST_POSITIVE_FIXNUM)
+ READ_ERROR_FIXNUM();
+ }
+ else
+ integer = 0;
+
+ switch (ch) {
+ case 'a':
+ case 'A':
+ if (integer == 1) {
+ /* LispReadArray and LispReadList expect
+ * the '(' being already read */
+ if ((ch = LispSkipWhiteSpace()) != '(') {
+ if (info->discard)
+ return (ch == EOF ? NULL : NIL);
+ READ_ERROR0("bad array specification");
+ }
+ result = LispReadVector(info);
+ }
+ else
+ result = LispReadArray(integer, info);
+ break;
+ case 'r':
+ case 'R':
+ result = LispReadRational(integer, info);
+ break;
+ case '=':
+ if (integer > MAX_LABEL_VALUE)
+ READ_ERROR_FIXNUM();
+ if (!info->discard) {
+ long num_objects = info->num_objects;
+
+ /* check for duplicated label */
+ for (i = 0; i < info->num_objects; i++) {
+ if (info->objects[i].label == integer)
+ READ_ERROR1("label #%ld# defined more than once",
+ integer);
+ }
+ info->objects = LispRealloc(info->objects,
+ sizeof(object_info) *
+ (num_objects + 1));
+ /* if this label is referenced it is a shared/circular object */
+ info->objects[num_objects].label = integer;
+ info->objects[num_objects].object = NULL;
+ info->objects[num_objects].num_circles = 0;
+ ++info->num_objects;
+ result = LispDoRead(info);
+ if (READLABELP(result) && READLABEL_VALUE(result) == integer)
+ READ_ERROR2("incorrect syntax #%ld= #%ld#",
+ integer, integer);
+ /* any reference to it now is not shared/circular */
+ info->objects[num_objects].object = result;
+ }
+ else
+ result = LispDoRead(info);
+ break;
+ case '#':
+ if (integer > MAX_LABEL_VALUE)
+ READ_ERROR_FIXNUM();
+ if (!info->discard) {
+ /* search object */
+ for (i = 0; i < info->num_objects; i++) {
+ if (info->objects[i].label == integer) {
+ result = info->objects[i].object;
+ if (result == NULL) {
+ ++info->objects[i].num_circles;
+ ++info->circle_count;
+ result = READLABEL(integer);
+ }
+ break;
+ }
+ }
+ if (i == info->num_objects)
+ READ_ERROR1("undefined label #%ld#", integer);
+ }
+ break;
+ default:
+ if (!info->discard)
+ READ_ERROR1("undefined dispatch macro character #%c", ch);
+ break;
+ }
+
+ return (result);
+}
+
+static int
+LispSkipWhiteSpace(void)
+{
+ int ch;
+
+ for (;;) {
+ while (ch = LispGet(), isspace(ch) && ch != EOF)
+ ;
+ if (ch == ';') {
+ while (ch = LispGet(), ch != '\n' && ch != EOF)
+ ;
+ if (ch == EOF)
+ return (EOF);
+ }
+ else
+ break;
+ }
+
+ return (ch);
+}
+
+/* any data in the format '(' FORM ')' is read here */
+static LispObj *
+LispReadList(read_info *info)
+{
+ READ_ENTER();
+ GC_ENTER();
+ LispObj *result, *cons, *object;
+ int dot = 0;
+
+ ++info->level;
+ /* check for () */
+ object = LispDoRead(info);
+ if (object == EOLIST) {
+ --info->level;
+
+ return (NIL);
+ }
+
+ if (object == DOT)
+ READ_ERROR0("illegal start of dotted list");
+
+ result = cons = CONS(object, NIL);
+
+ /* make sure GC will not release data being read */
+ GC_PROTECT(result);
+
+ while ((object = LispDoRead(info)) != EOLIST) {
+ if (object == NULL)
+ READ_ERROR_EOF();
+ if (object == DOT) {
+ if (info->nodot == info->level)
+ READ_ERROR0("dotted list not allowed");
+ /* this is a dotted list */
+ if (dot)
+ READ_ERROR0("more than one . in list");
+ dot = 1;
+ }
+ else {
+ if (dot) {
+ /* only one object after a dot */
+ if (++dot > 2)
+ READ_ERROR0("more than one object after . in list");
+ RPLACD(cons, object);
+ }
+ else {
+ RPLACD(cons, CONS(object, NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ /* this will happen if last list element was a dot */
+ if (dot == 1)
+ READ_ERROR0("illegal end of dotted list");
+
+ --info->level;
+ GC_LEAVE();
+
+ return (result);
+}
+
+static LispObj *
+LispReadQuote(read_info *info)
+{
+ READ_ENTER();
+ LispObj *quote = LispDoRead(info), *result;
+
+ if (INVALIDP(quote))
+ READ_ERROR_INVARG();
+
+ result = QUOTE(quote);
+
+ return (result);
+}
+
+static LispObj *
+LispReadBackquote(read_info *info)
+{
+ READ_ENTER();
+ LispObj *backquote = LispDoRead(info), *result;
+
+ if (INVALIDP(backquote))
+ READ_ERROR_INVARG();
+
+ result = BACKQUOTE(backquote);
+
+ return (result);
+}
+
+static LispObj *
+LispReadCommaquote(read_info *info)
+{
+ READ_ENTER();
+ LispObj *comma, *result;
+ int atlist = LispGet();
+
+ if (atlist == EOF)
+ READ_ERROR_EOF();
+ else if (atlist != '@' && atlist != '.')
+ LispUnget(atlist);
+
+ comma = LispDoRead(info);
+ if (comma == DOT) {
+ atlist = '@';
+ comma = LispDoRead(info);
+ }
+ if (INVALIDP(comma))
+ READ_ERROR_INVARG();
+
+ result = COMMA(comma, atlist == '@' || atlist == '.');
+
+ return (result);
+}
+
+/*
+ * Read anything that is not readily identifiable by it's first character
+ * and also put the code for reading atoms, numbers and strings together.
+ */
+static LispObj *
+LispReadObject(int unintern, read_info *info)
+{
+ READ_ENTER();
+ LispObj *object;
+ char stk[128], *string, *package, *symbol;
+ int ch, length, backslash, size, quote, unreadable, collon;
+
+ package = symbol = string = stk;
+ size = sizeof(stk);
+ backslash = quote = unreadable = collon = 0;
+ length = 0;
+
+ ch = LispGet();
+ if (unintern && (ch == ':' || ch == '"'))
+ READ_ERROR0("syntax error after #:");
+ else if (ch == '"' || ch == '|')
+ quote = ch;
+ else if (ch == '\\') {
+ unreadable = backslash = 1;
+ string[length++] = ch;
+ }
+ else if (ch == ':') {
+ collon = 1;
+ string[length++] = ch;
+ symbol = string + 1;
+ }
+ else if (ch) {
+ if (islower(ch))
+ ch = toupper(ch);
+ string[length++] = ch;
+ }
+ else
+ unreadable = 1;
+
+ /* read remaining data */
+ for (; ch;) {
+ ch = LispGet();
+
+ if (ch == EOF) {
+ if (quote) {
+ /* if quote, file ended with an open quoted object */
+ if (string != stk)
+ LispFree(string);
+ return (NULL);
+ }
+ break;
+ }
+ else if (ch == '\0')
+ break;
+
+ if (ch == '\\') {
+ backslash = !backslash;
+ if (quote == '"') {
+ /* only remove backslashs from strings */
+ if (backslash)
+ continue;
+ }
+ else
+ unreadable = 1;
+ }
+ else if (backslash)
+ backslash = 0;
+ else if (ch == quote)
+ break;
+ else if (!quote && !backslash) {
+ if (islower(ch))
+ ch = toupper(ch);
+ else if (isspace(ch))
+ break;
+ else if (AtomSeparator(ch, 0, 0)) {
+ LispUnget(ch);
+ break;
+ }
+ else if (ch == ':') {
+ if (collon == 0 ||
+ (collon == (1 - unintern) && symbol == string + length)) {
+ ++collon;
+ symbol = string + length + 1;
+ }
+ else
+ READ_ERROR0("too many collons");
+ }
+ }
+
+ if (length + 2 >= size) {
+ if (string == stk) {
+ size = 1024;
+ string = LispMalloc(size);
+ strcpy(string, stk);
+ }
+ else {
+ size += 1024;
+ string = LispRealloc(string, size);
+ }
+ symbol = string + (symbol - package);
+ package = string;
+ }
+ string[length++] = ch;
+ }
+
+ if (info->discard) {
+ if (string != stk)
+ LispFree(string);
+
+ return (ch == EOF ? NULL : NIL);
+ }
+
+ string[length] = '\0';
+
+ if (unintern) {
+ if (length == 0)
+ READ_ERROR0("syntax error after #:");
+ object = UNINTERNED_ATOM(string);
+ }
+
+ else if (quote == '"')
+ object = LSTRING(string, length);
+
+ else if (quote == '|' || (unreadable && !collon)) {
+ /* Set unreadable field, this atom needs quoting to be read back */
+ object = ATOM(string);
+ object->data.atom->unreadable = 1;
+ }
+
+ else if (collon) {
+ /* Package specified in object name */
+ symbol[-1] = '\0';
+ if (collon > 1)
+ symbol[-2] = '\0';
+ object = LispParseAtom(package, symbol,
+ collon == 2, unreadable,
+ read__stream, read__line);
+ }
+
+ /* Check some common symbols */
+ else if (length == 1 && string[0] == 'T')
+ /* The T */
+ object = T;
+
+ else if (length == 1 && string[0] == '.')
+ /* The dot */
+ object = DOT;
+
+ else if (length == 3 &&
+ string[0] == 'N' && string[1] == 'I' && string[2] == 'L')
+ /* The NIL */
+ object = NIL;
+
+ else if (isdigit(string[0]) || string[0] == '.' ||
+ ((string[0] == '-' || string[0] == '+') && string[1]))
+ /* Looks like a number */
+ object = LispParseNumber(string, 10, read__stream, read__line);
+
+ else
+ /* A normal atom */
+ object = ATOM(string);
+
+ if (string != stk)
+ LispFree(string);
+
+ return (object);
+}
+
+static LispObj *
+LispParseAtom(char *package, char *symbol, int intern, int unreadable,
+ LispObj *read__stream, int read__line)
+{
+ LispObj *object = NULL, *thepackage = NULL;
+ LispPackage *pack = NULL;
+
+ if (!unreadable) {
+ /* Until NIL and T be treated as normal symbols */
+ if (symbol[0] == 'N' && symbol[1] == 'I' &&
+ symbol[2] == 'L' && symbol[3] == '\0')
+ return (NIL);
+ if (symbol[0] == 'T' && symbol[1] == '\0')
+ return (T);
+ unreadable = !LispCheckAtomString(symbol);
+ }
+
+ /* If package is empty, it is a keyword */
+ if (package[0] == '\0') {
+ thepackage = lisp__data.keyword;
+ pack = lisp__data.key;
+ }
+
+ else {
+ /* Else, search it in the package list */
+ thepackage = LispFindPackageFromString(package);
+
+ if (thepackage == NIL)
+ READ_ERROR1("the package %s is not available", package);
+
+ pack = thepackage->data.package.package;
+ }
+
+ if (pack == lisp__data.pack && intern) {
+ /* Redundant package specification, since requesting a
+ * intern symbol, create it if does not exist */
+
+ object = ATOM(symbol);
+ if (unreadable)
+ object->data.atom->unreadable = 1;
+ }
+
+ else if (intern || pack == lisp__data.key) {
+ /* Symbol is created, or just fetched from the specified package */
+
+ LispPackage *savepack;
+ LispObj *savepackage = PACKAGE;
+
+ /* Remember curent package */
+ savepack = lisp__data.pack;
+
+ /* Temporarily set another package */
+ lisp__data.pack = pack;
+ PACKAGE = thepackage;
+
+ /* Get the object pointer */
+ if (pack == lisp__data.key)
+ object = KEYWORD(LispDoGetAtom(symbol, 0)->string);
+ else
+ object = ATOM(symbol);
+ if (unreadable)
+ object->data.atom->unreadable = 1;
+
+ /* Restore current package */
+ lisp__data.pack = savepack;
+ PACKAGE = savepackage;
+ }
+
+ 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;
+ }
+
+ /* No object found */
+ if (object == NULL || object->data.atom->ext == 0)
+ READ_ERROR2("no extern symbol %s in package %s", symbol, package);
+ }
+
+ return (object);
+}
+
+static LispObj *
+LispParseNumber(char *str, int radix, LispObj *read__stream, int read__line)
+{
+ int len;
+ long integer;
+ double dfloat;
+ char *ratio, *ptr;
+ LispObj *number;
+ mpi *bignum;
+ mpr *bigratio;
+
+ if (radix < 2 || radix > 36)
+ READ_ERROR1("radix %d is not in the range 2 to 36", radix);
+
+ if (*str == '\0')
+ return (NULL);
+
+ ratio = strchr(str, '/');
+ if (ratio) {
+ /* check if looks like a correctly specified ratio */
+ if (ratio[1] == '\0' || strchr(ratio + 1, '/') != NULL)
+ return (ATOM(str));
+
+ /* ratio must point to an integer in radix base */
+ *ratio++ = '\0';
+ }
+ else if (radix == 10) {
+ int dot = 0;
+ int type = 0;
+
+ /* check if it is a floating point number */
+ ptr = str;
+ if (*ptr == '-' || *ptr == '+')
+ ++ptr;
+ else if (*ptr == '.') {
+ dot = 1;
+ ++ptr;
+ }
+ while (*ptr) {
+ if (*ptr == '.') {
+ if (dot)
+ return (ATOM(str));
+ /* ignore it if last char is a dot */
+ if (ptr[1] == '\0') {
+ *ptr = '\0';
+ break;
+ }
+ dot = 1;
+ }
+ else if (!isdigit(*ptr))
+ break;
+ ++ptr;
+ }
+
+ switch (*ptr) {
+ case '\0':
+ if (dot) /* if dot, it is default float */
+ type = 'E';
+ break;
+ case 'E': case 'S': case 'F': case 'D': case 'L':
+ type = *ptr;
+ *ptr = 'E';
+ break;
+ default:
+ return (ATOM(str)); /* syntax error */
+ }
+
+ /* if type set, it is not an integer specification */
+ if (type) {
+ if (*ptr) {
+ int itype = *ptr;
+ char *ptype = ptr;
+
+ ++ptr;
+ if (*ptr == '+' || *ptr == '-')
+ ++ptr;
+ while (*ptr && isdigit(*ptr))
+ ++ptr;
+ if (*ptr) {
+ *ptype = itype;
+
+ return (ATOM(str));
+ }
+ }
+
+ dfloat = strtod(str, NULL);
+ if (!finite(dfloat))
+ READ_ERROR0("floating point overflow");
+
+ return (DFLOAT(dfloat));
+ }
+ }
+
+ /* check if correctly specified in the given radix */
+ len = strlen(str) - 1;
+ if (!ratio && radix != 10 && str[len] == '.')
+ str[len] = '\0';
+
+ if (ratio || radix != 10) {
+ if (!StringInRadix(str, radix, 1)) {
+ if (ratio)
+ ratio[-1] = '/';
+ return (ATOM(str));
+ }
+ if (ratio && !StringInRadix(ratio, radix, 0)) {
+ ratio[-1] = '/';
+ return (ATOM(str));
+ }
+ }
+
+ bignum = NULL;
+ bigratio = NULL;
+
+ errno = 0;
+ integer = strtol(str, NULL, radix);
+
+ /* if does not fit in a long */
+ if (errno == ERANGE &&
+ ((*str == '-' && integer == LONG_MIN) ||
+ (*str != '-' && integer == LONG_MAX))) {
+ bignum = LispMalloc(sizeof(mpi));
+ mpi_init(bignum);
+ mpi_setstr(bignum, str, radix);
+ }
+
+
+ if (ratio && integer != 0) {
+ long denominator;
+
+ errno = 0;
+ denominator = strtol(ratio, NULL, radix);
+ if (denominator == 0)
+ READ_ERROR0("divide by zero");
+
+ if (bignum == NULL) {
+ if (integer == MINSLONG ||
+ (denominator == LONG_MAX && errno == ERANGE)) {
+ bigratio = LispMalloc(sizeof(mpr));
+ mpr_init(bigratio);
+ mpi_seti(mpr_num(bigratio), integer);
+ mpi_setstr(mpr_den(bigratio), ratio, radix);
+ }
+ }
+ else {
+ bigratio = LispMalloc(sizeof(mpr));
+ mpr_init(bigratio);
+ mpi_set(mpr_num(bigratio), bignum);
+ mpi_clear(bignum);
+ LispFree(bignum);
+ mpi_setstr(mpr_den(bigratio), ratio, radix);
+ }
+
+ if (bigratio) {
+ mpr_canonicalize(bigratio);
+ if (mpi_fiti(mpr_num(bigratio)) &&
+ mpi_fiti(mpr_den(bigratio))) {
+ integer = mpi_geti(mpr_num(bigratio));
+ denominator = mpi_geti(mpr_den(bigratio));
+ mpr_clear(bigratio);
+ LispFree(bigratio);
+ if (denominator == 1)
+ number = INTEGER(integer);
+ else
+ number = RATIO(integer, denominator);
+ }
+ else
+ number = BIGRATIO(bigratio);
+ }
+ else {
+ long num = integer, den = denominator, rest;
+
+ if (num < 0)
+ num = -num;
+ for (;;) {
+ if ((rest = den % num) == 0)
+ break;
+ den = num;
+ num = rest;
+ }
+ if (den != 1) {
+ denominator /= num;
+ integer /= num;
+ }
+ if (denominator < 0) {
+ integer = -integer;
+ denominator = -denominator;
+ }
+ if (denominator == 1)
+ number = INTEGER(integer);
+ else
+ number = RATIO(integer, denominator);
+ }
+ }
+ else if (bignum)
+ number = BIGNUM(bignum);
+ else
+ number = INTEGER(integer);
+
+ return (number);
+}
+
+static int
+StringInRadix(char *str, int radix, int skip_sign)
+{
+ if (skip_sign && (*str == '-' || *str == '+'))
+ ++str;
+ while (*str) {
+ if (*str >= '0' && *str <= '9') {
+ if (*str - '0' >= radix)
+ return (0);
+ }
+ else if (*str >= 'A' && *str <= 'Z') {
+ if (radix <= 10 || *str - 'A' + 10 >= radix)
+ return (0);
+ }
+ else
+ return (0);
+ str++;
+ }
+
+ return (1);
+}
+
+static int
+AtomSeparator(int ch, int check_space, int check_backslash)
+{
+ if (check_space && isspace(ch))
+ return (1);
+ if (check_backslash && ch == '\\')
+ return (1);
+ return (strchr("(),\";'`#|,", ch) != NULL);
+}
+
+static LispObj *
+LispReadVector(read_info *info)
+{
+ LispObj *objects;
+ int nodot = info->nodot;
+
+ info->nodot = info->level + 1;
+ objects = LispReadList(info);
+ info->nodot = nodot;
+
+ if (info->discard)
+ return (objects);
+
+ return (VECTOR(objects));
+}
+
+static LispObj *
+LispReadFunction(read_info *info)
+{
+ READ_ENTER();
+ int nodot = info->nodot;
+ LispObj *function;
+
+ info->nodot = info->level + 1;
+ function = LispDoRead(info);
+ info->nodot = nodot;
+
+ if (info->discard)
+ return (function);
+
+ if (INVALIDP(function))
+ READ_ERROR_INVARG();
+ else if (CONSP(function)) {
+ if (CAR(function) != Olambda)
+ READ_ERROR_INVARG();
+
+ return (FUNCTION_QUOTE(function));
+ }
+ else if (!SYMBOLP(function))
+ READ_ERROR_INVARG();
+
+ return (FUNCTION_QUOTE(function));
+}
+
+static LispObj *
+LispReadRational(int radix, read_info *info)
+{
+ READ_ENTER();
+ LispObj *number;
+ int ch, len, size;
+ char stk[128], *str;
+
+ len = 0;
+ str = stk;
+ size = sizeof(stk);
+
+ for (;;) {
+ ch = LispGet();
+ if (ch == EOF || isspace(ch))
+ break;
+ else if (AtomSeparator(ch, 0, 1)) {
+ LispUnget(ch);
+ break;
+ }
+ else if (islower(ch))
+ ch = toupper(ch);
+ if ((ch < '0' || ch > '9') && (ch < 'A' || ch > 'Z') &&
+ ch != '+' && ch != '-' && ch != '/') {
+ if (str != stk)
+ LispFree(str);
+ if (!info->discard)
+ READ_ERROR1("bad character %c for rational number", ch);
+ }
+ if (len + 1 >= size) {
+ if (str == stk) {
+ size = 512;
+ str = LispMalloc(size);
+ strcpy(str + 1, stk + 1);
+ }
+ else {
+ size += 512;
+ str = LispRealloc(str, size);
+ }
+ }
+ str[len++] = ch;
+ }
+
+ if (info->discard) {
+ if (str != stk)
+ LispFree(str);
+
+ return (ch == EOF ? NULL : NIL);
+ }
+
+ str[len] = '\0';
+
+ number = LispParseNumber(str, radix, read__stream, read__line);
+ if (str != stk)
+ LispFree(str);
+
+ if (!RATIONALP(number))
+ READ_ERROR0("bad rational number specification");
+
+ return (number);
+}
+
+static LispObj *
+LispReadCharacter(read_info *info)
+{
+ READ_ENTER();
+ long c;
+ int ch, len;
+ char stk[64];
+
+ ch = LispGet();
+ if (ch == EOF)
+ return (NULL);
+
+ stk[0] = ch;
+ len = 1;
+
+ for (;;) {
+ ch = LispGet();
+ if (ch == EOF)
+ break;
+ else if (ch != '-' && !isalnum(ch)) {
+ LispUnget(ch);
+ break;
+ }
+ if (len + 1 < sizeof(stk))
+ stk[len++] = ch;
+ }
+ if (len > 1) {
+ char **names;
+ int found = 0;
+ stk[len] = '\0';
+
+ for (c = ch = 0; ch <= ' ' && !found; ch++) {
+ for (names = LispChars[ch].names; *names; names++)
+ if (strcasecmp(*names, stk) == 0) {
+ c = ch;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ for (names = LispChars[0177].names; *names; names++)
+ if (strcasecmp(*names, stk) == 0) {
+ c = 0177;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (info->discard)
+ return (NIL);
+ READ_ERROR1("unkwnown character %s", stk);
+ }
+ }
+ else
+ c = stk[0];
+
+ return (SCHAR(c));
+}
+
+static void
+LispSkipComment(void)
+{
+ READ_ENTER();
+ int ch, comm = 1;
+
+ for (;;) {
+ ch = LispGet();
+ if (ch == '#') {
+ ch = LispGet();
+ if (ch == '|')
+ ++comm;
+ continue;
+ }
+ while (ch == '|') {
+ ch = LispGet();
+ if (ch == '#' && --comm == 0)
+ return;
+ }
+ if (ch == EOF)
+ READ_ERROR_EOF();
+ }
+}
+
+static LispObj *
+LispReadEval(read_info *info)
+{
+ READ_ENTER();
+ int nodot = info->nodot;
+ LispObj *code;
+
+ info->nodot = info->level + 1;
+ code = LispDoRead(info);
+ info->nodot = nodot;
+
+ if (info->discard)
+ return (code);
+
+ if (INVALIDP(code))
+ READ_ERROR_INVARG();
+
+ return (EVAL(code));
+}
+
+static LispObj *
+LispReadComplex(read_info *info)
+{
+ READ_ENTER();
+ GC_ENTER();
+ int nodot = info->nodot;
+ LispObj *number, *arguments;
+
+ info->nodot = info->level + 1;
+ arguments = LispDoRead(info);
+ info->nodot = nodot;
+
+ /* form read */
+ if (info->discard)
+ return (arguments);
+
+ if (INVALIDP(arguments) || !CONSP(arguments))
+ READ_ERROR_INVARG();
+
+ GC_PROTECT(arguments);
+ number = APPLY(Ocomplex, arguments);
+ GC_LEAVE();
+
+ return (number);
+}
+
+static LispObj *
+LispReadPathname(read_info *info)
+{
+ READ_ENTER();
+ GC_ENTER();
+ int nodot = info->nodot;
+ LispObj *path, *arguments;
+
+ info->nodot = info->level + 1;
+ arguments = LispDoRead(info);
+ info->nodot = nodot;
+
+ /* form read */
+ if (info->discard)
+ return (arguments);
+
+ if (INVALIDP(arguments))
+ READ_ERROR_INVARG();
+
+ GC_PROTECT(arguments);
+ path = APPLY1(Oparse_namestring, arguments);
+ GC_LEAVE();
+
+ return (path);
+}
+
+static LispObj *
+LispReadStruct(read_info *info)
+{
+ READ_ENTER();
+ GC_ENTER();
+ int len, nodot = info->nodot;
+ char stk[128], *str;
+ LispObj *struc, *fields;
+
+ info->nodot = info->level + 1;
+ fields = LispDoRead(info);
+ info->nodot = nodot;
+
+ /* form read */
+ if (info->discard)
+ return (fields);
+
+ if (INVALIDP(fields) || !CONSP(fields) || !SYMBOLP(CAR(fields)))
+ READ_ERROR_INVARG();
+
+ GC_PROTECT(fields);
+
+ len = strlen(ATOMID(CAR(fields)));
+ /* MAKE- */
+ if (len + 6 > sizeof(stk))
+ str = LispMalloc(len + 6);
+ else
+ str = stk;
+ sprintf(str, "MAKE-%s", ATOMID(CAR(fields)));
+ RPLACA(fields, ATOM(str));
+ if (str != stk)
+ LispFree(str);
+ struc = APPLY(Omake_struct, fields);
+ GC_LEAVE();
+
+ return (struc);
+}
+
+/* XXX This is broken, needs a rewritten as soon as true vector/arrays be
+ * implemented. */
+static LispObj *
+LispReadArray(long dimensions, read_info *info)
+{
+ READ_ENTER();
+ GC_ENTER();
+ long count;
+ int nodot = info->nodot;
+ LispObj *arguments, *initial, *dim, *cons, *array, *data;
+
+ info->nodot = info->level + 1;
+ data = LispDoRead(info);
+ info->nodot = nodot;
+
+ /* form read */
+ if (info->discard)
+ return (data);
+
+ if (INVALIDP(data))
+ READ_ERROR_INVARG();
+
+ initial = Kinitial_contents;
+
+ dim = cons = NIL;
+ if (dimensions) {
+ LispObj *array;
+
+ for (count = 0, array = data; count < dimensions; count++) {
+ long length;
+ LispObj *item;
+
+ if (!CONSP(array))
+ READ_ERROR0("bad array for given dimension");
+ item = array;
+ array = CAR(array);
+
+ for (length = 0; CONSP(item); item = CDR(item), length++)
+ ;
+
+ if (dim == NIL) {
+ dim = cons = CONS(FIXNUM(length), NIL);
+ GC_PROTECT(dim);
+ }
+ else {
+ RPLACD(cons, CONS(FIXNUM(length), NIL));
+ cons = CDR(cons);
+ }
+ }
+ }
+
+ arguments = CONS(dim, CONS(initial, CONS(data, NIL)));
+ GC_PROTECT(arguments);
+ array = APPLY(Omake_array, arguments);
+ GC_LEAVE();
+
+ return (array);
+}
+
+static LispObj *
+LispReadFeature(int with, read_info *info)
+{
+ READ_ENTER();
+ LispObj *status;
+ LispObj *feature = LispDoRead(info);
+
+ /* form read */
+ if (info->discard)
+ return (feature);
+
+ if (INVALIDP(feature))
+ READ_ERROR_INVARG();
+
+ /* paranoia check, features must be a list, possibly empty */
+ if (!CONSP(FEATURES) && FEATURES != NIL)
+ READ_ERROR1("%s is not a list", STROBJ(FEATURES));
+
+ status = LispEvalFeature(feature);
+
+ if (with) {
+ if (status == T)
+ return (LispDoRead(info));
+
+ /* need to use the field discard because the following expression
+ * may be #.FORM or #,FORM or any other form that may generate
+ * side effects */
+ info->discard = 1;
+ LispDoRead(info);
+ info->discard = 0;
+
+ return (LispDoRead(info));
+ }
+
+ if (status == NIL)
+ return (LispDoRead(info));
+
+ info->discard = 1;
+ LispDoRead(info);
+ info->discard = 0;
+
+ return (LispDoRead(info));
+}
+
+/*
+ * A very simple eval loop with AND, NOT, and OR functions for testing
+ * the available features.
+ */
+static LispObj *
+LispEvalFeature(LispObj *feature)
+{
+ READ_ENTER();
+ Atom_id test;
+ LispObj *object;
+
+ if (CONSP(feature)) {
+ LispObj *function = CAR(feature), *arguments = CDR(feature);
+
+ if (!SYMBOLP(function))
+ READ_ERROR1("bad feature test function %s", STROBJ(function));
+ if (!CONSP(arguments))
+ READ_ERROR1("bad feature test arguments %s", STROBJ(arguments));
+ test = ATOMID(function);
+ if (test == Sand) {
+ for (; CONSP(arguments); arguments = CDR(arguments)) {
+ if (LispEvalFeature(CAR(arguments)) == NIL)
+ return (NIL);
+ }
+ return (T);
+ }
+ else if (test == Sor) {
+ for (; CONSP(arguments); arguments = CDR(arguments)) {
+ if (LispEvalFeature(CAR(arguments)) == T)
+ return (T);
+ }
+ return (NIL);
+ }
+ else if (test == Snot) {
+ if (CONSP(CDR(arguments)))
+ READ_ERROR0("too many arguments to NOT");
+
+ return (LispEvalFeature(CAR(arguments)) == NIL ? T : NIL);
+ }
+ else
+ READ_ERROR1("unimplemented feature test function %s", test);
+ }
+
+ if (KEYWORDP(feature))
+ feature = feature->data.quote;
+ else if (!SYMBOLP(feature))
+ READ_ERROR1("bad feature specification %s", STROBJ(feature));
+
+ test = ATOMID(feature);
+
+ for (object = FEATURES; CONSP(object); object = CDR(object)) {
+ /* paranoia check, elements in the feature list must ge keywords */
+ if (!KEYWORDP(CAR(object)))
+ READ_ERROR1("%s is not a keyword", STROBJ(CAR(object)));
+ if (ATOMID(CAR(object)) == test)
+ return (T);
+ }
+
+ /* unknown feature */
+ return (NIL);
+}
diff --git a/lisp/read.h b/lisp/read.h
new file mode 100644
index 0000000..229ced5
--- /dev/null
+++ b/lisp/read.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/read.h,v 1.3 2002/11/15 07:01:30 paulo Exp $ */
+
+#ifndef Lisp_read_h
+#define Lisp_read_h
+
+#include "io.h"
+
+/*
+ * Prototypes
+ */
+LispObj *LispRead(void);
+
+LispObj *Lisp_Read(LispBuiltin*);
+LispObj *Lisp_ReadChar(LispBuiltin*);
+LispObj *Lisp_ReadCharNoHang(LispBuiltin*);
+LispObj *Lisp_ReadLine(LispBuiltin*);
+
+#endif /* Lisp_read_h */
diff --git a/lisp/regex.c b/lisp/regex.c
new file mode 100644
index 0000000..bf3390d
--- /dev/null
+++ b/lisp/regex.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/regex.c,v 1.10 2002/12/11 04:44:28 paulo Exp $ */
+
+#include "regex.h"
+#include "private.h"
+#include "helper.h"
+
+/*
+ * Prototypes
+ */
+static re_cod *LispRecomp(LispBuiltin*, char*, int);
+
+/*
+ * Initialization
+ */
+LispObj *Knomatch;
+
+/*
+ * Implementation
+ */
+static re_cod *
+LispRecomp(LispBuiltin *builtin, char *pattern, int cflags)
+{
+ int code;
+ re_cod *regex = LispMalloc(sizeof(re_cod));
+
+ if ((code = recomp(regex, pattern, cflags)) != 0) {
+ char buffer[256];
+
+ reerror(code, regex, buffer, sizeof(buffer));
+ refree(regex);
+ LispFree(regex);
+ LispDestroy("%s: recomp(\"%s\"): %s", STRFUN(builtin), pattern, buffer);
+ }
+
+ return (regex);
+}
+
+void
+LispRegexInit(void)
+{
+ Knomatch = KEYWORD("NOMATCH");
+}
+
+LispObj *
+Lisp_Recomp(LispBuiltin *builtin)
+/*
+ re-comp pattern &key nospec icase nosub newline
+ */
+{
+ re_cod *regex;
+ int cflags = 0;
+
+ LispObj *result;
+
+ LispObj *pattern, *nospec, *icase, *nosub, *newline;
+
+ newline = ARGUMENT(4);
+ nosub = ARGUMENT(3);
+ icase = ARGUMENT(2);
+ nospec = ARGUMENT(1);
+ pattern = ARGUMENT(0);
+
+ /* Don't generate an error if it is already a compiled regex. */
+ if (REGEXP(pattern))
+ return (pattern);
+
+ CHECK_STRING(pattern);
+
+ if (nospec != UNSPEC && nospec != NIL)
+ cflags |= RE_NOSPEC;
+ if (icase != UNSPEC && icase != NIL)
+ cflags |= RE_ICASE;
+ if (nosub != UNSPEC && nosub != NIL)
+ cflags |= RE_NOSUB;
+ if (newline != UNSPEC && newline != NIL)
+ cflags |= RE_NEWLINE;
+
+ regex = LispRecomp(builtin, THESTR(pattern), cflags);
+ result = LispNew(pattern, NIL);
+ result->type = LispRegex_t;
+ result->data.regex.regex = regex;
+ result->data.regex.pattern = pattern;
+ result->data.regex.options = cflags;
+ LispMused(regex);
+
+ return (result);
+}
+
+LispObj *
+Lisp_Reexec(LispBuiltin *builtin)
+/*
+ re-exec regex string &key count start end notbol noteol
+ */
+{
+ size_t nmatch;
+ re_mat match[10];
+ long start, end, length;
+ int code, cflags, eflags;
+ char *string;
+ LispObj *result;
+ re_cod *regexp;
+
+ LispObj *regex, *ostring, *count, *ostart, *oend, *notbol, *noteol;
+
+ noteol = ARGUMENT(6);
+ notbol = ARGUMENT(5);
+ oend = ARGUMENT(4);
+ ostart = ARGUMENT(3);
+ count = ARGUMENT(2);
+ ostring = ARGUMENT(1);
+ regex = ARGUMENT(0);
+
+ if (STRINGP(regex))
+ regexp = LispRecomp(builtin, THESTR(regex), cflags = 0);
+ else {
+ CHECK_REGEX(regex);
+ regexp = regex->data.regex.regex;
+ cflags = regex->data.regex.options;
+ }
+
+ CHECK_STRING(ostring);
+
+ if (count == UNSPEC)
+ nmatch = 1;
+ else {
+ CHECK_INDEX(count);
+ nmatch = FIXNUM_VALUE(count);
+ if (nmatch > 10)
+ LispDestroy("%s: COUNT cannot be larger than 10", STRFUN(builtin));
+ }
+ if (nmatch && (cflags & RE_NOSUB))
+ nmatch = 1;
+
+ eflags = RE_STARTEND;
+ if (notbol != UNSPEC && notbol != NIL)
+ eflags |= RE_NOTBOL;
+ if (noteol != UNSPEC && noteol != NIL)
+ eflags |= RE_NOTEOL;
+
+ string = THESTR(ostring);
+ LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
+ &start, &end, &length);
+
+ match[0].rm_so = start;
+ match[0].rm_eo = end;
+ code = reexec(regexp, string, nmatch, &match[0], eflags);
+
+ if (code == 0) {
+ if (nmatch && match[0].rm_eo >= match[0].rm_so) {
+ result = CONS(CONS(FIXNUM(match[0].rm_so),
+ FIXNUM(match[0].rm_eo)), NIL);
+ if (nmatch > 1 && match[1].rm_eo >= match[1].rm_so) {
+ int i;
+ GC_ENTER();
+ LispObj *cons = result;
+
+ GC_PROTECT(result);
+ for (i = 1;
+ i < nmatch && match[i].rm_eo >= match[i].rm_so;
+ i++) {
+ RPLACD(cons, CONS(CONS(FIXNUM(match[i].rm_so),
+ FIXNUM(match[i].rm_eo)), NIL));
+ cons = CDR(cons);
+ }
+ GC_LEAVE();
+ }
+ }
+ else
+ result = NIL;
+ }
+ else
+ result = Knomatch;
+
+ /* Maybe shoud cache compiled regex, but better the caller do it */
+ if (!XREGEXP(regex)) {
+ refree(regexp);
+ LispFree(regexp);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_Rep(LispBuiltin *builtin)
+/*
+ re-p object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (REGEXP(object) ? T : NIL);
+}
diff --git a/lisp/regex.h b/lisp/regex.h
new file mode 100644
index 0000000..cc4d7e6
--- /dev/null
+++ b/lisp/regex.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/regex.h,v 1.3 2002/11/08 08:00:57 paulo Exp $ */
+
+#ifndef Lisp_regex_h
+#define Lisp_regex_h
+
+#include "internal.h"
+
+/*
+ * Prototypes
+ */
+void LispRegexInit(void);
+
+LispObj *Lisp_Recomp(LispBuiltin*);
+LispObj *Lisp_Reexec(LispBuiltin*);
+LispObj *Lisp_Rep(LispBuiltin*);
+
+#endif /* Lisp_regex_h */
diff --git a/lisp/require.c b/lisp/require.c
new file mode 100644
index 0000000..7f80c3f
--- /dev/null
+++ b/lisp/require.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/require.c,v 1.16 2002/11/23 08:26:50 paulo Exp $ */
+
+#include "require.h"
+
+/*
+ * Implementation
+ */
+LispObj *
+Lisp_Load(LispBuiltin *builtin)
+/*
+ load filename &key verbose print if-does-not-exist
+ */
+{
+ LispObj *filename, *verbose, *print, *if_does_not_exist;
+
+ if_does_not_exist = ARGUMENT(3);
+ print = ARGUMENT(2);
+ verbose = ARGUMENT(1);
+ filename = ARGUMENT(0);
+
+ if (PATHNAMEP(filename))
+ filename = CAR(filename->data.pathname);
+ else {
+ CHECK_STRING(filename);
+ }
+
+ return (LispLoadFile(filename,
+ verbose != UNSPEC && verbose != NIL,
+ print != UNSPEC && print != NIL,
+ if_does_not_exist != UNSPEC &&
+ if_does_not_exist != NIL));
+}
+
+LispObj *
+Lisp_Require(LispBuiltin *builtin)
+/*
+ require module &optional pathname
+ */
+{
+ char filename[1024], *ext;
+ int len;
+
+ LispObj *obj, *module, *pathname;
+
+ pathname = ARGUMENT(1);
+ module = ARGUMENT(0);
+
+ CHECK_STRING(module);
+ if (pathname != UNSPEC) {
+ if (PATHNAMEP(pathname))
+ pathname = CAR(pathname->data.pathname);
+ else {
+ CHECK_STRING(pathname);
+ }
+ }
+ else
+ pathname = module;
+
+ for (obj = MOD; CONSP(obj); obj = CDR(obj)) {
+ if (strcmp(THESTR(CAR(obj)), THESTR(module)) == 0)
+ return (module);
+ }
+
+ if (THESTR(pathname)[0] != '/') {
+#ifdef LISPDIR
+ snprintf(filename, sizeof(filename), "%s", LISPDIR);
+#else
+ getcwd(filename, sizeof(filename));
+#endif
+ }
+ else
+ filename[0] = '\0';
+ *(filename + sizeof(filename) - 5) = '\0'; /* make sure there is place for ext */
+ len = strlen(filename);
+ if (!len || filename[len - 1] != '/') {
+ strcat(filename, "/");
+ ++len;
+ }
+
+ snprintf(filename + len, sizeof(filename) - len - 5, "%s", THESTR(pathname));
+
+ ext = filename + strlen(filename);
+
+#ifdef SHARED_MODULES
+ strcpy(ext, ".so");
+ if (access(filename, R_OK) == 0) {
+ LispModule *lisp_module;
+ char data[64];
+ int len;
+
+ if (lisp__data.module == NULL) {
+ /* export our own symbols */
+ if (dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL) == NULL)
+ LispDestroy(mac, "%s: ", STRFUN(builtin), dlerror());
+ }
+
+ lisp_module = (LispModule*)LispMalloc(sizeof(LispModule));
+ if ((lisp_module->handle =
+ dlopen(filename, RTLD_LAZY | RTLD_GLOBAL)) == NULL)
+ LispDestroy(mac, "%s: dlopen: %s", STRFUN(builtin), dlerror());
+ snprintf(data, sizeof(data), "%sLispModuleData", THESTR(module));
+ if ((lisp_module->data =
+ (LispModuleData*)dlsym(lisp_module->handle, data)) == NULL) {
+ dlclose(lisp_module->handle);
+ LispDestroy("%s: cannot find LispModuleData for %s",
+ STRFUN(builtin), STROBJ(module));
+ }
+ LispMused(lisp_module);
+ lisp_module->next = lisp__data.module;
+ lisp__data.module = lisp_module;
+ if (lisp_module->data->load)
+ (lisp_module->data->load)();
+
+ if (MOD == NIL)
+ MOD = CONS(module, NIL);
+ else {
+ RPLACD(MOD, CONS(CAR(MOD), CDR(MOD)));
+ RPLACA(MOD, module);
+ }
+ LispSetVar(lisp__data.modules, MOD);
+
+ return (module);
+ }
+#endif
+
+ strcpy(ext, ".lsp");
+ (void)LispLoadFile(STRING(filename), 0, 0, 0);
+
+ return (module);
+}
diff --git a/lisp/require.h b/lisp/require.h
new file mode 100644
index 0000000..42c2064
--- /dev/null
+++ b/lisp/require.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/require.h,v 1.4 2002/11/08 08:00:57 paulo Exp $ */
+
+#ifndef Lisp_require_h
+#define Lisp_require_h
+
+#include "private.h"
+#include "helper.h"
+#ifdef SHARED_MODULES
+#include <dlfcn.h>
+#endif
+
+LispObj *Lisp_Load(LispBuiltin*);
+LispObj *Lisp_Require(LispBuiltin*);
+
+#endif /* Lisp_require_h */
diff --git a/lisp/stream.c b/lisp/stream.c
new file mode 100644
index 0000000..be0f44d
--- /dev/null
+++ b/lisp/stream.c
@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/stream.c,v 1.21 2002/12/10 03:59:03 paulo Exp $ */
+
+#include "read.h"
+#include "stream.h"
+#include "pathname.h"
+#include "write.h"
+#include "private.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+
+/*
+ * Initialization
+ */
+#define DIR_PROBE 0
+#define DIR_INPUT 1
+#define DIR_OUTPUT 2
+#define DIR_IO 3
+
+#define EXT_NIL 0
+#define EXT_ERROR 1
+#define EXT_NEW_VERSION 2
+#define EXT_RENAME 3
+#define EXT_RENAME_DELETE 4
+#define EXT_OVERWRITE 5
+#define EXT_APPEND 6
+#define EXT_SUPERSEDE 7
+
+#define NOEXT_NIL 0
+#define NOEXT_ERROR 1
+#define NOEXT_CREATE 2
+#define NOEXT_NOTHING 3
+
+extern char **environ;
+
+LispObj *Oopen, *Oclose, *Otruename;
+
+LispObj *Kif_does_not_exist, *Kprobe, *Kinput, *Koutput, *Kio,
+ *Knew_version, *Krename, *Krename_and_delete, *Koverwrite,
+ *Kappend, *Ksupersede, *Kcreate;
+
+/*
+ * Implementation
+ */
+void
+LispStreamInit(void)
+{
+ Oopen = STATIC_ATOM("OPEN");
+ Oclose = STATIC_ATOM("CLOSE");
+ Otruename = STATIC_ATOM("TRUENAME");
+
+ Kif_does_not_exist = KEYWORD("IF-DOES-NOT-EXIST");
+ Kprobe = KEYWORD("PROBE");
+ Kinput = KEYWORD("INPUT");
+ Koutput = KEYWORD("OUTPUT");
+ Kio = KEYWORD("IO");
+ Knew_version = KEYWORD("NEW-VERSION");
+ Krename = KEYWORD("RENAME");
+ Krename_and_delete = KEYWORD("RENAME-AND-DELETE");
+ Koverwrite = KEYWORD("OVERWRITE");
+ Kappend = KEYWORD("APPEND");
+ Ksupersede = KEYWORD("SUPERSEDE");
+ Kcreate = KEYWORD("CREATE");
+}
+
+LispObj *
+Lisp_DeleteFile(LispBuiltin *builtin)
+/*
+ delete-file filename
+ */
+{
+ GC_ENTER();
+ LispObj *filename;
+
+ filename = ARGUMENT(0);
+
+ if (STRINGP(filename)) {
+ filename = APPLY1(Oparse_namestring, filename);
+ GC_PROTECT(filename);
+ }
+ else if (STREAMP(filename)) {
+ if (filename->data.stream.type != LispStreamFile)
+ LispDestroy("%s: %s is not a FILE-STREAM",
+ STRFUN(builtin), STROBJ(filename));
+ filename = filename->data.stream.pathname;
+ }
+ else {
+ CHECK_PATHNAME(filename);
+ }
+ GC_LEAVE();
+
+ return (LispUnlink(THESTR(CAR(filename->data.pathname))) ? NIL : T);
+}
+
+LispObj *
+Lisp_RenameFile(LispBuiltin *builtin)
+/*
+ rename-file filename new-name
+ */
+{
+ int code;
+ GC_ENTER();
+ char *from, *to;
+ LispObj *old_truename, *new_truename;
+
+ LispObj *filename, *new_name;
+
+ new_name = ARGUMENT(1);
+ filename = ARGUMENT(0);
+
+ if (STRINGP(filename)) {
+ filename = APPLY1(Oparse_namestring, filename);
+ GC_PROTECT(filename);
+ }
+ else if (STREAMP(filename)) {
+ if (filename->data.stream.type != LispStreamFile)
+ LispDestroy("%s: %s is not a FILE-STREAM",
+ STRFUN(builtin), STROBJ(filename));
+ filename = filename->data.stream.pathname;
+ }
+ else {
+ CHECK_PATHNAME(filename);
+ }
+ old_truename = APPLY1(Otruename, filename);
+ GC_PROTECT(old_truename);
+
+ if (STRINGP(new_name)) {
+ new_name = APPLY3(Oparse_namestring, new_name, NIL, filename);
+ GC_PROTECT(new_name);
+ }
+ else {
+ CHECK_PATHNAME(new_name);
+ }
+
+ from = THESTR(CAR(filename->data.pathname));
+ to = THESTR(CAR(new_name->data.pathname));
+ code = LispRename(from, to);
+ if (code)
+ LispDestroy("%s: rename(%s, %s): %s",
+ STRFUN(builtin), from, to, strerror(errno));
+ GC_LEAVE();
+
+ new_truename = APPLY1(Otruename, new_name);
+ RETURN_COUNT = 2;
+ RETURN(0) = old_truename;
+ RETURN(1) = new_truename;
+
+ return (new_name);
+}
+
+LispObj *
+Lisp_Streamp(LispBuiltin *builtin)
+/*
+ streamp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (STREAMP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_InputStreamP(LispBuiltin *builtin)
+/*
+ input-stream-p stream
+ */
+{
+ LispObj *stream;
+
+ stream = ARGUMENT(0);
+
+ CHECK_STREAM(stream);
+
+ return (stream->data.stream.readable ? T : NIL);
+}
+
+LispObj *
+Lisp_OpenStreamP(LispBuiltin *builtin)
+/*
+ open-stream-p stream
+ */
+{
+ LispObj *stream;
+
+ stream = ARGUMENT(0);
+
+ CHECK_STREAM(stream);
+
+ return (stream->data.stream.readable || stream->data.stream.writable ?
+ T : NIL);
+}
+
+LispObj *
+Lisp_OutputStreamP(LispBuiltin *builtin)
+/*
+ output-stream-p stream
+ */
+{
+ LispObj *stream;
+
+ stream = ARGUMENT(0);
+
+ CHECK_STREAM(stream);
+
+ return (stream->data.stream.writable ? T : NIL);
+}
+
+LispObj *
+Lisp_Open(LispBuiltin *builtin)
+/*
+ open filename &key direction element-type if-exists if-does-not-exist external-format
+ */
+{
+ GC_ENTER();
+ char *string;
+ LispObj *stream = NIL;
+ int mode, flags, direction, exist, noexist, file_exist;
+ LispFile *file;
+
+ LispObj *filename, *odirection, *element_type, *if_exists,
+ *if_does_not_exist, *external_format;
+
+ external_format = ARGUMENT(5);
+ if_does_not_exist = ARGUMENT(4);
+ if_exists = ARGUMENT(3);
+ element_type = ARGUMENT(2);
+ odirection = ARGUMENT(1);
+ filename = ARGUMENT(0);
+
+ if (STRINGP(filename)) {
+ filename = APPLY1(Oparse_namestring, filename);
+ GC_PROTECT(filename);
+ }
+ else if (STREAMP(filename)) {
+ if (filename->data.stream.type != LispStreamFile)
+ LispDestroy("%s: %s is not a FILE-STREAM",
+ STRFUN(builtin), STROBJ(filename));
+ filename = filename->data.stream.pathname;
+ }
+ else {
+ CHECK_PATHNAME(filename);
+ }
+
+ if (odirection != UNSPEC) {
+ direction = -1;
+ if (KEYWORDP(odirection)) {
+ if (odirection == Kprobe)
+ direction = DIR_PROBE;
+ else if (odirection == Kinput)
+ direction = DIR_INPUT;
+ else if (odirection == Koutput)
+ direction = DIR_OUTPUT;
+ else if (odirection == Kio)
+ direction = DIR_IO;
+ }
+ if (direction == -1)
+ LispDestroy("%s: bad :DIRECTION %s",
+ STRFUN(builtin), STROBJ(odirection));
+ }
+ else
+ direction = DIR_INPUT;
+
+ if (element_type != UNSPEC) {
+ /* just check argument... */
+ if (SYMBOLP(element_type) &&
+ ATOMID(element_type) == Scharacter)
+ ; /* do nothing */
+ else if (KEYWORDP(element_type) &&
+ ATOMID(element_type) == Sdefault)
+ ; /* do nothing */
+ else
+ LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
+ STRFUN(builtin), Sdefault, Scharacter, STROBJ(element_type));
+ }
+
+ if (if_exists != UNSPEC) {
+ exist = -1;
+ if (if_exists == NIL)
+ exist = EXT_NIL;
+ else if (KEYWORDP(if_exists)) {
+ if (if_exists == Kerror)
+ exist = EXT_ERROR;
+ else if (if_exists == Knew_version)
+ exist = EXT_NEW_VERSION;
+ else if (if_exists == Krename)
+ exist = EXT_RENAME;
+ else if (if_exists == Krename_and_delete)
+ exist = EXT_RENAME_DELETE;
+ else if (if_exists == Koverwrite)
+ exist = EXT_OVERWRITE;
+ else if (if_exists == Kappend)
+ exist = EXT_APPEND;
+ else if (if_exists == Ksupersede)
+ exist = EXT_SUPERSEDE;
+ }
+ if (exist == -1)
+ LispDestroy("%s: bad :IF-EXISTS %s",
+ STRFUN(builtin), STROBJ(if_exists));
+ }
+ else
+ exist = EXT_ERROR;
+
+ if (if_does_not_exist != UNSPEC) {
+ noexist = -1;
+ if (if_does_not_exist == NIL)
+ noexist = NOEXT_NIL;
+ if (KEYWORDP(if_does_not_exist)) {
+ if (if_does_not_exist == Kerror)
+ noexist = NOEXT_ERROR;
+ else if (if_does_not_exist == Kcreate)
+ noexist = NOEXT_CREATE;
+ }
+ if (noexist == -1)
+ LispDestroy("%s: bad :IF-DOES-NO-EXISTS %s",
+ STRFUN(builtin), STROBJ(if_does_not_exist));
+ }
+ else
+ noexist = direction != DIR_INPUT ? NOEXT_NOTHING : NOEXT_ERROR;
+
+ if (external_format != UNSPEC) {
+ /* just check argument... */
+ if (SYMBOLP(external_format) &&
+ ATOMID(external_format) == Scharacter)
+ ; /* do nothing */
+ else if (KEYWORDP(external_format) &&
+ ATOMID(external_format) == Sdefault)
+ ; /* do nothing */
+ else
+ LispDestroy("%s: only :%s and %s supported for :EXTERNAL-FORMAT, not %s",
+ STRFUN(builtin), Sdefault, Scharacter, STROBJ(external_format));
+ }
+
+ /* string representation of pathname */
+ string = THESTR(CAR(filename->data.pathname));
+ mode = 0;
+
+ file_exist = access(string, F_OK) == 0;
+ if (file_exist) {
+ if (exist == EXT_NIL) {
+ GC_LEAVE();
+ return (NIL);
+ }
+ }
+ else {
+ if (noexist == NOEXT_NIL) {
+ GC_LEAVE();
+ return (NIL);
+ }
+ if (noexist == NOEXT_ERROR)
+ LispDestroy("%s: file %s does not exist",
+ STRFUN(builtin), STROBJ(CAR(filename->data.quote)));
+ else if (noexist == NOEXT_CREATE) {
+ LispFile *tmp = LispFopen(string, FILE_WRITE);
+
+ if (tmp)
+ LispFclose(tmp);
+ else
+ LispDestroy("%s: cannot create file %s",
+ STRFUN(builtin),
+ STROBJ(CAR(filename->data.quote)));
+ }
+ }
+
+ if (direction == DIR_OUTPUT || direction == DIR_IO) {
+ if (file_exist) {
+ if (exist == EXT_ERROR)
+ LispDestroy("%s: file %s already exists",
+ STRFUN(builtin), STROBJ(CAR(filename->data.quote)));
+ if (exist == EXT_RENAME) {
+ /* Add an ending '~' at the end of the backup file */
+ char tmp[PATH_MAX + 1];
+
+ strcpy(tmp, string);
+ if (strlen(tmp) + 1 > PATH_MAX)
+ LispDestroy("%s: backup name for %s too long",
+ STRFUN(builtin),
+ STROBJ(CAR(filename->data.quote)));
+ strcat(tmp, "~");
+ if (rename(string, tmp))
+ LispDestroy("%s: rename: %s",
+ STRFUN(builtin), strerror(errno));
+ mode |= FILE_WRITE;
+ }
+ else if (exist == EXT_OVERWRITE)
+ mode |= FILE_WRITE;
+ else if (exist == EXT_APPEND)
+ mode |= FILE_APPEND;
+ }
+ else
+ mode |= FILE_WRITE;
+ if (direction == DIR_IO)
+ mode |= FILE_IO;
+ }
+ else
+ mode |= FILE_READ;
+
+ file = LispFopen(string, mode);
+ if (file == NULL)
+ LispDestroy("%s: open: %s", STRFUN(builtin), strerror(errno));
+
+ flags = 0;
+ if (direction == DIR_PROBE) {
+ LispFclose(file);
+ file = NULL;
+ }
+ else {
+ if (direction == DIR_INPUT || direction == DIR_IO)
+ flags |= STREAM_READ;
+ if (direction == DIR_OUTPUT || direction == DIR_IO)
+ flags |= STREAM_WRITE;
+ }
+ stream = FILESTREAM(file, filename, flags);
+ GC_LEAVE();
+
+ return (stream);
+}
+
+LispObj *
+Lisp_Close(LispBuiltin *builtin)
+/*
+ close stream &key abort
+ */
+{
+ LispObj *stream, *oabort;
+
+ oabort = ARGUMENT(1);
+ stream = ARGUMENT(0);
+
+ CHECK_STREAM(stream);
+
+ if (stream->data.stream.readable || stream->data.stream.writable) {
+ stream->data.stream.readable = stream->data.stream.writable = 0;
+ if (stream->data.stream.type == LispStreamFile) {
+ LispFclose(stream->data.stream.source.file);
+ stream->data.stream.source.file = NULL;
+ }
+ else if (stream->data.stream.type == LispStreamPipe) {
+ if (IPSTREAMP(stream)) {
+ LispFclose(IPSTREAMP(stream));
+ IPSTREAMP(stream) = NULL;
+ }
+ if (OPSTREAMP(stream)) {
+ LispFclose(OPSTREAMP(stream));
+ OPSTREAMP(stream) = NULL;
+ }
+ if (EPSTREAMP(stream)) {
+ LispFclose(EPSTREAMP(stream));
+ EPSTREAMP(stream) = NULL;
+ }
+ if (PIDPSTREAMP(stream) > 0) {
+ kill(PIDPSTREAMP(stream),
+ oabort == UNSPEC || oabort == NIL ? SIGTERM : SIGKILL);
+ waitpid(PIDPSTREAMP(stream), NULL, 0);
+ }
+ }
+ return (T);
+ }
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Listen(LispBuiltin *builtin)
+/*
+ listen &optional input-stream
+ */
+{
+ LispFile *file = NULL;
+ LispObj *result = NIL;
+
+ LispObj *stream;
+
+ stream = ARGUMENT(0);
+
+ if (stream == UNSPEC)
+ stream = NIL;
+ else if (stream != NIL) {
+ CHECK_STREAM(stream);
+ }
+ else
+ stream = lisp__data.standard_input;
+
+ if (stream->data.stream.readable) {
+ switch (stream->data.stream.type) {
+ case LispStreamString:
+ if (SSTREAMP(stream)->input < SSTREAMP(stream)->length)
+ result = T;
+ break;
+ case LispStreamFile:
+ file = FSTREAMP(stream);
+ break;
+ case LispStreamStandard:
+ file = FSTREAMP(stream);
+ break;
+ case LispStreamPipe:
+ file = IPSTREAMP(stream);
+ break;
+ }
+
+ if (file != NULL) {
+ if (file->available || file->offset < file->length)
+ result = T;
+ else {
+ unsigned char c;
+
+ if (!file->nonblock) {
+ if (fcntl(file->descriptor, F_SETFL, O_NONBLOCK) < 0)
+ LispDestroy("%s: fcntl: %s",
+ STRFUN(builtin), strerror(errno));
+ file->nonblock = 1;
+ }
+ if (read(file->descriptor, &c, 1) == 1) {
+ LispFungetc(file, c);
+ result = T;
+ }
+ }
+ }
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_MakeStringInputStream(LispBuiltin *builtin)
+/*
+ make-string-input-stream string &optional start end
+ */
+{
+ char *string;
+ long start, end, length;
+
+ LispObj *ostring, *ostart, *oend, *result;
+
+ oend = ARGUMENT(2);
+ ostart = ARGUMENT(1);
+ ostring = ARGUMENT(0);
+
+ start = end = 0;
+ CHECK_STRING(ostring);
+ LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
+ &start, &end, &length);
+ string = THESTR(ostring);
+
+ if (end - start != length)
+ length = end - start;
+ result = LSTRINGSTREAM(string + start, STREAM_READ, length);
+
+ return (result);
+}
+
+LispObj *
+Lisp_MakeStringOutputStream(LispBuiltin *builtin)
+/*
+ make-string-output-stream &key element-type
+ */
+{
+ LispObj *element_type;
+
+ element_type = ARGUMENT(0);
+
+ if (element_type != UNSPEC) {
+ /* just check argument... */
+ if (SYMBOLP(element_type) && ATOMID(element_type) == Scharacter)
+ ; /* do nothing */
+ else if (KEYWORDP(element_type) &&
+ ATOMID(element_type) == Sdefault)
+ ; /* do nothing */
+ else
+ LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
+ STRFUN(builtin), Sdefault, Scharacter, STROBJ(element_type));
+ }
+
+ return (LSTRINGSTREAM("", STREAM_WRITE, 1));
+}
+
+LispObj *
+Lisp_GetOutputStreamString(LispBuiltin *builtin)
+/*
+ get-output-stream-string string-output-stream
+ */
+{
+ int length;
+ char *string;
+ LispObj *string_output_stream, *result;
+
+ string_output_stream = ARGUMENT(0);
+
+ if (!STREAMP(string_output_stream) ||
+ string_output_stream->data.stream.type != LispStreamString ||
+ string_output_stream->data.stream.readable ||
+ !string_output_stream->data.stream.writable)
+ LispDestroy("%s: %s is not an output string stream",
+ STRFUN(builtin), STROBJ(string_output_stream));
+
+ string = LispGetSstring(SSTREAMP(string_output_stream), &length);
+ result = LSTRING(string, length);
+
+ /* reset string */
+ SSTREAMP(string_output_stream)->output =
+ SSTREAMP(string_output_stream)->length =
+ SSTREAMP(string_output_stream)->column = 0;
+
+ return (result);
+}
+
+
+/* XXX Non standard functions below
+ */
+LispObj *
+Lisp_MakePipe(LispBuiltin *builtin)
+/*
+ make-pipe command-line &key :direction :element-type :external-format
+ */
+{
+ char *string;
+ LispObj *stream = NIL;
+ int flags, direction;
+ LispFile *error_file;
+ LispPipe *program;
+ int ifd[2];
+ int ofd[2];
+ int efd[2];
+ char *argv[4];
+
+ LispObj *command_line, *odirection, *element_type, *external_format;
+
+ external_format = ARGUMENT(3);
+ element_type = ARGUMENT(2);
+ odirection = ARGUMENT(1);
+ command_line = ARGUMENT(0);
+
+ if (PATHNAMEP(command_line))
+ command_line = CAR(command_line->data.quote);
+ else if (!STRINGP(command_line))
+ LispDestroy("%s: %s is a bad pathname",
+ STRFUN(builtin), STROBJ(command_line));
+
+ if (odirection != UNSPEC) {
+ direction = -1;
+ if (KEYWORDP(odirection)) {
+ if (odirection == Kprobe)
+ direction = DIR_PROBE;
+ else if (odirection == Kinput)
+ direction = DIR_INPUT;
+ else if (odirection == Koutput)
+ direction = DIR_OUTPUT;
+ else if (odirection == Kio)
+ direction = DIR_IO;
+ }
+ if (direction == -1)
+ LispDestroy("%s: bad :DIRECTION %s",
+ STRFUN(builtin), STROBJ(odirection));
+ }
+ else
+ direction = DIR_INPUT;
+
+ if (element_type != UNSPEC) {
+ /* just check argument... */
+ if (SYMBOLP(element_type) && ATOMID(element_type) == Scharacter)
+ ; /* do nothing */
+ else if (KEYWORDP(element_type) && ATOMID(element_type) == Sdefault)
+ ; /* do nothing */
+ else
+ LispDestroy("%s: only :%s and %s supported for :ELEMENT-TYPE, not %s",
+ STRFUN(builtin), Sdefault, Scharacter, STROBJ(element_type));
+ }
+
+ if (external_format != UNSPEC) {
+ /* just check argument... */
+ if (SYMBOLP(external_format) && ATOMID(external_format) == Scharacter)
+ ; /* do nothing */
+ else if (KEYWORDP(external_format) &&
+ ATOMID(external_format) == Sdefault)
+ ; /* do nothing */
+ else
+ LispDestroy("%s: only :%s and %s supported for :EXTERNAL-FORMAT, not %s",
+ STRFUN(builtin), Sdefault, Scharacter, STROBJ(external_format));
+ }
+
+ string = THESTR(command_line);
+ program = LispMalloc(sizeof(LispPipe));
+ if (direction != DIR_PROBE) {
+ argv[0] = "sh";
+ argv[1] = "-c";
+ argv[2] = string;
+ argv[3] = NULL;
+ pipe(ifd);
+ pipe(ofd);
+ pipe(efd);
+ if ((program->pid = fork()) == 0) {
+ close(0);
+ close(1);
+ close(2);
+ dup2(ofd[0], 0);
+ dup2(ifd[1], 1);
+ dup2(efd[1], 2);
+ close(ifd[0]);
+ close(ifd[1]);
+ close(ofd[0]);
+ close(ofd[1]);
+ close(efd[0]);
+ close(efd[1]);
+ execve("/bin/sh", argv, environ);
+ exit(-1);
+ }
+ else if (program->pid < 0)
+ LispDestroy("%s: fork: %s", STRFUN(builtin), strerror(errno));
+
+ program->input = LispFdopen(ifd[0], FILE_READ | FILE_UNBUFFERED);
+ close(ifd[1]);
+ program->output = LispFdopen(ofd[1], FILE_WRITE | FILE_UNBUFFERED);
+ close(ofd[0]);
+ error_file = LispFdopen(efd[0], FILE_READ | FILE_UNBUFFERED);
+ close(efd[1]);
+ }
+ else {
+ program->pid = -1;
+ program->input = program->output = error_file = NULL;
+ }
+
+ flags = direction == DIR_PROBE ? 0 : STREAM_READ;
+ program->errorp = FILESTREAM(error_file, command_line, flags);
+
+ flags = 0;
+ if (direction != DIR_PROBE) {
+ if (direction == DIR_INPUT || direction == DIR_IO)
+ flags |= STREAM_READ;
+ if (direction == DIR_OUTPUT || direction == DIR_IO)
+ flags |= STREAM_WRITE;
+ }
+ stream = PIPESTREAM(program, command_line, flags);
+ LispMused(program);
+
+ return (stream);
+}
+
+/* Helper function, primarily for use with the xt module
+ */
+LispObj *
+Lisp_PipeBroken(LispBuiltin *builtin)
+/*
+ pipe-broken pipe-stream
+ */
+{
+ int pid, status, retval;
+ LispObj *result = NIL;
+
+ LispObj *pipe_stream;
+
+ pipe_stream = ARGUMENT(0);
+
+ if (!STREAMP(pipe_stream) ||
+ pipe_stream->data.stream.type != LispStreamPipe)
+ LispDestroy("%s: %s is not a pipe stream",
+ STRFUN(builtin), STROBJ(pipe_stream));
+
+ if ((pid = PIDPSTREAMP(pipe_stream)) > 0) {
+ retval = waitpid(pid, &status, WNOHANG | WUNTRACED);
+ if (retval == pid || (retval == -1 && errno == ECHILD))
+ result = T;
+ }
+
+ return (result);
+}
+
+/*
+ Helper function, so that it is not required to redirect error output
+ */
+LispObj *
+Lisp_PipeErrorStream(LispBuiltin *builtin)
+/*
+ pipe-error-stream pipe-stream
+ */
+{
+ LispObj *pipe_stream;
+
+ pipe_stream = ARGUMENT(0);
+
+ if (!STREAMP(pipe_stream) ||
+ pipe_stream->data.stream.type != LispStreamPipe)
+ LispDestroy("%s: %s is not a pipe stream",
+ STRFUN(builtin), STROBJ(pipe_stream));
+
+ return (pipe_stream->data.stream.source.program->errorp);
+}
+
+/*
+ Helper function, primarily for use with the xt module
+ */
+LispObj *
+Lisp_PipeInputDescriptor(LispBuiltin *builtin)
+/*
+ pipe-input-descriptor pipe-stream
+ */
+{
+ LispObj *pipe_stream;
+
+ pipe_stream = ARGUMENT(0);
+
+ if (!STREAMP(pipe_stream) ||
+ pipe_stream->data.stream.type != LispStreamPipe)
+ LispDestroy("%s: %s is not a pipe stream",
+ STRFUN(builtin), STROBJ(pipe_stream));
+ if (!IPSTREAMP(pipe_stream))
+ LispDestroy("%s: pipe %s is unreadable",
+ STRFUN(builtin), STROBJ(pipe_stream));
+
+ return (INTEGER(LispFileno(IPSTREAMP(pipe_stream))));
+}
+
+/*
+ Helper function, primarily for use with the xt module
+ */
+LispObj *
+Lisp_PipeErrorDescriptor(LispBuiltin *builtin)
+/*
+ pipe-error-descriptor pipe-stream
+ */
+{
+ LispObj *pipe_stream;
+
+ pipe_stream = ARGUMENT(0);
+
+ if (!STREAMP(pipe_stream) ||
+ pipe_stream->data.stream.type != LispStreamPipe)
+ LispDestroy("%s: %s is not a pipe stream",
+ STRFUN(builtin), STROBJ(pipe_stream));
+ if (!EPSTREAMP(pipe_stream))
+ LispDestroy("%s: pipe %s is closed",
+ STRFUN(builtin), STROBJ(pipe_stream));
+
+ return (INTEGER(LispFileno(EPSTREAMP(pipe_stream))));
+}
diff --git a/lisp/stream.h b/lisp/stream.h
new file mode 100644
index 0000000..c510224
--- /dev/null
+++ b/lisp/stream.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/stream.h,v 1.8 2002/12/06 03:25:27 paulo Exp $ */
+
+#ifndef Lisp_stream_h
+#define Lisp_stream_h
+
+#include "io.h"
+#include "internal.h"
+
+void LispStreamInit(void);
+
+LispObj *Lisp_DeleteFile(LispBuiltin*);
+LispObj *Lisp_RenameFile(LispBuiltin*);
+LispObj *Lisp_InputStreamP(LispBuiltin*);
+LispObj *Lisp_OpenStreamP(LispBuiltin*);
+LispObj *Lisp_OutputStreamP(LispBuiltin*);
+LispObj *Lisp_Open(LispBuiltin*);
+LispObj *Lisp_MakePipe(LispBuiltin*);
+LispObj *Lisp_PipeBroken(LispBuiltin*);
+LispObj *Lisp_PipeErrorStream(LispBuiltin*);
+LispObj *Lisp_PipeInputDescriptor(LispBuiltin*);
+LispObj *Lisp_PipeErrorDescriptor(LispBuiltin*);
+LispObj *Lisp_Close(LispBuiltin*);
+LispObj *Lisp_Listen(LispBuiltin*);
+LispObj *Lisp_Streamp(LispBuiltin*);
+LispObj *Lisp_MakeStringInputStream(LispBuiltin*);
+LispObj *Lisp_MakeStringOutputStream(LispBuiltin*);
+LispObj *Lisp_GetOutputStreamString(LispBuiltin*);
+
+#endif /* Lisp_stream_h */
diff --git a/lisp/string.c b/lisp/string.c
new file mode 100644
index 0000000..95952bd
--- /dev/null
+++ b/lisp/string.c
@@ -0,0 +1,1383 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/string.c,v 1.22 2002/12/04 05:27:58 paulo Exp $ */
+
+#include "helper.h"
+#include "read.h"
+#include "string.h"
+#include "private.h"
+#include <ctype.h>
+
+#define CHAR_LESS 1
+#define CHAR_LESS_EQUAL 2
+#define CHAR_EQUAL 3
+#define CHAR_GREATER_EQUAL 4
+#define CHAR_GREATER 5
+#define CHAR_NOT_EQUAL 6
+
+#define CHAR_ALPHAP 1
+#define CHAR_DOWNCASE 2
+#define CHAR_UPCASE 3
+#define CHAR_INT 4
+#define CHAR_BOTHP 5
+#define CHAR_UPPERP 6
+#define CHAR_LOWERP 7
+#define CHAR_GRAPHICP 8
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * Prototypes
+ */
+static LispObj *LispCharCompare(LispBuiltin*, int, int);
+static LispObj *LispStringCompare(LispBuiltin*, int, int);
+static LispObj *LispCharOp(LispBuiltin*, int);
+static LispObj *LispStringTrim(LispBuiltin*, int, int, int);
+static LispObj *LispStringUpcase(LispBuiltin*, int);
+static LispObj *LispStringDowncase(LispBuiltin*, int);
+static LispObj *LispStringCapitalize(LispBuiltin*, int);
+
+/*
+ * Implementation
+ */
+static LispObj *
+LispCharCompare(LispBuiltin *builtin, int operation, int ignore_case)
+{
+ LispObj *object;
+ int cmp, value, next_value;
+
+ LispObj *character, *more_characters;
+
+ more_characters = ARGUMENT(1);
+ character = ARGUMENT(0);
+
+ CHECK_SCHAR(character);
+ value = SCHAR_VALUE(character);
+ if (ignore_case && islower(value))
+ value = toupper(value);
+
+ if (!CONSP(more_characters))
+ return (T);
+
+ /* First check if all parameters are characters */
+ for (object = more_characters; CONSP(object); object = CDR(object))
+ CHECK_SCHAR(CAR(object));
+
+ /* All characters in list must be different */
+ if (operation == CHAR_NOT_EQUAL) {
+ /* Compare all characters */
+ do {
+ for (object = more_characters; CONSP(object); object = CDR(object)) {
+ character = CAR(object);
+ next_value = SCHAR_VALUE(character);
+ if (ignore_case && islower(next_value))
+ next_value = toupper(next_value);
+ if (value == next_value)
+ return (NIL);
+ }
+ value = SCHAR_VALUE(CAR(more_characters));
+ if (ignore_case && islower(value))
+ value = toupper(value);
+ more_characters = CDR(more_characters);
+ } while (CONSP(more_characters));
+
+ return (T);
+ }
+
+ /* Linearly compare characters */
+ for (; CONSP(more_characters); more_characters = CDR(more_characters)) {
+ character = CAR(more_characters);
+ next_value = SCHAR_VALUE(character);
+ if (ignore_case && islower(next_value))
+ next_value = toupper(next_value);
+
+ switch (operation) {
+ case CHAR_LESS: cmp = value < next_value; break;
+ case CHAR_LESS_EQUAL: cmp = value <= next_value; break;
+ case CHAR_EQUAL: cmp = value == next_value; break;
+ case CHAR_GREATER_EQUAL: cmp = value >= next_value; break;
+ case CHAR_GREATER: cmp = value > next_value; break;
+ default: cmp = 0; break;
+ }
+
+ if (!cmp)
+ return (NIL);
+ value = next_value;
+ }
+
+ return (T);
+}
+
+LispObj *
+Lisp_CharLess(LispBuiltin *builtin)
+/*
+ char< character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_LESS, 0));
+}
+
+LispObj *
+Lisp_CharLessEqual(LispBuiltin *builtin)
+/*
+ char<= character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_LESS_EQUAL, 0));
+}
+
+LispObj *
+Lisp_CharEqual_(LispBuiltin *builtin)
+/*
+ char= character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_EQUAL, 0));
+}
+
+LispObj *
+Lisp_CharGreater(LispBuiltin *builtin)
+/*
+ char> character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_GREATER, 0));
+}
+
+LispObj *
+Lisp_CharGreaterEqual(LispBuiltin *builtin)
+/*
+ char>= character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_GREATER_EQUAL, 0));
+}
+
+LispObj *
+Lisp_CharNotEqual_(LispBuiltin *builtin)
+/*
+ char/= character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_NOT_EQUAL, 0));
+}
+
+LispObj *
+Lisp_CharLessp(LispBuiltin *builtin)
+/*
+ char-lessp character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_LESS, 1));
+}
+
+LispObj *
+Lisp_CharNotGreaterp(LispBuiltin *builtin)
+/*
+ char-not-greaterp character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_LESS_EQUAL, 1));
+}
+
+LispObj *
+Lisp_CharEqual(LispBuiltin *builtin)
+/*
+ char-equalp character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_EQUAL, 1));
+}
+
+LispObj *
+Lisp_CharGreaterp(LispBuiltin *builtin)
+/*
+ char-greaterp character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_GREATER, 1));
+}
+
+LispObj *
+Lisp_CharNotLessp(LispBuiltin *builtin)
+/*
+ char-not-lessp &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_GREATER_EQUAL, 1));
+}
+
+LispObj *
+Lisp_CharNotEqual(LispBuiltin *builtin)
+/*
+ char-not-equal character &rest more-characters
+ */
+{
+ return (LispCharCompare(builtin, CHAR_NOT_EQUAL, 1));
+}
+
+static LispObj *
+LispCharOp(LispBuiltin *builtin, int operation)
+{
+ int value;
+ LispObj *result, *character;
+
+ character = ARGUMENT(0);
+ CHECK_SCHAR(character);
+ value = (int)SCHAR_VALUE(character);
+
+ switch (operation) {
+ case CHAR_ALPHAP:
+ result = isalpha(value) ? T : NIL;
+ break;
+ case CHAR_DOWNCASE:
+ result = SCHAR(tolower(value));
+ break;
+ case CHAR_UPCASE:
+ result = SCHAR(toupper(value));
+ break;
+ case CHAR_INT:
+ result = FIXNUM(value);
+ break;
+ case CHAR_BOTHP:
+ result = isupper(value) || islower(value) ? T : NIL;
+ break;
+ case CHAR_UPPERP:
+ result = isupper(value) ? T : NIL;
+ break;
+ case CHAR_LOWERP:
+ result = islower(value) ? T : NIL;
+ break;
+ case CHAR_GRAPHICP:
+ result = value == ' ' || isgraph(value) ? T : NIL;
+ break;
+ default:
+ result = NIL;
+ break;
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_AlphaCharP(LispBuiltin *builtin)
+/*
+ alpha-char-p char
+ */
+{
+ return (LispCharOp(builtin, CHAR_ALPHAP));
+}
+
+LispObj *
+Lisp_CharDowncase(LispBuiltin *builtin)
+/*
+ char-downcase character
+ */
+{
+ return (LispCharOp(builtin, CHAR_DOWNCASE));
+}
+
+LispObj *
+Lisp_CharInt(LispBuiltin *builtin)
+/*
+ char-int character
+ char-code character
+ */
+{
+ return (LispCharOp(builtin, CHAR_INT));
+}
+
+LispObj *
+Lisp_CharUpcase(LispBuiltin *builtin)
+/*
+ char-upcase character
+ */
+{
+ return (LispCharOp(builtin, CHAR_UPCASE));
+}
+
+LispObj *
+Lisp_BothCaseP(LispBuiltin *builtin)
+/*
+ both-case-p character
+ */
+{
+ return (LispCharOp(builtin, CHAR_BOTHP));
+}
+
+LispObj *
+Lisp_UpperCaseP(LispBuiltin *builtin)
+/*
+ upper-case-p character
+ */
+{
+ return (LispCharOp(builtin, CHAR_UPPERP));
+}
+
+LispObj *
+Lisp_LowerCaseP(LispBuiltin *builtin)
+/*
+ upper-case-p character
+ */
+{
+ return (LispCharOp(builtin, CHAR_LOWERP));
+}
+
+LispObj *
+Lisp_GraphicCharP(LispBuiltin *builtin)
+/*
+ graphic-char-p char
+ */
+{
+ return (LispCharOp(builtin, CHAR_GRAPHICP));
+}
+
+LispObj *
+Lisp_Char(LispBuiltin *builtin)
+/*
+ char string index
+ schar simple-string index
+ */
+{
+ char *string;
+ long offset, length;
+
+ LispObj *ostring, *oindex;
+
+ oindex = ARGUMENT(1);
+ ostring = ARGUMENT(0);
+
+ CHECK_STRING(ostring);
+ CHECK_INDEX(oindex);
+ offset = FIXNUM_VALUE(oindex);
+ string = THESTR(ostring);
+ length = STRLEN(ostring);
+
+ if (offset >= length)
+ LispDestroy("%s: index %ld too large for string length %ld",
+ STRFUN(builtin), offset, length);
+
+ return (SCHAR(string[offset]));
+}
+
+/* helper function for setf
+ * DONT explicitly call. Non standard function
+ */
+LispObj *
+Lisp_XeditCharStore(LispBuiltin *builtin)
+/*
+ xedit::char-store string index value
+ */
+{
+ int character;
+ long offset, length;
+ LispObj *ostring, *oindex, *ovalue;
+
+ ovalue = ARGUMENT(2);
+ oindex = ARGUMENT(1);
+ ostring = ARGUMENT(0);
+
+ CHECK_STRING(ostring);
+ CHECK_INDEX(oindex);
+ length = STRLEN(ostring);
+ offset = FIXNUM_VALUE(oindex);
+ if (offset >= length)
+ LispDestroy("%s: index %ld too large for string length %ld",
+ STRFUN(builtin), offset, length);
+ CHECK_SCHAR(ovalue);
+ CHECK_STRING_WRITABLE(ostring);
+
+ character = SCHAR_VALUE(ovalue);
+
+ if (character < 0 || character > 255)
+ LispDestroy("%s: cannot represent character %d",
+ STRFUN(builtin), character);
+
+ THESTR(ostring)[offset] = character;
+
+ return (ovalue);
+}
+
+LispObj *
+Lisp_Character(LispBuiltin *builtin)
+/*
+ character object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (LispCharacterCoerce(builtin, object));
+}
+
+LispObj *
+Lisp_Characterp(LispBuiltin *builtin)
+/*
+ characterp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (SCHARP(object) ? T : NIL);
+}
+
+LispObj *
+Lisp_DigitChar(LispBuiltin *builtin)
+/*
+ digit-char weight &optional radix
+ */
+{
+ long radix = 10, weight;
+ LispObj *oweight, *oradix, *result = NIL;
+
+ oradix = ARGUMENT(1);
+ oweight = ARGUMENT(0);
+
+ CHECK_FIXNUM(oweight);
+ weight = FIXNUM_VALUE(oweight);
+
+ if (oradix != UNSPEC) {
+ CHECK_INDEX(oradix);
+ radix = FIXNUM_VALUE(oradix);
+ }
+ if (radix < 2 || radix > 36)
+ LispDestroy("%s: radix must be >= 2 and <= 36, not %ld",
+ STRFUN(builtin), radix);
+
+ if (weight >= 0 && weight < radix) {
+ if (weight < 9)
+ weight += '0';
+ else
+ weight += 'A' - 10;
+ result = SCHAR(weight);
+ }
+
+ return (result);
+}
+
+LispObj *
+Lisp_DigitCharP(LispBuiltin *builtin)
+/*
+ digit-char-p character &optional radix
+ */
+{
+ long radix = 10, character;
+ LispObj *ochar, *oradix, *result = NIL;
+
+ oradix = ARGUMENT(1);
+ ochar = ARGUMENT(0);
+
+ CHECK_SCHAR(ochar);
+ character = SCHAR_VALUE(ochar);
+ if (oradix != UNSPEC) {
+ CHECK_INDEX(oradix);
+ radix = FIXNUM_VALUE(oradix);
+ }
+ if (radix < 2 || radix > 36)
+ LispDestroy("%s: radix must be >= 2 and <= 36, not %ld",
+ STRFUN(builtin), radix);
+
+ if (character >= '0' && character <= '9')
+ character -= '0';
+ else if (character >= 'A' && character <= 'Z')
+ character -= 'A' - 10;
+ else if (character >= 'a' && character <= 'z')
+ character -= 'a' - 10;
+ if (character < radix)
+ result = FIXNUM(character);
+
+ return (result);
+}
+
+LispObj *
+Lisp_IntChar(LispBuiltin *builtin)
+/*
+ int-char integer
+ code-char integer
+ */
+{
+ long character = 0;
+ LispObj *integer;
+
+ integer = ARGUMENT(0);
+
+ CHECK_FIXNUM(integer);
+ character = FIXNUM_VALUE(integer);
+
+ return (character >= 0 && character < 0xff ? SCHAR(character) : NIL);
+}
+
+/* XXX ignoring element-type */
+LispObj *
+Lisp_MakeString(LispBuiltin *builtin)
+/*
+ make-string size &key initial-element element-type
+ */
+{
+ long length;
+ char *string, initial;
+
+ LispObj *size, *initial_element, *element_type;
+
+ element_type = ARGUMENT(2);
+ initial_element = ARGUMENT(1);
+ size = ARGUMENT(0);
+
+ CHECK_INDEX(size);
+ length = FIXNUM_VALUE(size);
+ if (initial_element != UNSPEC) {
+ CHECK_SCHAR(initial_element);
+ initial = SCHAR_VALUE(initial_element);
+ }
+ else
+ initial = 0;
+
+ string = LispMalloc(length + 1);
+ memset(string, initial, length);
+ string[length] = '\0';
+
+ return (LSTRING2(string, length));
+}
+
+LispObj *
+Lisp_ParseInteger(LispBuiltin *builtin)
+/*
+ parse-integer string &key start end radix junk-allowed
+ */
+{
+ GC_ENTER();
+ char *ptr, *string;
+ int character, junk, sign, overflow;
+ long i, start, end, radix, length, integer, check;
+ LispObj *result;
+
+ LispObj *ostring, *ostart, *oend, *oradix, *junk_allowed;
+
+ junk_allowed = ARGUMENT(4);
+ oradix = ARGUMENT(3);
+ oend = ARGUMENT(2);
+ ostart = ARGUMENT(1);
+ ostring = ARGUMENT(0);
+
+ start = end = radix = 0;
+ result = NIL;
+
+ CHECK_STRING(ostring);
+ LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
+ &start, &end, &length);
+ string = THESTR(ostring);
+ if (radix < 2 || radix > 36)
+ LispDestroy("%s: :RADIX %ld must be in the range 2 to 36",
+ STRFUN(builtin), radix);
+
+ integer = check = 0;
+ ptr = string + start;
+ sign = overflow = 0;
+
+ /* Skip leading white spaces */
+ for (i = start; i < end && *ptr && isspace(*ptr); ptr++, i++)
+ ;
+
+ /* Check for sign specification */
+ if (i < end && (*ptr == '-' || *ptr == '+')) {
+ sign = *ptr == '-';
+ ++ptr;
+ ++i;
+ }
+
+ for (junk = 0; i < end; i++, ptr++) {
+ character = *ptr;
+ if (islower(character))
+ character = toupper(character);
+ if (character >= '0' && character <= '9') {
+ if (character - '0' >= radix)
+ junk = 1;
+ else {
+ check = integer;
+ integer = integer * radix + character - '0';
+ }
+ }
+ else if (character >= 'A' && character <= 'Z') {
+ if (character - 'A' + 10 >= radix)
+ junk = 1;
+ else {
+ check = integer;
+ integer = integer * radix + character - 'A' + 10;
+ }
+ }
+ else {
+ if (isspace(character))
+ break;
+ junk = 1;
+ }
+
+ if (junk)
+ break;
+
+ if (!overflow && check > integer)
+ overflow = 1;
+ /* keep looping just to count read bytes */
+ }
+
+ if (!junk)
+ /* Skip white spaces */
+ for (; i < end && *ptr && isspace(*ptr); ptr++, i++)
+ ;
+
+ if ((junk || ptr == string) &&
+ (junk_allowed == UNSPEC || junk_allowed == NIL))
+ LispDestroy("%s: %s has a bad integer representation",
+ STRFUN(builtin), STROBJ(ostring));
+ else if (ptr == string)
+ result = NIL;
+ else if (overflow) {
+ mpi *bigi = LispMalloc(sizeof(mpi));
+ char *str;
+
+ length = end - start + sign;
+ str = LispMalloc(length + 1);
+
+ strncpy(str, string - sign, length + sign);
+ str[length + sign] = '\0';
+ mpi_init(bigi);
+ mpi_setstr(bigi, str, radix);
+ LispFree(str);
+ result = BIGNUM(bigi);
+ }
+ else
+ result = INTEGER(sign ? -integer : integer);
+
+ GC_PROTECT(result);
+ RETURN(0) = FIXNUM(i);
+ RETURN_COUNT = 1;
+ GC_LEAVE();
+
+ return (result);
+}
+
+LispObj *
+Lisp_String(LispBuiltin *builtin)
+/*
+ string object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (LispStringCoerce(builtin, object));
+}
+
+LispObj *
+Lisp_Stringp(LispBuiltin *builtin)
+/*
+ stringp object
+ */
+{
+ LispObj *object;
+
+ object = ARGUMENT(0);
+
+ return (STRINGP(object) ? T : NIL);
+}
+
+/* XXX preserve-whitespace is being ignored */
+LispObj *
+Lisp_ReadFromString(LispBuiltin *builtin)
+/*
+ read-from-string string &optional eof-error-p eof-value &key start end preserve-whitespace
+ */
+{
+ GC_ENTER();
+ char *string;
+ LispObj *stream, *result;
+ long length, start, end, bytes_read;
+
+ LispObj *ostring, *eof_error_p, *eof_value,
+ *ostart, *oend, *preserve_white_space;
+
+ preserve_white_space = ARGUMENT(5);
+ oend = ARGUMENT(4);
+ ostart = ARGUMENT(3);
+ eof_value = ARGUMENT(2);
+ eof_error_p = ARGUMENT(1);
+ ostring = ARGUMENT(0);
+
+ CHECK_STRING(ostring);
+ string = THESTR(ostring);
+ LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
+ &start, &end, &length);
+
+ if (start > 0 || end < length)
+ length = end - start;
+ stream = LSTRINGSTREAM(string + start, STREAM_READ, length);
+
+ if (eof_value == UNSPEC)
+ eof_value = NIL;
+
+ LispPushInput(stream);
+ result = LispRead();
+ /* stream->data.stream.source.string->input is
+ * the offset of the last byte read in string */
+ bytes_read = stream->data.stream.source.string->input;
+ LispPopInput(stream);
+
+ if (result == NULL) {
+ if (eof_error_p == NIL)
+ result = eof_value;
+ else
+ LispDestroy("%s: unexpected end of input", STRFUN(builtin));
+ }
+
+ GC_PROTECT(result);
+ RETURN(0) = FIXNUM(start + bytes_read);
+ RETURN_COUNT = 1;
+ GC_LEAVE();
+
+ return (result);
+}
+
+static LispObj *
+LispStringTrim(LispBuiltin *builtin, int left, int right, int inplace)
+/*
+ string-{,left-,right-}trim character-bag string
+*/
+{
+ unsigned char *string;
+ long start, end, length;
+
+ LispObj *ochars, *ostring;
+
+ ostring = ARGUMENT(1);
+ ochars = ARGUMENT(0);
+
+ if (!POINTERP(ochars) || !(XSTRINGP(ochars) || XCONSP(ochars))) {
+ if (ARRAYP(ochars) && ochars->data.array.rank == 1)
+ ochars = ochars->data.array.list;
+ else
+ LispDestroy("%s: %s is not a sequence",
+ STRFUN(builtin), STROBJ(ochars));
+ }
+ CHECK_STRING(ostring);
+
+ string = (unsigned char*)THESTR(ostring);
+ length = STRLEN(ostring);
+
+ start = 0;
+ end = length;
+
+ if (XSTRINGP(ochars)) {
+ unsigned char *chars = (unsigned char*)THESTR(ochars);
+ long i, clength = STRLEN(ochars);
+
+ if (left) {
+ for (; start < end; start++) {
+ for (i = 0; i < clength; i++)
+ if (string[start] == chars[i])
+ break;
+ if (i >= clength)
+ break;
+ }
+ }
+ if (right) {
+ for (--end; end >= 0; end--) {
+ for (i = 0; i < clength; i++)
+ if (string[end] == chars[i])
+ break;
+ if (i >= clength)
+ break;
+ }
+ ++end;
+ }
+ }
+ else {
+ LispObj *ochar, *list;
+
+ if (left) {
+ for (; start < end; start++) {
+ for (list = ochars; CONSP(list); list = CDR(list)) {
+ ochar = CAR(list);
+ if (SCHARP(ochar) && string[start] == SCHAR_VALUE(ochar))
+ break;
+ }
+ if (!CONSP(list))
+ break;
+ }
+ }
+ if (right) {
+ for (--end; end >= 0; end--) {
+ for (list = ochars; CONSP(list); list = CDR(list)) {
+ ochar = CAR(list);
+ if (SCHARP(ochar) && string[end] == SCHAR_VALUE(ochar))
+ break;
+ }
+ if (!CONSP(list))
+ break;
+ }
+ ++end;
+ }
+ }
+
+ if (start == 0 && end == length)
+ return (ostring);
+
+ length = end - start;
+
+ if (inplace) {
+ CHECK_STRING_WRITABLE(ostring);
+ memmove(string, string + start, length);
+ string[length] = '\0';
+ STRLEN(ostring) = length;
+ }
+ else {
+ string = LispMalloc(length + 1);
+ memcpy(string, THESTR(ostring) + start, length);
+ string[length] = '\0';
+ ostring = LSTRING2((char*)string, length);
+ }
+
+ return (ostring);
+}
+
+LispObj *
+Lisp_StringTrim(LispBuiltin *builtin)
+/*
+ string-trim character-bag string
+ */
+{
+ return (LispStringTrim(builtin, 1, 1, 0));
+}
+
+LispObj *
+Lisp_NstringTrim(LispBuiltin *builtin)
+/*
+ ext::nstring-trim character-bag string
+ */
+{
+ return (LispStringTrim(builtin, 1, 1, 1));
+}
+
+LispObj *
+Lisp_StringLeftTrim(LispBuiltin *builtin)
+/*
+ string-left-trim character-bag string
+ */
+{
+ return (LispStringTrim(builtin, 1, 0, 0));
+}
+
+LispObj *
+Lisp_NstringLeftTrim(LispBuiltin *builtin)
+/*
+ ext::nstring-left-trim character-bag string
+ */
+{
+ return (LispStringTrim(builtin, 1, 0, 1));
+}
+
+LispObj *
+Lisp_StringRightTrim(LispBuiltin *builtin)
+/*
+ string-right-trim character-bag string
+ */
+{
+ return (LispStringTrim(builtin, 0, 1, 0));
+}
+
+LispObj *
+Lisp_NstringRightTrim(LispBuiltin *builtin)
+/*
+ ext::nstring-right-trim character-bag string
+ */
+{
+ return (LispStringTrim(builtin, 0, 1, 1));
+}
+
+static LispObj *
+LispStringCompare(LispBuiltin *builtin, int function, int ignore_case)
+{
+ int cmp1, cmp2;
+ LispObj *fixnum;
+ unsigned char *string1, *string2;
+ long start1, end1, start2, end2, offset, length;
+
+ LispGetStringArgs(builtin, (char**)&string1, (char**)&string2,
+ &start1, &end1, &start2, &end2);
+
+ string1 += start1;
+ string2 += start2;
+
+ if (function == CHAR_EQUAL) {
+ length = end1 - start1;
+
+ if (length != (end2 - start2))
+ return (NIL);
+
+ if (!ignore_case)
+ return (memcmp(string1, string2, length) ? NIL : T);
+
+ for (; length; length--, string1++, string2++)
+ if (toupper(*string1) != toupper(*string2))
+ return (NIL);
+ return (T);
+ }
+
+ end1 -= start1;
+ end2 -= start2;
+ length = MIN(end1, end2);
+ for (offset = 0;
+ offset < length;
+ string1++, string2++, offset++, start1++, start2++) {
+ cmp1 = *string1;
+ cmp2 = *string2;
+ if (ignore_case) {
+ cmp1 = toupper(cmp1);
+ cmp2 = toupper(cmp2);
+ }
+ if (cmp1 != cmp2) {
+ fixnum = FIXNUM(start1);
+ switch (function) {
+ case CHAR_LESS:
+ return ((cmp1 < cmp2) ? fixnum : NIL);
+ case CHAR_LESS_EQUAL:
+ return ((cmp1 <= cmp2) ? fixnum : NIL);
+ case CHAR_NOT_EQUAL:
+ return (fixnum);
+ case CHAR_GREATER_EQUAL:
+ return ((cmp1 >= cmp2) ? fixnum : NIL);
+ case CHAR_GREATER:
+ return ((cmp1 > cmp2) ? fixnum : NIL);
+ }
+ }
+ }
+
+ fixnum = FIXNUM(start1);
+ switch (function) {
+ case CHAR_LESS:
+ return (start1 >= end1 && start2 < end2 ? fixnum : NIL);
+ case CHAR_LESS_EQUAL:
+ return (start1 >= end1 ? fixnum : NIL);
+ case CHAR_NOT_EQUAL:
+ return (start1 >= end1 && start2 >= end2 ? NIL : fixnum);
+ case CHAR_GREATER_EQUAL:
+ return (start2 >= end2 ? fixnum : NIL);
+ case CHAR_GREATER:
+ return (start2 >= end2 && start1 < end1 ? fixnum : NIL);
+ }
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_StringEqual_(LispBuiltin *builtin)
+/*
+ string= string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_EQUAL, 0));
+}
+
+LispObj *
+Lisp_StringLess(LispBuiltin *builtin)
+/*
+ string< string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_LESS, 0));
+}
+
+LispObj *
+Lisp_StringGreater(LispBuiltin *builtin)
+/*
+ string> string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_GREATER, 0));
+}
+
+LispObj *
+Lisp_StringLessEqual(LispBuiltin *builtin)
+/*
+ string<= string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_LESS_EQUAL, 0));
+}
+
+LispObj *
+Lisp_StringGreaterEqual(LispBuiltin *builtin)
+/*
+ string>= string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_GREATER_EQUAL, 0));
+}
+
+LispObj *
+Lisp_StringNotEqual_(LispBuiltin *builtin)
+/*
+ string/= string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_NOT_EQUAL, 0));
+}
+
+LispObj *
+Lisp_StringEqual(LispBuiltin *builtin)
+/*
+ string-equal string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_EQUAL, 1));
+}
+
+LispObj *
+Lisp_StringLessp(LispBuiltin *builtin)
+/*
+ string-lessp string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_LESS, 1));
+}
+
+LispObj *
+Lisp_StringGreaterp(LispBuiltin *builtin)
+/*
+ string-greaterp string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_GREATER, 1));
+}
+
+LispObj *
+Lisp_StringNotGreaterp(LispBuiltin *builtin)
+/*
+ string-not-greaterp string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_LESS_EQUAL, 1));
+}
+
+LispObj *
+Lisp_StringNotLessp(LispBuiltin *builtin)
+/*
+ string-not-lessp string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_GREATER_EQUAL, 1));
+}
+
+LispObj *
+Lisp_StringNotEqual(LispBuiltin *builtin)
+/*
+ string-not-equal string1 string2 &key start1 end1 start2 end2
+ */
+{
+ return (LispStringCompare(builtin, CHAR_NOT_EQUAL, 1));
+}
+
+LispObj *
+LispStringUpcase(LispBuiltin *builtin, int inplace)
+/*
+ string-upcase string &key start end
+ nstring-upcase string &key start end
+ */
+{
+ LispObj *result;
+ char *string, *newstring;
+ long start, end, length, offset;
+
+ LispObj *ostring, *ostart, *oend;
+
+ oend = ARGUMENT(2);
+ ostart = ARGUMENT(1);
+ ostring = ARGUMENT(0);
+ CHECK_STRING(ostring);
+ LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
+ &start, &end, &offset);
+ result = ostring;
+ string = THESTR(ostring);
+ length = STRLEN(ostring);
+
+ /* first check if something need to be done */
+ for (offset = start; offset < end; offset++)
+ if (string[offset] != toupper(string[offset]))
+ break;
+
+ if (offset >= end)
+ return (result);
+
+ if (inplace) {
+ CHECK_STRING_WRITABLE(ostring);
+ newstring = string;
+ }
+ else {
+ /* upcase a copy of argument */
+ newstring = LispMalloc(length + 1);
+ if (offset)
+ memcpy(newstring, string, offset);
+ if (length > end)
+ memcpy(newstring + end, string + end, length - end);
+ newstring[length] = '\0';
+ }
+
+ for (; offset < end; offset++)
+ newstring[offset] = toupper(string[offset]);
+
+ if (!inplace)
+ result = LSTRING2(newstring, length);
+
+ return (result);
+}
+
+LispObj *
+Lisp_StringUpcase(LispBuiltin *builtin)
+/*
+ string-upcase string &key start end
+ */
+{
+ return (LispStringUpcase(builtin, 0));
+}
+
+LispObj *
+Lisp_NstringUpcase(LispBuiltin *builtin)
+/*
+ nstring-upcase string &key start end
+ */
+{
+ return (LispStringUpcase(builtin, 1));
+}
+
+LispObj *
+LispStringDowncase(LispBuiltin *builtin, int inplace)
+/*
+ string-downcase string &key start end
+ nstring-downcase string &key start end
+ */
+{
+ LispObj *result;
+ char *string, *newstring;
+ long start, end, length, offset;
+
+ LispObj *ostring, *ostart, *oend;
+
+ oend = ARGUMENT(2);
+ ostart = ARGUMENT(1);
+ ostring = ARGUMENT(0);
+ CHECK_STRING(ostring);
+ LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
+ &start, &end, &offset);
+ result = ostring;
+ string = THESTR(ostring);
+ length = STRLEN(ostring);
+
+ /* first check if something need to be done */
+ for (offset = start; offset < end; offset++)
+ if (string[offset] != tolower(string[offset]))
+ break;
+
+ if (offset >= end)
+ return (result);
+
+ if (inplace) {
+ CHECK_STRING_WRITABLE(ostring);
+ newstring = string;
+ }
+ else {
+ /* downcase a copy of argument */
+ newstring = LispMalloc(length + 1);
+ if (offset)
+ memcpy(newstring, string, offset);
+ if (length > end)
+ memcpy(newstring + end, string + end, length - end);
+ newstring[length] = '\0';
+ }
+ for (; offset < end; offset++)
+ newstring[offset] = tolower(string[offset]);
+
+ if (!inplace)
+ result = LSTRING2(newstring, length);
+
+ return (result);
+}
+
+LispObj *
+Lisp_StringDowncase(LispBuiltin *builtin)
+/*
+ string-downcase string &key start end
+ */
+{
+ return (LispStringDowncase(builtin, 0));
+}
+
+LispObj *
+Lisp_NstringDowncase(LispBuiltin *builtin)
+/*
+ nstring-downcase string &key start end
+ */
+{
+ return (LispStringDowncase(builtin, 1));
+}
+
+LispObj *
+LispStringCapitalize(LispBuiltin *builtin, int inplace)
+/*
+ string-capitalize string &key start end
+ nstring-capitalize string &key start end
+ */
+{
+ LispObj *result;
+ char *string, *newstring;
+ long start, end, length, offset, upcase;
+
+ LispObj *ostring, *ostart, *oend;
+
+ oend = ARGUMENT(2);
+ ostart = ARGUMENT(1);
+ ostring = ARGUMENT(0);
+ CHECK_STRING(ostring);
+ LispCheckSequenceStartEnd(builtin, ostring, ostart, oend,
+ &start, &end, &offset);
+ result = ostring;
+ string = THESTR(ostring);
+ length = STRLEN(ostring);
+
+ /* first check if something need to be done */
+ for (upcase = 1, offset = start; offset < end; offset++) {
+ if (upcase) {
+ if (!isalnum(string[offset]))
+ continue;
+ if (string[offset] != toupper(string[offset]))
+ break;
+ upcase = 0;
+ }
+ else {
+ if (isalnum(string[offset])) {
+ if (string[offset] != tolower(string[offset]))
+ break;
+ }
+ else
+ upcase = 1;
+ }
+ }
+
+ if (offset >= end)
+ return (result);
+
+ if (inplace) {
+ CHECK_STRING_WRITABLE(ostring);
+ newstring = string;
+ }
+ else {
+ /* capitalize a copy of argument */
+ newstring = LispMalloc(length + 1);
+ memcpy(newstring, string, length);
+ newstring[length] = '\0';
+ }
+ for (; offset < end; offset++) {
+ if (upcase) {
+ if (!isalnum(string[offset]))
+ continue;
+ newstring[offset] = toupper(string[offset]);
+ upcase = 0;
+ }
+ else {
+ if (isalnum(newstring[offset]))
+ newstring[offset] = tolower(string[offset]);
+ else
+ upcase = 1;
+ }
+ }
+
+ if (!inplace)
+ result = LSTRING2(newstring, length);
+
+ return (result);
+}
+
+LispObj *
+Lisp_StringCapitalize(LispBuiltin *builtin)
+/*
+ string-capitalize string &key start end
+ */
+{
+ return (LispStringCapitalize(builtin, 0));
+}
+
+LispObj *
+Lisp_NstringCapitalize(LispBuiltin *builtin)
+/*
+ nstring-capitalize string &key start end
+ */
+{
+ return (LispStringCapitalize(builtin, 1));
+}
+
+LispObj *
+Lisp_StringConcat(LispBuiltin *builtin)
+/*
+ string-concat &rest strings
+ */
+{
+ char *buffer;
+ long size, length;
+ LispObj *object, *string;
+
+ LispObj *strings;
+
+ strings = ARGUMENT(0);
+
+ if (strings == NIL)
+ return (STRING(""));
+
+ for (length = 1, object = strings; CONSP(object); object = CDR(object)) {
+ string = CAR(object);
+ CHECK_STRING(string);
+ length += STRLEN(string);
+ }
+
+ buffer = LispMalloc(length);
+
+ for (length = 0, object = strings; CONSP(object); object = CDR(object)) {
+ string = CAR(object);
+ size = STRLEN(string);
+ memcpy(buffer + length, THESTR(string), size);
+ length += size;
+ }
+ buffer[length] = '\0';
+ object = LSTRING2(buffer, length);
+
+ return (object);
+}
diff --git a/lisp/string.h b/lisp/string.h
new file mode 100644
index 0000000..02b416b
--- /dev/null
+++ b/lisp/string.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/string.h,v 1.11 2002/11/25 02:35:30 paulo Exp $ */
+
+#ifndef Lisp_string_h
+#define Lisp_string_h
+
+#include "internal.h"
+
+LispObj *Lisp_AlphaCharP(LispBuiltin*);
+LispObj *Lisp_BothCaseP(LispBuiltin*);
+LispObj *Lisp_Char(LispBuiltin*);
+LispObj *Lisp_CharLess(LispBuiltin*);
+LispObj *Lisp_CharLessEqual(LispBuiltin*);
+LispObj *Lisp_CharEqual_(LispBuiltin*);
+LispObj *Lisp_CharGreater(LispBuiltin*);
+LispObj *Lisp_CharGreaterEqual(LispBuiltin*);
+LispObj *Lisp_CharNotEqual_(LispBuiltin*);
+LispObj *Lisp_CharLessp(LispBuiltin*);
+LispObj *Lisp_CharNotGreaterp(LispBuiltin*);
+LispObj *Lisp_CharEqual(LispBuiltin*);
+LispObj *Lisp_CharGreaterp(LispBuiltin*);
+LispObj *Lisp_CharNotLessp(LispBuiltin*);
+LispObj *Lisp_CharNotEqual(LispBuiltin*);
+LispObj *Lisp_Character(LispBuiltin*);
+LispObj *Lisp_Characterp(LispBuiltin*);
+LispObj *Lisp_CharDowncase(LispBuiltin*);
+LispObj *Lisp_CharInt(LispBuiltin*);
+LispObj *Lisp_CharUpcase(LispBuiltin*);
+LispObj *Lisp_DigitChar(LispBuiltin*);
+LispObj *Lisp_DigitCharP(LispBuiltin*);
+LispObj *Lisp_IntChar(LispBuiltin*);
+LispObj *Lisp_GraphicCharP(LispBuiltin*);
+LispObj *Lisp_LowerCaseP(LispBuiltin*);
+LispObj *Lisp_MakeString(LispBuiltin*);
+LispObj *Lisp_ParseInteger(LispBuiltin*);
+LispObj *Lisp_ReadFromString(LispBuiltin*);
+LispObj *Lisp_String(LispBuiltin*);
+LispObj *Lisp_Stringp(LispBuiltin*);
+LispObj *Lisp_StringTrim(LispBuiltin*);
+LispObj *Lisp_StringLeftTrim(LispBuiltin*);
+LispObj *Lisp_StringRightTrim(LispBuiltin*);
+LispObj *Lisp_NstringTrim(LispBuiltin*);
+LispObj *Lisp_NstringLeftTrim(LispBuiltin*);
+LispObj *Lisp_NstringRightTrim(LispBuiltin*);
+LispObj *Lisp_StringEqual_(LispBuiltin*);
+LispObj *Lisp_StringLess(LispBuiltin*);
+LispObj *Lisp_StringGreater(LispBuiltin*);
+LispObj *Lisp_StringLessEqual(LispBuiltin*);
+LispObj *Lisp_StringGreaterEqual(LispBuiltin*);
+LispObj *Lisp_StringNotEqual_(LispBuiltin*);
+LispObj *Lisp_StringEqual(LispBuiltin*);
+LispObj *Lisp_StringGreaterp(LispBuiltin*);
+LispObj *Lisp_StringLessp(LispBuiltin*);
+LispObj *Lisp_StringNotLessp(LispBuiltin*);
+LispObj *Lisp_StringNotGreaterp(LispBuiltin*);
+LispObj *Lisp_StringNotEqual(LispBuiltin*);
+LispObj *Lisp_NstringUpcase(LispBuiltin*);
+LispObj *Lisp_StringUpcase(LispBuiltin*);
+LispObj *Lisp_StringDowncase(LispBuiltin*);
+LispObj *Lisp_NstringDowncase(LispBuiltin*);
+LispObj *Lisp_StringCapitalize(LispBuiltin*);
+LispObj *Lisp_NstringCapitalize(LispBuiltin*);
+LispObj *Lisp_StringConcat(LispBuiltin*);
+LispObj *Lisp_UpperCaseP(LispBuiltin*);
+LispObj *Lisp_XeditCharStore(LispBuiltin*);
+
+#endif /* Lisp_string_h */
diff --git a/lisp/struct.c b/lisp/struct.c
new file mode 100644
index 0000000..0d2a768
--- /dev/null
+++ b/lisp/struct.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/struct.c,v 1.22 2002/11/23 08:26:50 paulo Exp $ */
+
+#include "struct.h"
+
+/*
+ * Prototypes
+ */
+static LispObj *LispStructAccessOrStore(LispBuiltin*, int);
+
+/*
+ * Initialization
+ */
+LispObj *Omake_struct, *Ostruct_access, *Ostruct_store, *Ostruct_type;
+
+Atom_id Smake_struct, Sstruct_access, Sstruct_store, Sstruct_type;
+
+/*
+ * Implementation
+ */
+LispObj *
+Lisp_Defstruct(LispBuiltin *builtin)
+/*
+ defstruct name &rest description
+ */
+{
+ int intern;
+ LispAtom *atom;
+ int i, size, length, slength;
+ char *name, *strname, *sname;
+ LispObj *list, *cons, *object, *definition, *documentation;
+
+ LispObj *oname, *description;
+
+ description = ARGUMENT(1);
+ oname = ARGUMENT(0);
+
+ CHECK_SYMBOL(oname);
+
+ strname = ATOMID(oname);
+ length = strlen(strname);
+
+ /* MAKE- */
+ size = length + 6;
+ name = LispMalloc(size);
+
+ sprintf(name, "MAKE-%s", strname);
+ atom = (object = ATOM(name))->data.atom;
+
+ if (atom->a_builtin)
+ LispDestroy("%s: %s cannot be a structure name",
+ STRFUN(builtin), STROBJ(oname));
+
+ intern = !atom->ext;
+
+ if (CONSP(description) && STRINGP(CAR(description))) {
+ documentation = CAR(description);
+ description = CDR(description);
+ }
+ else
+ documentation = NIL;
+
+ /* get structure fields and default values */
+ for (list = description; CONSP(list); list = CDR(list)) {
+ object = CAR(list);
+
+ cons = list;
+ if (CONSP(object)) {
+ if ((CONSP(CDR(object)) && CDR(CDR(object)) != NIL) ||
+ (!CONSP(CDR(object)) && CDR(object) != NIL))
+ LispDestroy("%s: bad initialization %s",
+ STRFUN(builtin), STROBJ(object));
+ cons = object;
+ object = CAR(object);
+ }
+ if (!SYMBOLP(object) || strcmp(ATOMID(object), "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));
+
+ /* check for repeated field names */
+ for (object = description; object != list; object = CDR(object)) {
+ LispObj *left = CAR(object), *right = CAR(list);
+
+ if (CONSP(left))
+ left = CAR(left);
+ if (CONSP(right))
+ right = CAR(right);
+
+ if (ATOMID(left) == ATOMID(right))
+ LispDestroy("%s: only one slot named %s allowed",
+ STRFUN(builtin), STROBJ(left));
+ }
+ }
+
+ /* atom should not have been modified */
+ definition = CONS(oname, description);
+ LispSetAtomStructProperty(atom, definition, STRUCT_CONSTRUCTOR);
+ if (!intern)
+ LispExportSymbol(object);
+
+ atom = oname->data.atom;
+ if (atom->a_defstruct)
+ LispWarning("%s: structure %s is being redefined",
+ STRFUN(builtin), strname);
+ LispSetAtomStructProperty(atom, definition, STRUCT_NAME);
+
+ sprintf(name, "%s-P", strname);
+ atom = (object = ATOM(name))->data.atom;
+ LispSetAtomStructProperty(atom, definition, STRUCT_CHECK);
+ if (!intern)
+ LispExportSymbol(object);
+
+ for (i = 0, list = description; CONSP(list); i++, list = CDR(list)) {
+ if (CONSP(CAR(list)))
+ sname = ATOMID(CAR(CAR(list)));
+ else
+ sname = ATOMID(CAR(list));
+ slength = strlen(sname);
+ if (length + slength + 2 > size) {
+ size = length + slength + 2;
+ name = LispRealloc(name, size);
+ }
+ sprintf(name, "%s-%s", strname, sname);
+ atom = (object = ATOM(name))->data.atom;
+ LispSetAtomStructProperty(atom, definition, i);
+ if (!intern)
+ LispExportSymbol(object);
+ }
+
+ LispFree(name);
+
+ if (documentation != NIL)
+ LispAddDocumentation(oname, documentation, LispDocStructure);
+
+ return (oname);
+}
+
+/* helper functions
+ * DONT explicitly call them. Non standard functions.
+ */
+LispObj *
+Lisp_XeditMakeStruct(LispBuiltin *builtin)
+/*
+ lisp::make-struct atom &rest init
+ */
+{
+ int nfld, ncvt, length = lisp__data.protect.length;
+ LispAtom *atom = NULL;
+
+ LispObj *definition, *object, *field, *fields, *value = NIL, *cons, *list;
+ LispObj *struc, *init;
+
+ init = ARGUMENT(1);
+ struc = ARGUMENT(0);
+
+ field = cons = NIL;
+ if (!POINTERP(struc) ||
+ !(XSYMBOLP(struc) || XFUNCTIONP(struc)) ||
+ (atom = struc->data.atom)->a_defstruct == 0 ||
+ atom->property->structure.function != STRUCT_CONSTRUCTOR)
+ LispDestroy("%s: invalid constructor %s",
+ STRFUN(builtin), STROBJ(struc));
+ definition = atom->property->structure.definition;
+
+ ncvt = nfld = 0;
+ fields = NIL;
+
+ /* check for errors in argument list */
+ for (list = init, nfld = 0; CONSP(list); list = CDR(list)) {
+ CHECK_KEYWORD(CAR(list));
+ if (!CONSP(CDR(list)))
+ LispDestroy("%s: values must be provided as pairs",
+ ATOMID(struc));
+ nfld++;
+ list = CDR(list);
+ }
+
+ /* create structure, CAR(definition) is structure name */
+ for (list = CDR(definition); CONSP(list); list = CDR(list)) {
+ Atom_id id;
+ LispObj *defvalue = NIL;
+
+ ++nfld;
+ field = CAR(list);
+ if (CONSP(field)) {
+ /* if default value provided */
+ if (CONSP(CDR(field)))
+ defvalue = CAR(CDR(field));
+ field = CAR(field);
+ }
+ id = ATOMID(field);
+
+ for (object = init; CONSP(object); object = CDR(object)) {
+ /* field is a keyword, test above checked it */
+ field = CAR(object);
+ if (id == ATOMID(field)) {
+ /* value provided */
+ value = CAR(CDR(object));
+ ncvt++;
+ break;
+ }
+ object = CDR(object);
+ }
+
+ /* if no initialization given */
+ if (!CONSP(object)) {
+ /* if default value in structure definition */
+ if (defvalue != NIL)
+ value = EVAL(defvalue);
+ else
+ value = NIL;
+ }
+
+ if (fields == NIL) {
+ fields = cons = CONS(value, NIL);
+ if (length + 1 >= lisp__data.protect.space)
+ LispMoreProtects();
+ lisp__data.protect.objects[lisp__data.protect.length++] = fields;
+ }
+ else {
+ RPLACD(cons, CONS(value, NIL));
+ cons = CDR(cons);
+ }
+ }
+
+ /* if not enough arguments were converted, need to check because
+ * it is acceptable to set a field more than once, but in that case,
+ * only the first value will be used. */
+ if (nfld > ncvt) {
+ for (list = init; CONSP(list); list = CDR(list)) {
+ Atom_id id = ATOMID(CAR(list));
+
+ for (object = CDR(definition); CONSP(object);
+ object = CDR(object)) {
+ field = CAR(object);
+ if (CONSP(field))
+ field = CAR(field);
+ if (ATOMID(field) == id)
+ break;
+ }
+ if (!CONSP(object))
+ LispDestroy("%s: %s is not a field for %s",
+ ATOMID(struc), STROBJ(CAR(list)),
+ ATOMID(CAR(definition)));
+ list = CDR(list);
+ }
+ }
+
+ lisp__data.protect.length = length;
+
+ return (STRUCT(fields, definition));
+}
+
+static LispObj *
+LispStructAccessOrStore(LispBuiltin *builtin, int store)
+/*
+ lisp::struct-access atom struct
+ lisp::struct-store atom struct value
+ */
+{
+ long offset;
+ LispAtom *atom;
+ LispObj *definition, *list;
+
+ LispObj *name, *struc, *value = NIL;
+
+ if (store)
+ value = ARGUMENT(2);
+ struc = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ if (!POINTERP(name) ||
+ !(XSYMBOLP(name) || XFUNCTIONP(name)) ||
+ (atom = name->data.atom)->a_defstruct == 0 ||
+ (offset = atom->property->structure.function) < 0) {
+ LispDestroy("%s: invalid argument %s",
+ STRFUN(builtin), STROBJ(name));
+ /*NOTREACHED*/
+ offset = 0;
+ atom = NULL;
+ }
+ definition = atom->property->structure.definition;
+
+ /* check if the object is of the required type */
+ if (!STRUCTP(struc) || struc->data.struc.def != definition)
+ LispDestroy("%s: %s is not a %s",
+ ATOMID(name), STROBJ(struc), ATOMID(CAR(definition)));
+
+ for (list = struc->data.struc.fields; offset; list = CDR(list), offset--)
+ ;
+
+ return (store ? RPLACA(list, value) : CAR(list));
+}
+
+LispObj *
+Lisp_XeditStructAccess(LispBuiltin *builtin)
+/*
+ lisp::struct-access atom struct
+ */
+{
+ return (LispStructAccessOrStore(builtin, 0));
+}
+
+LispObj *
+Lisp_XeditStructStore(LispBuiltin *builtin)
+/*
+ lisp::struct-store atom struct value
+ */
+{
+ return (LispStructAccessOrStore(builtin, 1));
+}
+
+LispObj *
+Lisp_XeditStructType(LispBuiltin *builtin)
+/*
+ lisp::struct-type atom struct
+ */
+{
+ LispAtom *atom = NULL;
+
+ LispObj *definition, *struc, *name;
+
+ struc = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ if (!POINTERP(name) ||
+ !(XSYMBOLP(name) || XFUNCTIONP(name)) ||
+ (atom = name->data.atom)->a_defstruct == 0 ||
+ (atom->property->structure.function != STRUCT_CHECK))
+ LispDestroy("%s: invalid argument %s",
+ STRFUN(builtin), STROBJ(name));
+ definition = atom->property->structure.definition;
+
+ /* check if the object is of the required type */
+ if (STRUCTP(struc) && struc->data.struc.def == definition)
+ return (T);
+
+ return (NIL);
+}
diff --git a/lisp/struct.h b/lisp/struct.h
new file mode 100644
index 0000000..7559a02
--- /dev/null
+++ b/lisp/struct.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/struct.h,v 1.6 2002/11/10 16:29:07 paulo Exp $ */
+
+#ifndef Lisp_struct_h
+#define Lisp_struct_h
+
+#include "private.h"
+
+/*
+ * Defines
+ */
+#define XSTRUCTP(object) ((object)->type == LispStruct_t)
+#define STRUCTP(object) (POINTERP(object) && XSTRUCTP(object))
+#define STRUCT(fields, def) LispNewStruct(fields, def)
+
+/*
+ * Prototypes
+ */
+LispObj *Lisp_Defstruct(LispBuiltin*);
+LispObj *Lisp_XeditMakeStruct(LispBuiltin*);
+LispObj *Lisp_XeditStructAccess(LispBuiltin*);
+LispObj *Lisp_XeditStructStore(LispBuiltin*);
+LispObj *Lisp_XeditStructType(LispBuiltin*);
+
+#endif /* Lisp_struct_h */
diff --git a/lisp/test/hello.lsp b/lisp/test/hello.lsp
new file mode 100644
index 0000000..5446919
--- /dev/null
+++ b/lisp/test/hello.lsp
@@ -0,0 +1,72 @@
+;;
+;; Copyright (c) 2001 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/test/hello.lsp,v 1.3 2002/11/08 08:01:01 paulo Exp $
+;;
+(require "xaw")
+(require "xt")
+
+(defun quit-callback (widget user call) (quit))
+
+(defun fix-shell-size (shell)
+ (let ((size (xt-get-values shell '("width" "height"))))
+ (xt-set-values shell
+ (list (cons "minWidth" (cdar size))
+ (cons "maxWidth" (cdar size))
+ (cons "minHeight" (cdadr size))
+ (cons "maxHeight" (cdadr size)))
+ )
+ )
+)
+
+(setq toplevel
+ (xt-app-initialize 'appcontext "Hello"
+ '(("title" . "Hello World!"))))
+
+(setq form
+ (xt-create-managed-widget "form" form-widget-class toplevel
+ '(("background" . "gray85")
+ ("displayList" . "foreground rgb:7/9/7;lines 1,-1,-1,-1,-1,1;foreground gray90;lines -1,0,0,0,0,-1")
+ )))
+
+(setq button
+ (xt-create-managed-widget "button" command-widget-class form
+ '(("label" . "Goodbye world!")
+ ("tip" . "This sample uses some customizations")
+ ("foreground" . "gray10")
+ ("background" . "gray80")
+ ("displayList" . "foreground rgb:7/9/7;lines 1,-1,-1,-1,-1,1;foreground gray90;lines -1,0,0,0,0,-1")
+ )))
+(xt-add-callback button "callback" 'quit-callback)
+
+(xt-realize-widget toplevel)
+
+(fix-shell-size toplevel)
+
+(xt-app-main-loop appcontext)
diff --git a/lisp/test/list.lsp b/lisp/test/list.lsp
new file mode 100644
index 0000000..23f4496
--- /dev/null
+++ b/lisp/test/list.lsp
@@ -0,0 +1,1895 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/test/list.lsp,v 1.6 2002/12/06 03:25:29 paulo Exp $
+;;
+
+;; basic lisp function tests
+
+;; Most of the tests are just the examples from the
+;;
+;; Common Lisp HyperSpec (TM)
+;; Copyright 1996-2001, Xanalys Inc. All rights reserved.
+;;
+;; Some tests are hand crafted, to test how the interpreter treats
+;; uncommon arguments or special conditions
+
+
+#|
+ MAJOR PROBLEMS:
+
+ o NIL and T should be always treated as symbols, actually it is
+ legal to say (defun nil (...) ...)
+ o There aren't true uninterned symbols, there are only symbols that
+ did not yet establish the home package, but once one is created, an
+ interned symbol is always returned.
+|#
+
+(defun compare-test (test expect function arguments
+ &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (apply function arguments))
+ (setq error nil)
+ )
+ )
+ (if error
+ (format t "ERROR: (~S~{ ~S~}) => ~S~%" function arguments error-value)
+ (or (funcall test result expect)
+ (format t "(~S~{ ~S~}) => should be ~S not ~S~%"
+ function arguments expect result
+ )
+ )
+ )
+)
+
+(defun compare-eval (test expect form
+ &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (eval form))
+ (setq error nil)
+ )
+ )
+ (if error
+ (format t "ERROR: ~S => ~S~%" form error-value)
+ (or (funcall test result expect)
+ (format t "~S => should be ~S not ~S~%"
+ form expect result
+ )
+ )
+ )
+)
+
+(defun error-test (function &rest arguments &aux result (error t))
+ (ignore-errors
+ (setq result (apply function arguments))
+ (setq error nil)
+ )
+ (or error
+ (format t "ERROR: no error for (~S~{ ~S~}), result was ~S~%"
+ function arguments result)
+ )
+)
+
+(defun error-eval (form &aux result (error t))
+ (ignore-errors
+ (setq result (eval form))
+ (setq error nil)
+ )
+ (or error
+ (format t "ERROR: no error for ~S, result was ~S~%" form result)
+ )
+)
+
+(defun eq-test (expect function &rest arguments)
+ (compare-test #'eq expect function arguments))
+
+(defun eql-test (expect function &rest arguments)
+ (compare-test #'eql expect function arguments))
+
+(defun equal-test (expect function &rest arguments)
+ (compare-test #'equal expect function arguments))
+
+(defun equalp-test (expect function &rest arguments)
+ (compare-test #'equalp expect function arguments))
+
+
+(defun eq-eval (expect form)
+ (compare-eval #'eq expect form))
+
+(defun eql-eval (expect form)
+ (compare-eval #'eql expect form))
+
+(defun equal-eval (expect form)
+ (compare-eval #'equal expect form))
+
+(defun equalp-eval (expect form)
+ (compare-eval #'equalp expect form))
+
+;; clisp treats strings loaded from a file as constants
+(defun xseq (sequence)
+ #+clisp (if *load-pathname* (copy-seq sequence) sequence)
+ #-clisp sequence
+)
+
+;; apply - function
+(equal-test '((+ 2 3) . 4) #'apply 'cons '((+ 2 3) 4))
+(eql-test -1 #'apply #'- '(1 2))
+(eql-test 7 #'apply #'max 3 5 '(2 7 3))
+(error-test #'apply #'+ 1)
+(error-test #'apply #'+ 1 2)
+(error-test #'apply #'+ 1 . 2)
+(error-test #'apply #'+ 1 2 3)
+(error-test #'apply #'+ 1 2 . 3)
+(eql-test 6 #'apply #'+ 1 2 3 ())
+
+;; eq - function
+(eq-eval t '(let* ((a #\a) (b a)) (eq a b)))
+(eq-test t #'eq 'a 'a)
+(eq-test nil #'eq 'a 'b)
+(eq-eval t '(eq #1=1 #1#))
+(eq-test nil #'eq "abc" "abc")
+(setq a '('x #c(1 2) #\z))
+(eq-test nil #'eq a (copy-seq a))
+
+;; eql - function
+(eq-test t #'eql 1 1)
+(eq-test t #'eql 1.3d0 1.3d0)
+(eq-test nil #'eql 1 1d0)
+(eq-test t #'eql #c(1 -5) #c(1 -5))
+(eq-test t #'eql 'a 'a)
+(eq-test nil #'eql :a 'a)
+(eq-test t #'eql #c(5d0 0) 5d0)
+(eq-test nil #'eql #c(5d0 0d0) 5d0)
+(eq-test nil #'eql "abc" "abc")
+(equal-eval '(1 5/6 #p"test" #\#) '(setq a '(1 5/6 #p"test" #\#)))
+(eq-test nil #'eql a (copy-seq a))
+
+(setf
+ hash0 (make-hash-table)
+ hash1 (make-hash-table)
+ (gethash 1 hash0) 2
+ (gethash 1 hash1) 2
+ (gethash :foo hash0) :bar
+ (gethash :foo hash1) :bar
+)
+(defstruct test a b c)
+(setq
+ struc0 (make-test :a 1 :b 2 :c #\c)
+ struc1 (make-test :a 1 :b 2 :c #\c)
+)
+
+;; equal - function
+(eq-test t #'equal "abc" "abc")
+(eq-test t #'equal 1 1)
+(eq-test t #'equal #c(1 2) #c(1 2))
+(eq-test nil #'equal #c(1 2) #c(1 2d0))
+(eq-test t #'equal #\A #\A)
+(eq-test nil #'equal #\A #\a)
+(eq-test nil #'equal "abc" "Abc")
+(equal-eval '(1 2 3/5 #\a) '(setq a '(1 2 3/5 #\a)))
+(eq-test t #'equal a (copy-seq a))
+(eq-test nil #'equal hash0 hash1)
+(eq-test nil #'equal struc0 struc1)
+(eq-test nil #'equal #(1 2 3 4) #(1 2 3 4))
+
+;; equalp - function
+(eq-test t #'equalp hash0 hash1)
+(setf
+ (gethash 2 hash0) "FoObAr"
+ (gethash 2 hash1) "fOoBaR"
+)
+(eq-test t #'equalp hash0 hash1)
+(setf
+ (gethash 3 hash0) 3
+ (gethash 3d0 hash1) 3
+)
+(eq-test nil #'equalp hash0 hash1)
+(eq-test t #'equalp struc0 struc1)
+(setf
+ (test-a struc0) #\a
+ (test-a struc1) #\A
+)
+(eq-test t #'equalp struc0 struc1)
+(setf
+ (test-b struc0) 'test
+ (test-b struc1) :test
+)
+(eq-test nil #'equalp struc0 struc1)
+(eq-test t #'equalp #c(1/2 1d0) #c(0.5d0 1))
+(eq-test t #'equalp 1 1d0)
+(eq-test t #'equalp #(1 2 3 4) #(1 2 3 4))
+(eq-test t #'equalp #(1 #\a 3 4d0) #(1 #\A 3 4))
+
+;; acons - function
+(equal-test '((1 . "one")) #'acons 1 "one" nil)
+(equal-test '((2 . "two") (1 . "one")) #'acons 2 "two" '((1 . "one")))
+
+;; adjoin - function
+(equal-test '(nil) #'adjoin nil nil)
+(equal-test '(a) #'adjoin 'a nil)
+(equal-test '(1 2 3) #'adjoin 1 '(1 2 3))
+(equal-test '(1 2 3) #'adjoin 2 '(1 2 3))
+(equal-test '((1) (1) (2) (3)) #'adjoin '(1) '((1) (2) (3)))
+(equal-test '((1) (2) (3)) #'adjoin '(1) '((1) (2) (3)) :key #'car)
+(error-test #'adjoin nil 1)
+
+;; alpha-char-p - function
+(eq-test t #'alpha-char-p #\a)
+(eq-test nil #'alpha-char-p #\5)
+(error-test #'alpha-char-p 'a)
+
+;; alphanumericp - function
+(eq-test t #'alphanumericp #\Z)
+(eq-test t #'alphanumericp #\8)
+(eq-test nil #'alphanumericp #\#)
+
+;; and - macro
+(eql-eval 1 '(setq temp1 1 temp2 1 temp3 1))
+(eql-eval 2 '(and (incf temp1) (incf temp2) (incf temp3)))
+(eq-eval t '(and (eql 2 temp1) (eql 2 temp2) (eql 2 temp3)))
+(eql-eval 1 '(decf temp3))
+(eq-eval nil '(and (decf temp1) (decf temp2) (eq temp3 'nil) (decf temp3)))
+(eq-eval t '(and (eql temp1 temp2) (eql temp2 temp3)))
+(eq-eval t '(and))
+(equal-eval '(1 2 3) '(multiple-value-list (and (values 'a) (values 1 2 3))))
+(equal-eval nil '(and (values) t))
+
+;; append - function
+(equal-test '(a b c d e f g) #'append '(a b c) '(d e f) '() '(g))
+(equal-test '(a b c . d) #'append '(a b c) 'd)
+(eq-test nil #'append)
+(eql-test 'a #'append nil 'a)
+(error-test #'append 1 2)
+
+;; assoc - function
+(equal-test '(1 . "one") #'assoc 1 '((2 . "two") (1 . "one")))
+(equal-test '(2 . "two") #'assoc 2 '((1 . "one") (2 . "two")))
+(eq-test nil #'assoc 1 nil)
+(equal-test '(2 . "two") #'assoc-if #'evenp '((1 . "one") (2 . "two")))
+(equal-test '(3 . "three") #'assoc-if-not #'(lambda(x) (< x 3))
+ '((1 . "one") (2 . "two") (3 . "three")))
+(equal-test '("two" . 2) #'assoc #\o '(("one" . 1) ("two" . 2) ("three" . 3))
+ :key #'(lambda (x) (char x 2)))
+(equal-test '(a . b) #'assoc 'a '((x . a) (y . b) (a . b) (a . c)))
+
+;; atom - function
+(eq-test t #'atom 1)
+(eq-test t #'atom '())
+(eq-test nil #'atom '(1))
+(eq-test t #'atom 'a)
+
+;; block - special operator
+(eq-eval nil '(block empty))
+(eql-eval 2 '(let ((x 1))
+ (block stop (setq x 2) (return-from stop) (setq x 3)) x))
+(eql-eval 2 '(block twin (block twin (return-from twin 1)) 2))
+
+;; both-case-p - function
+(eq-test t #'both-case-p #\a)
+(eq-test nil #'both-case-p #\1)
+
+;; boundp - function
+(eql-eval 1 '(setq x 1))
+(eq-test t #'boundp 'x)
+(makunbound 'x)
+(eq-test nil #'boundp 'x)
+(eq-eval nil '(let ((x 1)) (boundp 'x)))
+(error-test #'boundp 1)
+
+;; butlast, nbutlast - function
+(setq x '(1 2 3 4 5 6 7 8 9))
+(equal-test '(1 2 3 4 5 6 7 8) #'butlast x)
+(equal-eval '(1 2 3 4 5 6 7 8 9) 'x)
+(eq-eval nil '(nbutlast x 9))
+(equal-test '(1) #'nbutlast x 8)
+(equal-eval '(1) 'x)
+(eq-test nil #'butlast nil)
+(eq-test nil #'nbutlast '())
+(error-test #'butlast 1 2)
+(error-test #'butlast -1 '(1 2))
+
+;; car, cdr, caar ... - function
+(eql-test 1 #'car '(1 2))
+(eql-test 2 #'cdr '(1 . 2))
+(eql-test 1 #'caar '((1 2)))
+(eql-test 2 #'cadr '(1 2))
+(eql-test 2 #'cdar '((1 . 2)))
+(eql-test 3 #'cddr '(1 2 . 3))
+(eql-test 1 #'caaar '(((1 2))))
+(eql-test 2 #'caadr '(1 (2 3)))
+(eql-test 2 #'cadar '((1 2) 2 3))
+(eql-test 3 #'caddr '(1 2 3 4))
+(eql-test 2 #'cdaar '(((1 . 2)) 3))
+(eql-test 3 #'cdadr '(1 (2 . 3) 4))
+(eql-test 3 #'cddar '((1 2 . 3) 3))
+(eql-test 4 #'cdddr '(1 2 3 . 4))
+(eql-test 1 #'caaaar '((((1 2)))))
+(eql-test 2 #'caaadr '(1 ((2))))
+(eql-test 2 #'caadar '((1 (2)) 3))
+(eql-test 3 #'caaddr '(1 2 (3 4)))
+(eql-test 2 #'cadaar '(((1 2)) 3))
+(eql-test 3 #'cadadr '(1 (2 3) 4))
+(eql-test 3 #'caddar '((1 2 3) 4))
+(eql-test 4 #'cadddr '(1 2 3 4 5))
+(eql-test 2 #'cdaaar '((((1 . 2))) 3))
+(eql-test 3 #'cdaadr '(1 ((2 . 3)) 4))
+(eql-test 3 #'cdadar '((1 (2 . 3)) 4))
+(eql-test 4 #'cdaddr '(1 2 (3 . 4) 5))
+(eql-test 3 #'cddaar '(((1 2 . 3)) 4))
+(eql-test 4 #'cddadr '(1 (2 3 . 4) 5))
+(eql-test 4 #'cdddar '((1 2 3 . 4) 5))
+(eql-test 5 #'cddddr '(1 2 3 4 . 5))
+
+;; first ... tenth, rest - function
+(eql-test 2 #'rest '(1 . 2))
+(eql-test 1 #'first '(1 2))
+(eql-test 2 #'second '(1 2 3))
+(eql-test 2 #'second '(1 2 3))
+(eql-test 3 #'third '(1 2 3 4))
+(eql-test 4 #'fourth '(1 2 3 4 5))
+(eql-test 5 #'fifth '(1 2 3 4 5 6))
+(eql-test 6 #'sixth '(1 2 3 4 5 6 7))
+(eql-test 7 #'seventh '(1 2 3 4 5 6 7 8))
+(eql-test 8 #'eighth '(1 2 3 4 5 6 7 8 9))
+(eql-test 9 #'ninth '(1 2 3 4 5 6 7 8 9 10))
+(eql-test 10 #'tenth '(1 2 3 4 5 6 7 8 9 10 11))
+(error-test #'car 1)
+(error-test #'car #c(1 2))
+(error-test #'car #(1 2))
+
+;; case - macro
+(eql-eval t '(let ((a 1)) (case a ((4 5 6) nil) ((3 2 1) t) (otherwise :error))))
+(eql-eval t '(let ((a 1)) (case a ((3 2) nil) (1 t) (t :error))))
+(error-eval '(let ((a 1)) (case a (2 :error) (t nil) (otherwise t))))
+(error-eval '(let ((a 1)) (case a (2 :error) (otherwise t) (t nil))))
+
+;; catch - special operator
+(eql-eval 3 '(catch 'dummy-tag 1 2 (throw 'dummy-tag 3) 4))
+(eql-eval 4 '(catch 'dummy-tag 1 2 3 4))
+(eq-eval 'throw-back '(defun throw-back (tag) (throw tag t)))
+(eq-eval t '(catch 'dummy-tag (throw-back 'dummy-tag) 2))
+
+;; char - function
+(eql-test #\a #'char "abc" 0)
+(eql-test #\b #'char "abc" 1)
+(error-test #'char "abc" 3)
+
+;; char-* - function
+(eq-test nil #'alpha-char-p #\3)
+(eq-test t #'alpha-char-p #\y)
+(eql-test #\a #'char-downcase #\a)
+(eql-test #\a #'char-downcase #\a)
+(eql-test #\1 #'char-downcase #\1)
+(error-test #'char-downcase 1)
+(eql-test #\A #'char-upcase #\a)
+(eql-test #\A #'char-upcase #\A)
+(eql-test #\1 #'char-upcase #\1)
+(error-test #'char-upcase 1)
+(eq-test t #'lower-case-p #\a)
+(eq-test nil #'lower-case-p #\A)
+(eq-test t #'upper-case-p #\W)
+(eq-test nil #'upper-case-p #\w)
+(eq-test t #'both-case-p #\x)
+(eq-test nil #'both-case-p #\%)
+(eq-test t #'char= #\d #\d)
+(eq-test t #'char-equal #\d #\d)
+(eq-test nil #'char= #\A #\a)
+(eq-test t #'char-equal #\A #\a)
+(eq-test nil #'char= #\d #\x)
+(eq-test nil #'char-equal #\d #\x)
+(eq-test nil #'char= #\d #\D)
+(eq-test t #'char-equal #\d #\D)
+(eq-test nil #'char/= #\d #\d)
+(eq-test nil #'char-not-equal #\d #\d)
+(eq-test nil #'char/= #\d #\d)
+(eq-test nil #'char-not-equal #\d #\d)
+(eq-test t #'char/= #\d #\x)
+(eq-test t #'char-not-equal #\d #\x)
+(eq-test t #'char/= #\d #\D)
+(eq-test nil #'char-not-equal #\d #\D)
+(eq-test t #'char= #\d #\d #\d #\d)
+(eq-test t #'char-equal #\d #\d #\d #\d)
+(eq-test nil #'char= #\d #\D #\d #\d)
+(eq-test t #'char-equal #\d #\D #\d #\d)
+(eq-test nil #'char/= #\d #\d #\d #\d)
+(eq-test nil #'char-not-equal #\d #\d #\d #\d)
+(eq-test nil #'char/= #\d #\d #\D #\d)
+(eq-test nil #'char-not-equal #\d #\d #\D #\d)
+(eq-test nil #'char= #\d #\d #\x #\d)
+(eq-test nil #'char-equal #\d #\d #\x #\d)
+(eq-test nil #'char/= #\d #\d #\x #\d)
+(eq-test nil #'char-not-equal #\d #\d #\x #\d)
+(eq-test nil #'char= #\d #\y #\x #\c)
+(eq-test nil #'char-equal #\d #\y #\x #\c)
+(eq-test t #'char/= #\d #\y #\x #\c)
+(eq-test t #'char-not-equal #\d #\y #\x #\c)
+(eq-test nil #'char= #\d #\c #\d)
+(eq-test nil #'char-equal #\d #\c #\d)
+(eq-test nil #'char/= #\d #\c #\d)
+(eq-test nil #'char-not-equal #\d #\c #\d)
+(eq-test t #'char< #\d #\x)
+(eq-test t #'char-lessp #\d #\x)
+(eq-test t #'char-lessp #\d #\X)
+(eq-test t #'char-lessp #\D #\x)
+(eq-test t #'char-lessp #\D #\X)
+(eq-test t #'char<= #\d #\x)
+(eq-test t #'char-not-greaterp #\d #\x)
+(eq-test t #'char-not-greaterp #\d #\X)
+(eq-test t #'char-not-greaterp #\D #\x)
+(eq-test t #'char-not-greaterp #\D #\X)
+(eq-test nil #'char< #\d #\d)
+(eq-test nil #'char-lessp #\d #\d)
+(eq-test nil #'char-lessp #\d #\D)
+(eq-test nil #'char-lessp #\D #\d)
+(eq-test nil #'char-lessp #\D #\D)
+(eq-test t #'char<= #\d #\d)
+(eq-test t #'char-not-greaterp #\d #\d)
+(eq-test t #'char-not-greaterp #\d #\D)
+(eq-test t #'char-not-greaterp #\D #\d)
+(eq-test t #'char-not-greaterp #\D #\D)
+(eq-test t #'char< #\a #\e #\y #\z)
+(eq-test t #'char-lessp #\a #\e #\y #\z)
+(eq-test t #'char-lessp #\a #\e #\y #\Z)
+(eq-test t #'char-lessp #\a #\E #\y #\z)
+(eq-test t #'char-lessp #\A #\e #\y #\Z)
+(eq-test t #'char<= #\a #\e #\y #\z)
+(eq-test t #'char-not-greaterp #\a #\e #\y #\z)
+(eq-test t #'char-not-greaterp #\a #\e #\y #\Z)
+(eq-test t #'char-not-greaterp #\A #\e #\y #\z)
+(eq-test nil #'char< #\a #\e #\e #\y)
+(eq-test nil #'char-lessp #\a #\e #\e #\y)
+(eq-test nil #'char-lessp #\a #\e #\E #\y)
+(eq-test nil #'char-lessp #\A #\e #\E #\y)
+(eq-test t #'char<= #\a #\e #\e #\y)
+(eq-test t #'char-not-greaterp #\a #\e #\e #\y)
+(eq-test t #'char-not-greaterp #\a #\E #\e #\y)
+(eq-test t #'char> #\e #\d)
+(eq-test t #'char-greaterp #\e #\d)
+(eq-test t #'char-greaterp #\e #\D)
+(eq-test t #'char-greaterp #\E #\d)
+(eq-test t #'char-greaterp #\E #\D)
+(eq-test t #'char>= #\e #\d)
+(eq-test t #'char-not-lessp #\e #\d)
+(eq-test t #'char-not-lessp #\e #\D)
+(eq-test t #'char-not-lessp #\E #\d)
+(eq-test t #'char-not-lessp #\E #\D)
+(eq-test t #'char> #\d #\c #\b #\a)
+(eq-test t #'char-greaterp #\d #\c #\b #\a)
+(eq-test t #'char-greaterp #\d #\c #\b #\A)
+(eq-test t #'char-greaterp #\d #\c #\B #\a)
+(eq-test t #'char-greaterp #\d #\C #\b #\a)
+(eq-test t #'char-greaterp #\D #\C #\b #\a)
+(eq-test t #'char>= #\d #\c #\b #\a)
+(eq-test t #'char-not-lessp #\d #\c #\b #\a)
+(eq-test t #'char-not-lessp #\d #\c #\b #\A)
+(eq-test t #'char-not-lessp #\D #\c #\b #\a)
+(eq-test t #'char-not-lessp #\d #\C #\B #\a)
+(eq-test nil #'char> #\d #\d #\c #\a)
+(eq-test nil #'char-greaterp #\d #\d #\c #\a)
+(eq-test nil #'char-greaterp #\d #\d #\c #\A)
+(eq-test nil #'char-greaterp #\d #\D #\c #\a)
+(eq-test nil #'char-greaterp #\d #\D #\C #\a)
+(eq-test t #'char>= #\d #\d #\c #\a)
+(eq-test t #'char-not-lessp #\d #\d #\c #\a)
+(eq-test t #'char-not-lessp #\d #\D #\c #\a)
+(eq-test t #'char-not-lessp #\D #\d #\c #\a)
+(eq-test t #'char-not-lessp #\D #\D #\c #\A)
+(eq-test nil #'char> #\e #\d #\b #\c #\a)
+(eq-test nil #'char-greaterp #\e #\d #\b #\c #\a)
+(eq-test nil #'char-greaterp #\E #\d #\b #\c #\a)
+(eq-test nil #'char-greaterp #\e #\D #\b #\c #\a)
+(eq-test nil #'char-greaterp #\E #\d #\B #\c #\A)
+(eq-test nil #'char>= #\e #\d #\b #\c #\a)
+(eq-test nil #'char-not-lessp #\e #\d #\b #\c #\a)
+(eq-test nil #'char-not-lessp #\e #\d #\b #\c #\A)
+(eq-test nil #'char-not-lessp #\E #\d #\B #\c #\a)
+
+;; char-code - function
+;; XXX assumes ASCII
+(eql-test 49 #'char-code #\1)
+(eql-test 90 #'char-code #\Z)
+(eql-test 127 #'char-code #\Delete)
+(eql-test 27 #'char-code #\Escape)
+(eql-test 13 #'char-code #\Return)
+(eql-test 0 #'char-code #\Null)
+(eql-test 10 #'char-code #\Newline)
+(error-test #'char-code 65)
+
+;; character - function
+(eql-test #\a #'character #\a)
+(eql-test #\a #'character "a")
+(eql-test #\A #'character 'a)
+
+;; XXX assumes ASCII, and should be allowed to fail?
+(eql-test #\A #'character 65)
+
+(error-test #'character 1/2)
+(error-test #'character "abc")
+(error-test #'character :test)
+(eq-test #\T #'character t)
+(error-test #'character nil)
+
+;; characterp - function
+(eq-test t #'characterp #\a)
+(eq-test nil #'characterp 1)
+(eq-test nil #'characterp 1/2)
+(eq-test nil #'characterp 'a)
+(eq-test nil #'characterp '`a)
+
+
+
+
+;; TODO coerce
+
+
+
+
+;; cond - macro
+(eql-eval 2 '(let ((a 1)) (cond ((= a 2) 1) ((= a 1) 2) ((= a 0) 1) (t nil))))
+(eql-eval nil '(let ((a 1)) (cond ((= a 2) 1) (t nil) ((= a 1) 2) ((= a 0) 1))))
+
+;; consp - function (predicate)
+(eq-test t #'consp '(1 2))
+(eq-test t #'consp '(1 . 2))
+(eq-test nil #'consp nil)
+(eq-test nil #'consp 1)
+
+;; constantp - function (predicate)
+(eq-test t #'constantp 1)
+(eq-test t #'constantp #\x)
+(eq-test t #'constantp :test)
+(eq-test nil #'constantp 'test)
+(eq-test t #'constantp ''1)
+(eq-test t #'constantp '(quote 1))
+(eq-test t #'constantp "string")
+(eq-test t #'constantp #c(1 2))
+(eq-test t #'constantp #(1 2))
+(eq-test nil #'constantp #p"test")
+(eq-test nil #'constantp '(1 2))
+(eq-test nil #'constantp (make-hash-table))
+(eq-test nil #'constantp *package*)
+(eq-test nil #'constantp *standard-input*)
+
+;; copy-list, copy-alist and copy-tree - function
+(equal-test '(1 2) #'copy-list '(1 2))
+(equal-test '(1 . 2) #'copy-list '(1 . 2))
+(eq-test nil #'copy-list nil)
+(error-test #'copy-list 1)
+(equal-eval '(1 (2 3)) '(setq x '(1 (2 3))))
+(equal-eval x '(setq y (copy-list x)))
+(equal-test '("one" (2 3)) #'rplaca x "one")
+(eql-test 1 #'car y)
+(equal-test '("two" 3) #'rplaca (cadr x) "two")
+(eq-test (caadr x) #'caadr y)
+(equal-eval '(1 (2 3) 4) '(setq a '(1 (2 3) 4) b (copy-list a)))
+(eq-eval t '(eq (cadr a) (cadr b)))
+(eq-eval t '(eq (car a) (car b)))
+(setq a '(1 (2 3) 4) b (copy-alist a))
+(eq-eval nil '(eq (cadr a) (cadr b)))
+(eq-eval t '(eq (car a) (car b)))
+(eq-test nil #'copy-alist nil)
+(eq-test nil #'copy-list nil)
+(error-test #'copy-list 1)
+(setq a '(1 (2 (3))))
+(setq as-list (copy-list a))
+(setq as-alist (copy-alist a))
+(setq as-tree (copy-tree a))
+(eq-eval t '(eq (cadadr a) (cadadr as-list)))
+(eq-eval t '(eq (cadadr a) (cadadr as-alist)))
+(eq-eval nil '(eq (cadadr a) (cadadr as-tree)))
+
+;; decf - macro
+(setq n 2)
+(eql-eval 1 '(decf n))
+(eql-eval 1 'n)
+(setq n -2147483648)
+(eql-eval -2147483649 '(decf n))
+(eql-eval -2147483649 'n)
+(setq n 0)
+(eql-eval -0.5d0 '(decf n 0.5d0))
+(eql-eval -0.5d0 'n)
+(setq n 1)
+(eql-eval 1/2 '(decf n 1/2))
+(eql-eval 1/2 'n)
+
+;; delete and remove - function
+(setq a '(1 3 4 5 9) b a)
+(equal-test '(1 3 5 9) #'remove 4 a)
+(eq-eval t '(eq a b))
+(setq a (delete 4 a))
+(equal-eval '(1 3 5 9) 'a)
+(setq a '(1 2 4 1 3 4 5) b a)
+(equal-test '(1 2 1 3 5) #'remove 4 a)
+(eq-eval t '(eq a b))
+(equal-test '(1 2 1 3 4 5) #'remove 4 a :count 1)
+(eq-eval t '(eq a b))
+(equal-test '(1 2 4 1 3 5) #'remove 4 a :count 1 :from-end t)
+(eq-eval t '(eq a b))
+(equal-test '(4 3 4 5) #'remove 3 a :test #'>)
+(eq-eval t '(eq a b))
+(setq a (delete 4 '(1 2 4 1 3 4 5)))
+(equal-eval '(1 2 1 3 5) 'a)
+(setq a (delete 4 '(1 2 4 1 3 4 5) :count 1))
+(equal-eval '(1 2 1 3 4 5) 'a)
+(setq a (delete 4 '(1 2 4 1 3 4 5) :count 1 :from-end t))
+(equal-eval '(1 2 4 1 3 5) 'a)
+(equal-test "abc" #'delete-if #'digit-char-p "a1b2c3")
+(equal-test "123" #'delete-if-not #'digit-char-p "a1b2c3")
+(eq-test nil #'delete 1 nil)
+(eq-test nil #'remove 1 nil)
+(setq a '(1 2 3 4 :test 5 6 7 8) b a)
+(equal-test '(1 2 :test 7 8) #'remove-if #'numberp a :start 2 :end 7)
+(eq-eval t '(eq a b))
+(setq a (delete-if #'numberp a :start 2 :end 7))
+(equal-eval '(1 2 :test 7 8) 'a)
+
+;; digit-char - function
+(eql-test #\0 #'digit-char 0)
+(eql-test #\A #'digit-char 10 11)
+(eq-test nil #'digit-char 10 10)
+(eql-test 35 #'digit-char-p #\z 36)
+(error-test #'digit-char #\a)
+(error-test #'digit-char-p 1/2)
+
+
+
+;; TODO directory (known to have problems with parameters like "../*/../*/")
+
+
+
+;; elt - function
+(eql-test #\a #'elt "xabc" 1)
+(eql-test 3 #'elt '(0 1 2 3) 3)
+(error-test #'elt nil 0)
+
+;; endp - function
+(eql-test t #'endp nil)
+(error-test #'endp t)
+(eql-test nil #'endp '(1 . 2))
+(error-test #'endp #(1 2))
+
+;; every - function
+(eql-test t #'every 'not-used ())
+(eql-test t #'every #'characterp "abc")
+(eql-test nil #'every #'< '(1 2 3) '(4 5 6) #(7 8 -1))
+(eql-test t #'every #'< '(1 2 3) '(4 5 6) #(7 8))
+
+;; fboundp and fmakunbound - function
+(eq-test t #'fboundp 'car)
+(eq-eval 'test '(defun test ()))
+(eq-test t #'fboundp 'test)
+(eq-test 'test #'fmakunbound 'test)
+(eq-test nil #'fboundp 'test)
+(eq-eval 'test '(defmacro test (x) x))
+(eq-test t #'fboundp 'test)
+(eq-test 'test #'fmakunbound 'test)
+
+;; fill - function
+(setq x (list 1 2 3 4))
+(equal-test '((4 4 4 4) (4 4 4 4) (4 4 4 4) (4 4 4 4)) #'fill x '(4 4 4 4))
+(eq-eval t '(eq (car x) (cadr x)))
+(equalp-test '#(a z z d e) #'fill '#(a b c d e) 'z :start 1 :end 3)
+(equal-test "012ee" #'fill (xseq "01234") #\e :start 3)
+(error-test #'fill 1 #\a)
+
+;; find - function
+(eql-test #\Space #'find #\d "here are some letters that can be looked at" :test #'char>)
+(eql-test 3 #'find-if #'oddp '(1 2 3 4 5) :end 3 :from-end t)
+(eq-test nil #'find-if-not #'complexp '#(3.5 2 #C(1.0 0.0) #C(0.0 1.0)) :start 2)
+(eq-test nil #'find 1 "abc")
+(error-test #'find 1 #c(1 2))
+
+;; find-symbol - function
+(equal-eval '(nil nil)
+ '(multiple-value-list (find-symbol "NEVER-BEFORE-USED")))
+(equal-eval '(nil nil)
+ '(multiple-value-list (find-symbol "NEVER-BEFORE-USED")))
+(setq test (multiple-value-list (intern "NEVER-BEFORE-USED")))
+(equal-eval test '(read-from-string "(never-before-used nil)"))
+(equal-eval '(never-before-used :internal)
+ '(multiple-value-list (intern "NEVER-BEFORE-USED")))
+(equal-eval '(never-before-used :internal)
+ '(multiple-value-list (find-symbol "NEVER-BEFORE-USED")))
+(equal-eval '(nil nil)
+ '(multiple-value-list (find-symbol "never-before-used")))
+(equal-eval '(car :inherited)
+ '(multiple-value-list (find-symbol "CAR" 'common-lisp-user)))
+(equal-eval '(car :external)
+ '(multiple-value-list (find-symbol "CAR" 'common-lisp)))
+;; XXX these will generate wrong results, NIL is not really a symbol
+;; currently in the interpreter
+(equal-eval '(nil :inherited)
+ '(multiple-value-list (find-symbol "NIL" 'common-lisp-user)))
+(equal-eval '(nil :external)
+ '(multiple-value-list (find-symbol "NIL" 'common-lisp)))
+(setq test (multiple-value-list
+ (find-symbol "NIL" (prog1 (make-package "JUST-TESTING" :use '())
+ (intern "NIL" "JUST-TESTING")))))
+(equal-eval (read-from-string "(just-testing::nil :internal)") 'test)
+(eq-eval t '(export 'just-testing::nil 'just-testing))
+(equal-eval '(just-testing:nil :external)
+ '(multiple-value-list (find-symbol "NIL" 'just-testing)))
+
+#+xedit (equal-eval '(nil nil)
+ '(multiple-value-list (find-symbol "NIL" "KEYWORD")))
+#|
+;; optional result of previous form:
+(equal-eval '(:nil :external)
+ '(multiple-value-list (find-symbol "NIL" "KEYWORD")))
+|#
+
+
+
+;; funcall - function
+(eql-test 6 #'funcall #'+ 1 2 3)
+(eql-test 1 #'funcall #'car '(1 2 3))
+(equal-test '(1 2 3) #'funcall #'list 1 2 3)
+
+
+
+;; TODO properly implement ``function''
+
+
+
+;; functionp - function (predicate)
+(eq-test nil #'functionp 'append)
+(eq-test t #'functionp #'append)
+(eq-test nil #'functionp '(lambda (x) (* x x)))
+(eq-test t #'functionp #'(lambda (x) (* x x)))
+(eq-test t #'functionp (symbol-function 'append))
+(eq-test nil #'functionp 1)
+(eq-test nil #'functionp nil)
+
+;; gensym - function
+(setq sym1 (gensym))
+(eq-test nil #'symbol-package sym1)
+(setq sym1 (gensym 100))
+(setq sym2 (gensym 100))
+(eq-test nil #'eq sym1 sym2)
+(eq-test nil #'equalp (gensym) (gensym))
+
+;; get - accessor
+(defun make-person (first-name last-name)
+ (let ((person (gensym "PERSON")))
+ (setf (get person 'first-name) first-name)
+ (setf (get person 'last-name) last-name)
+ person))
+(eq-eval '*john* '(defvar *john* (make-person "John" "Dow")))
+(eq-eval '*sally* '(defvar *sally* (make-person "Sally" "Jones")))
+(equal-eval "John" '(get *john* 'first-name))
+(equal-eval "Jones" '(get *sally* 'last-name))
+(defun marry (man woman married-name)
+ (setf (get man 'wife) woman)
+ (setf (get woman 'husband) man)
+ (setf (get man 'last-name) married-name)
+ (setf (get woman 'last-name) married-name)
+ married-name)
+(equal-eval "Dow-Jones" '(marry *john* *sally* "Dow-Jones"))
+(equal-eval "Dow-Jones" '(get *john* 'last-name))
+(equal-eval "Sally" '(get (get *john* 'wife) 'first-name))
+(equal-eval `(wife ,*sally* last-name "Dow-Jones" first-name "John")
+ '(symbol-plist *john*))
+(eq-eval 'age
+ '(defmacro age (person &optional (default ''thirty-something))
+ `(get ,person 'age ,default)))
+(eq-eval 'thirty-something '(age *john*))
+(eql-eval 20 '(age *john* 20))
+(eql-eval 25 '(setf (age *john*) 25))
+(eql-eval 25 '(age *john*))
+(eql-eval 25 '(age *john* 20))
+
+;; graphic-char-p - function
+(eq-test t #'graphic-char-p #\a)
+(eq-test t #'graphic-char-p #\Space)
+(eq-test nil #'graphic-char-p #\Newline)
+(eq-test nil #'graphic-char-p #\Tab)
+(eq-test nil #'graphic-char-p #\Rubout)
+
+;; if - special operator
+(eq-eval nil '(if nil t))
+(eq-eval nil '(if t nil t))
+(eq-eval nil '(if nil t nil))
+(eq-eval nil '(if nil t (if nil (if nil t) nil)))
+
+;; incf - macro
+(setq n 1)
+(eql-eval 2 '(incf n))
+(eql-eval 2 'n)
+(setq n 2147483647)
+(eql-eval 2147483648 '(incf n))
+(eql-eval 2147483648 'n)
+(setq n 0)
+(eql-eval 0.5d0 '(incf n 0.5d0))
+(eql-eval 0.5d0 'n)
+(setq n 1)
+(eql-eval 3/2 '(incf n 1/2))
+(eql-eval 3/2 'n)
+
+;; intersection - function
+(setq list1 (list 1 1 2 3 4 'a 'b 'c "A" "B" "C" "d")
+ list2 (list 1 4 5 'b 'c 'd "a" "B" "c" "D"))
+(equal-test '(1 1 4 b c) #'intersection list1 list2)
+(equal-test '(1 1 4 b c "B") #'intersection list1 list2 :test 'equal)
+(equal-test '(1 1 4 b c "A" "B" "C" "d")
+ #'intersection list1 list2 :test #'equalp)
+(setq list1 (nintersection list1 list2))
+(equal-eval '(1 1 4 b c) 'list1)
+(setq list1 (copy-list '((1 . 2) (2 . 3) (3 . 4) (4 . 5))))
+(setq list2 (copy-list '((1 . 3) (2 . 4) (3 . 6) (4 . 8))))
+(equal-test '((2 . 3) (3 . 4)) #'nintersection list1 list2 :key #'cdr)
+
+;; keywordp - function (predicate)
+(eq-test t #'keywordp :test)
+(eq-test nil #'keywordp 'test)
+(eq-test nil #'keywordp '#:test)
+(eq-test nil #'keywordp 1)
+(eq-test nil #'keywordp #'keywordp)
+(eq-test nil #'keywordp nil)
+
+;; last - function
+(equal-test '(3) #'last '(1 2 3))
+(equal-test '(2 . 3) #'last '(1 2 . 3))
+(eq-test nil #'last nil)
+(eql-test () #'last '(1 2 3) 0)
+(setq a '(1 . 2))
+(eql-test 2 #'last a 0)
+(eq-test a #'last a 1)
+(eq-test a #'last a 2)
+(eq-test t #'last t)
+(equal-test #c(1 2) #'last #c(1 2))
+(equalp-test #(1 2 3) #'last #(1 2 3))
+
+;; length - function
+(eql-test 3 #'length "abc")
+(eql-test 0 #'length nil)
+(eql-test 1 #'length '(1 . 2))
+(eql-test 2 #'length #(1 2))
+(error-test #'length #c(1 2))
+(error-test #'length t)
+
+;; let - special operator
+(eql-eval 2 '(setq a 1 b 2))
+(eql-eval 2 '(let ((a 2)) a))
+(eql-eval 1 'a)
+(eql-eval 1 '(let ((a 3) (b a)) b))
+(eql-eval 2 'b)
+
+;; let* - special operator
+(setq a 1 b 2)
+(eql-eval 2 '(let* ((a 2)) a))
+(eql-eval 1 'a)
+(eql-eval 3 '(let* ((a 3) (b a)) b))
+(eql-eval 2 'b)
+
+;; list - function
+(equal-test '(1) #'list 1)
+(equal-test '(3 4 a b 4) #'list 3 4 'a (car '(b . c)) (+ 6 -2))
+(eq-test nil #'list)
+
+;; list-length - function
+(eql-test 4 #'list-length '(a b c d))
+(eql-test 3 #'list-length '(a (b c) d))
+(eql-test 0 #'list-length '())
+(eql-test 0 #'list-length nil)
+(defun circular-list (&rest elements)
+ (let ((cycle (copy-list elements)))
+ (nconc cycle cycle)))
+(eq-test nil #'list-length (circular-list 'a 'b))
+(eq-test nil #'list-length (circular-list 'a))
+(eql-test 0 #'list-length (circular-list))
+
+;; list* - function
+(eql-test 1 #'list* 1)
+(equal-test '(a b c . d) #'list* 'a 'b 'c 'd)
+(error-test #'list*)
+(setq a '(1 2))
+(eq-test a #'list* a)
+
+;; listp - function (predicate)
+(eq-test t #'listp nil)
+(eq-test t #'listp '(1 . 2))
+(eq-test nil #'listp t)
+(eq-test nil #'listp #'listp)
+(eq-test nil #'listp #(1 2))
+(eq-test nil #'listp #c(1 2))
+
+;; lower-case-p - function
+(eq-test t #'lower-case-p #\a)
+(eq-test nil #'lower-case-p #\1)
+(eq-test nil #'lower-case-p #\Newline)
+(error-test #'lower-case-p 1)
+
+
+
+;; TODO make-array (will be rewritten)
+
+
+
+;; make-list - function
+(equal-test '(nil nil nil) #'make-list 3)
+(equal-test '((1 2) (1 2)) #'make-list 2 :initial-element '(1 2))
+(eq-test nil #'make-list 0)
+(eq-test nil #'make-list 0 :initial-element 1)
+
+;; make-package - function
+(setq pack1 (make-package "PACKAGE-1" :nicknames '("PACK-1" "PACK1")))
+(setq pack2 (make-package "PACKAGE-2" :nicknames '("PACK-2" "PACK2") :use '("PACK1")))
+(equal-test (list pack2) #'package-used-by-list pack1)
+(equal-test (list pack1) #'package-use-list pack2)
+(eq-test pack1 #'symbol-package 'pack1::test)
+(eq-test pack2 #'symbol-package 'pack2::test)
+
+;; make-string - function
+(equal-test "55555" #'make-string 5 :initial-element #\5)
+(equal-test "" #'make-string 0)
+(error-test #'make-string 10 :initial-element t)
+(error-test #'make-string 10 :initial-element nil)
+(error-test #'make-string 10 :initial-element 1)
+(eql-test 10 #'length (make-string 10))
+
+;; make-symbol - function
+(setq a "TEST")
+;; This will fail
+(eq-test nil #'eq (make-symbol a) (make-symbol a))
+(equal-test a #'symbol-name (make-symbol a))
+(setq temp-string "temp")
+(setq temp-symbol (make-symbol temp-string))
+(equal-test temp-string #'symbol-name temp-symbol)
+(equal-eval '(nil nil) '(multiple-value-list (find-symbol temp-string)))
+
+;; makunbound - function
+(eq-eval 1 '(setf (symbol-value 'a) 1))
+(eq-test t #'boundp 'a)
+(eql-eval 1 'a)
+(eq-test 'a #'makunbound 'a)
+(eq-test nil #'boundp 'a)
+(error-test #'makunbound 1)
+
+;; mapc - function
+(setq dummy nil)
+(equal-test '(1 2 3 4)
+ #'mapc #'(lambda (&rest x) (setq dummy (append dummy x)))
+ '(1 2 3 4)
+ '(a b c d e)
+ '(x y z))
+(equal-eval '(1 a x 2 b y 3 c z) 'dummy)
+
+;; mapcan - function
+(equal-test '(d 4 e 5)
+ #'mapcan #'(lambda (x y) (if (null x) nil (list x y)))
+ '(nil nil nil d e)
+ '(1 2 3 4 5 6))
+(equal-test '(1 3 4 5)
+ #'mapcan #'(lambda (x) (and (numberp x) (list x)))
+ '(a 1 b c 3 4 d 5))
+
+;; mapcar - function
+(equal-test '(1 2 3) #'mapcar #'car '((1 a) (2 b) (3 c)))
+(equal-test '(3 4 2 5 6) #'mapcar #'abs '(3 -4 2 -5 -6))
+(equal-test '((a . 1) (b . 2) (c . 3)) #'mapcar #'cons '(a b c) '(1 2 3))
+(equal-test '((1 3 5)) #'mapcar #'list* '(1 2) '(3 4) '((5)))
+(equal-test '((1 3 5) (2 4 6)) #'mapcar #'list* '(1 2) '(3 4) '((5) (6)))
+
+;; mapcon - function
+(equal-test '(1 a 2 b (3) c) #'mapcon #'car '((1 a) (2 b) ((3) c)))
+(equal-test '((1 2 3 4) (2 3 4) (3 4) (4)) #'mapcon #'list '(1 2 3 4))
+
+;; mapl - function
+(setq dummy nil)
+(equal-test '(1 2 3 4) #'mapl #'(lambda (x) (push x dummy)) '(1 2 3 4))
+(equal-eval '((4) (3 4) (2 3 4) (1 2 3 4)) 'dummy)
+
+;; maplist - function
+(equal-test '((1 2 3 4 1 2 1 2 3) (2 3 4 2 2 3))
+ #'maplist #'append '(1 2 3 4) '(1 2) '(1 2 3))
+(equal-test '((foo a b c d) (foo b c d) (foo c d) (foo d))
+ #'maplist #'(lambda (x) (cons 'foo x)) '(a b c d))
+(equal-test '(0 0 1 0 1 1 1)
+ #'maplist #'(lambda (x) (if (member (car x) (cdr x)) 0 1)) '(a b a c d b c))
+
+;; member - function
+(setq a '(1 2 3))
+(eq-test (cdr a) #'member 2 a)
+(setq a '((1 . 2) (3 . 4)))
+(eq-test (cdr a) #'member 2 a :test-not #'= :key #'cdr)
+(eq-test nil #'member 'e '(a b c d))
+(eq-test nil #'member 1 nil)
+(error-test #'member 2 '(1 . 2))
+(setq a '(a b nil c d))
+(eq-test (cddr a) #'member-if #'listp a)
+(setq a '(a #\Space 5/3 foo))
+(eq-test (cddr a) #'member-if #'numberp a)
+(setq a '(3 6 9 11 . 12))
+(eq-test (cdddr a) #'member-if-not #'zerop a :key #'(lambda (x) (mod x 3)))
+
+;; multiple-value-bind - macro
+(equal-eval '(11 9) '(multiple-value-bind (f r) (floor 130 11) (list f r)))
+
+;; multiple-value-call - special operator
+(equal-eval '(1 / 2 3 / / 2 0.5)
+ '(multiple-value-call #'list 1 '/ (values 2 3) '/ (values) '/ (floor 2.5)))
+(eql-eval 10 '(multiple-value-call #'+ (floor 5 3) (floor 19 4)))
+
+;; multiple-value-list - macro
+(equal-eval '(-1 1) '(multiple-value-list (floor -3 4)))
+(eql-eval nil '(multiple-value-list (values)))
+(equal-eval '(nil) '(multiple-value-list (values nil)))
+
+;; multiple-value-prog1 - special operator
+(setq temp '(1 2 3))
+(equal-eval temp
+ '(multiple-value-list
+ (multiple-value-prog1
+ (values-list temp)
+ (setq temp nil)
+ (values-list temp))))
+
+;; multiple-value-setq - macro
+(eql-eval 1 '(multiple-value-setq (quotient remainder) (truncate 3.5d0 2)))
+(eql-eval 1 quotient)
+(eql-eval 1.5d0 'remainder)
+(eql-eval 1 '(multiple-value-setq (a b c) (values 1 2)))
+(eql-eval 1 'a)
+(eql-eval 2 'b)
+(eq-eval nil 'c)
+(eql-eval 4 '(multiple-value-setq (a b) (values 4 5 6)))
+(eql-eval 4 'a)
+(eql-eval 5 'b)
+(setq a 1)
+(eql-eval nil '(multiple-value-setq (a) (values)))
+(eql-eval nil 'a)
+
+;; nconc - function
+(eq-test nil #'nconc)
+(setq x '(a b c))
+(setq y '(d e f))
+(equal-test '(a b c d e f) #'nconc x y)
+(equal-eval '(a b c d e f) 'x)
+(eq-test y #'cdddr x)
+(equal-test '(1 . 2) #'nconc (list 1) 2)
+(error-test #'nconc 1 2 3)
+(equal-eval '(k l m)
+ '(setq foo (list 'a 'b 'c 'd 'e)
+ bar (list 'f 'g 'h 'i 'j)
+ baz (list 'k 'l 'm)))
+(equal-test '(a b c d e f g h i j k l m) #'nconc foo bar baz)
+(equal-eval '(a b c d e f g h i j k l m) 'foo)
+(equal-eval (nthcdr 5 foo) 'bar)
+(equal-eval (nthcdr 10 foo) 'baz)
+(setq foo (list 'a 'b 'c 'd 'e)
+ bar (list 'f 'g 'h 'i 'j)
+ baz (list 'k 'l 'm))
+(equal-eval '(a b c d e f g h i j k l m) '(setq foo (nconc nil foo bar nil baz)))
+(equal-eval '(a b c d e f g h i j k l m) 'foo)
+(equal-eval (nthcdr 5 foo) 'bar)
+(equal-eval (nthcdr 10 foo) 'baz)
+
+;; notany - function
+(eql-test t #'notany #'> '(1 2 3 4) '(5 6 7 8) '(9 10 11 12))
+(eql-test t #'notany 'not-used ())
+(eql-test nil #'notany #'characterp #(1 2 3 4 5 #\6 7 8))
+
+;; notevery - function
+(eql-test nil #'notevery #'< '(1 2 3 4) '(5 6 7 8) '(9 10 11 12))
+(eql-test nil #'notevery 'not-used ())
+(eql-test t #'notevery #'numberp #(1 2 3 4 5 #\6 7 8))
+
+;; nth - accessor (function)
+(eql-test 'foo #'nth 0 '(foo bar baz))
+(eql-test 'bar #'nth 1 '(foo bar baz))
+(eq-test nil #'nth 3 '(foo bar baz))
+(error-test #'nth 0 #c(1 2))
+(error-test #'nth 0 #(1 2))
+(error-test #'nth 0 "test")
+
+;; nth-value - macro
+(equal-eval 'a '(nth-value 0 (values 'a 'b)))
+(equal-eval 'b '(nth-value 1 (values 'a 'b)))
+(eq-eval nil '(nth-value 2 (values 'a 'b)))
+(equal-eval '(3332987528 3332987528 t)
+ '(multiple-value-list
+ (let* ((x 83927472397238947423879243432432432)
+ (y 32423489732)
+ (a (nth-value 1 (floor x y)))
+ (b (mod x y)))
+ (values a b (= a b)))))
+
+;; nthcdr - function
+(eq-test nil #'nthcdr 0 '())
+(eq-test nil #'nthcdr 3 '())
+(equal-test '(a b c) #'nthcdr 0 '(a b c))
+(equal-test '(c) #'nthcdr 2 '(a b c))
+(eq-test () #'nthcdr 4 '(a b c))
+(eql-test 1 #'nthcdr 1 '(0 . 1))
+(error-test #'nthcdr -1 '(1 2))
+(error-test #'nthcdr #\Null '(1 2))
+(error-test #'nthcdr 1 t)
+(error-test #'nthcdr 1 #(1 2 3))
+
+;; or - macro
+(eq-eval nil '(or))
+(setq temp0 nil temp1 10 temp2 20 temp3 30)
+(eql-eval 10 '(or temp0 temp1 (setq temp2 37)))
+(eql-eval 20 'temp2)
+(eql-eval 11 '(or (incf temp1) (incf temp2) (incf temp3)))
+(eql-eval 11 'temp1)
+(eql-eval 20 temp2)
+(eql-eval 30 'temp3)
+(eql-eval 11 '(or (values) temp1))
+(eql-eval 11 '(or (values temp1 temp2) temp3))
+(equal-eval '(11 20) '(multiple-value-list (or temp0 (values temp1 temp2))))
+(equal-eval '(20 30)
+ '(multiple-value-list (or (values temp0 temp1) (values temp2 temp3))))
+
+;; packagep - function (predicate)
+(eq-test t #'packagep *package*)
+(eq-test nil #'packagep 10)
+(eq-test t #'packagep (make-package "TEST-PACKAGE"))
+(eq-test nil #'packagep 'keyword)
+(eq-test t #'packagep (find-package 'keyword))
+
+;; pairlis - function
+#+xedit ;; order of result may vary
+(progn
+ (equal-test '((one . 1) (two . 2) (three . 3) (four . 19))
+ #'pairlis '(one two) '(1 2) '((three . 3) (four . 19)))
+ (setq keys '(1 2 3)
+ data '("one" "two" "three")
+ alist '((4 . "four")))
+ (equal-test '((1 . "one") (2 . "two") (3 . "three"))
+ #'pairlis keys data)
+ (equal-test '((1 . "one") (2 . "two") (3 . "three") (4 . "four"))
+ #'pairlis keys data alist)
+ (equal-eval '(1 2 3) 'keys)
+ (equal-eval '("one" "two" "three") 'data)
+ (equal-eval '((4 . "four")) 'alist)
+ (eq-test nil #'pairlis 1 2)
+ (error-test #'pairlis '(1 2 3) '(4 5))
+)
+
+;; pop - macro
+(setq stack '(a b c) test stack)
+(eq-eval 'a '(pop stack))
+(eq-eval (cdr test) 'stack)
+(setq llst '((1 2 3 4)) test (car llst))
+(eq-eval 1 '(pop (car llst)))
+(eq-eval (cdr test) '(car llst))
+(error-eval '(pop 1))
+(error-eval '(pop nil))
+;; dotted list
+(setq stack (cons 1 2))
+(eq-eval 1 '(pop stack))
+(error-eval '(pop stack))
+;; circular list
+(setq stack '#1=(1 . #1#) *print-circle* t)
+(eql-eval 1 '(pop stack))
+(eql-eval 1 '(pop stack))
+(eql-eval 1 '(pop (cdr stack)))
+
+;; position - function
+(eql-test 4 #'position #\a "baobab" :from-end t)
+(eql-test 2 #'position-if #'oddp '((1) (2) (3) (4)) :start 1 :key #'car)
+(eq-test nil #'position 595 '())
+(eq-test 4 #'position-if-not #'integerp '(1 2 3 4 5.0))
+(eql-test 1 #'position (char-int #\1) "0123" :key #'char-int)
+
+;; prog - macro
+(eq-eval nil '(prog () :error))
+(eq-eval 'ok
+ '(prog ((a 0))
+ l1 (if (< a 10) (go l3) (go l2))
+ (return 'failed)
+ l2 (return 'ok)
+ (return 'failed)
+ l3 (incf a) (go l1)
+ (return 'failed)
+ ))
+(setq a 1)
+(eq-eval '/= '(prog ((a 2) (b a)) (return (if (= a b) '= '/=))))
+
+;; prog* - macro
+(setq a 1)
+(eq-eval nil '(prog* () :error))
+(eq-eval 'ok
+ '(prog* ((a 0) (b 0))
+ l1 (if (< a 10) (go l3) (go l2))
+ (return 'failed)
+ l2 (if (< b 10) (go l4) (return 'ok))
+ (return 'failed)
+ l3 (incf a) (go l1)
+ (return 'failed)
+ l4 (incf b) (setq a 0) (go l1)
+ (return 'failed)
+ ))
+(eq-eval '= '(prog* ((a 2) (b a)) (return (if (= a b) '= '/=))))
+
+;; prog1 - macro
+(setq temp 1)
+(eql-eval 1 '(prog1 temp (incf temp) (eql-eval 2 'temp) temp))
+(eql-eval 2 'temp)
+(eql-eval 2 '(prog1 temp (setq temp nil) (eql-eval nil 'temp) temp))
+(eq-eval nil 'temp)
+(eql-eval 1 '(prog1 (values 1 2 3) 4))
+(setq temp (list 'a 'b 'c))
+(eq-eval 'a '(prog1 (car temp) (setf (car temp) 'alpha)))
+(equal-eval '(alpha b c) 'temp)
+(equal-eval '(1)
+ '(multiple-value-list (prog1 (values 1 2) (values 4 5))))
+
+;; prog2 - macro
+(setq temp 1)
+(eql-eval 3 '(prog2 (incf temp) (incf temp) (incf temp)))
+(eql-eval 4 'temp)
+(eql-eval 2 '(prog2 1 (values 2 3 4) 5))
+(equal-eval '(3)
+ '(multiple-value-list (prog2 (values 1 2) (values 3 4) (values 5 6))))
+
+;; progn - special operator
+(eq-eval nil '(progn))
+(eql-eval 3 '(progn 1 2 3))
+(equal-eval '(1 2 3) '(multiple-value-list (progn (values 1 2 3))))
+(setq a 1)
+(eq-eval 'here '(if a (progn (setq a nil) 'here) (progn (setq a t) 'there)))
+(eq-eval nil 'a)
+
+;; progv - special operator
+(makunbound '*x*) ;; make sure it is not bound
+(setq *x* 1)
+(eql-eval 2 '(progv '(*x*) '(2) *x*))
+(eql-eval 1 '*x*)
+(equal-eval '(3 4)
+ '(let ((*x* 3)) (progv '(*x*) '(4) (list *x* (symbol-value '*x*)))))
+(makunbound '*x*)
+(defvar *x* 1)
+(equal-eval '(4 4)
+ '(let ((*x* 3)) (progv '(*x*) '(4) (list *x* (symbol-value '*x*)))))
+(equal-eval '(4 4)
+ '(multiple-value-list
+ (let ((*x* 3))
+ (progv '(*x*) '(4) (values-list (list *x* (symbol-value '*x*)))))))
+
+;; push - macro
+(setq llst '(nil))
+(equal-eval '(1) '(push 1 (car llst)))
+(equal-eval '((1)) 'llst)
+(equal-eval '(1 1) '(push 1 (car llst)))
+(equal-eval '((1 1)) 'llst)
+(setq x '(a (b c) d))
+(equal-eval '(5 B C) '(push 5 (cadr x)))
+(equal-eval '(a (5 b c) d) 'x)
+
+;; pushnew - macro
+(setq x '(a (b c) d))
+(equal-eval '(5 b c) '(pushnew 5 (cadr x)))
+(equal-eval '(a (5 b c) d) 'x)
+(equal-eval '(5 b c) '(pushnew 'b (cadr x)))
+(equal-eval '(a (5 b c) d) 'x)
+(setq lst '((1) (1 2) (1 2 3)))
+(equal-eval '((2) (1) (1 2) (1 2 3)) '(pushnew '(2) lst))
+(equal-eval '((1) (2) (1) (1 2) (1 2 3)) '(pushnew '(1) lst))
+(equal-eval '((1) (2) (1) (1 2) (1 2 3)) '(pushnew '(1) lst :test 'equal))
+(equal-eval '((1) (2) (1) (1 2) (1 2 3)) '(pushnew '(1) lst :key #'car))
+
+;; remove-duplicates - function
+(equal-test "aBcD" #'remove-duplicates "aBcDAbCd" :test #'char-equal :from-end t)
+(equal-test '(a c b d e) #'remove-duplicates '(a b c b d d e))
+(equal-test '(a b c d e) #'remove-duplicates '(a b c b d d e) :from-end t)
+(equal-test '((bar #\%) (baz #\A))
+ #'remove-duplicates '((foo #\a) (bar #\%) (baz #\A))
+ :test #'char-equal :key #'cadr)
+(equal-test '((foo #\a) (bar #\%))
+ #'remove-duplicates '((foo #\a) (bar #\%) (baz #\A))
+ :test #'char-equal :key #'cadr :from-end t)
+(setq tester (list 0 1 2 3 4 5 6))
+(equal-test '(0 4 5 6) #'delete-duplicates tester :key #'oddp :start 1 :end 6)
+
+;; replace - function
+(equal-test "abcd456hij"
+ #'replace (copy-seq "abcdefghij") "0123456789" :start1 4 :end1 7 :start2 4)
+(setq lst (xseq "012345678"))
+(equal-test "010123456" #'replace lst lst :start1 2 :start2 0)
+(equal-eval "010123456" 'lst)
+
+;; rest - accessor
+(equal-eval '(2) '(rest '(1 2)))
+(eql-eval 2 '(rest '(1 . 2)))
+(eq-eval nil '(rest '(1)))
+(setq *cons* '(1 . 2))
+(equal-eval "two" '(setf (rest *cons*) "two"))
+(equal-eval '(1 . "two") '*cons*)
+
+;; return - macro
+(eq-eval nil '(block nil (return) 1))
+(eql-eval 1 '(block nil (return 1) 2))
+(equal-eval '(1 2) '(multiple-value-list (block nil (return (values 1 2)) 3)))
+(eql-eval 1 '(block nil (block alpha (return 1) 2)))
+(eql-eval 2 '(block alpha (block nil (return 1)) 2))
+(eql-eval 1 '(block nil (block nil (return 1) 2)))
+
+;; return-from - special operator
+(eq-eval nil '(block alpha (return-from alpha) 1))
+(eql-eval 1 '(block alpha (return-from alpha 1) 2))
+(equal-eval '(1 2)
+ '(multiple-value-list (block alpha (return-from alpha (values 1 2)) 3)))
+(eql-eval 2
+ '(let ((a 0)) (dotimes (i 10) (incf a) (when (oddp i) (return))) a))
+(eq-eval 'temp '(defun temp (x) (if x (return-from temp ''dummy)) 44))
+(eql-eval 44 '(temp nil))
+(eq-eval 'dummy (temp t))
+(eql-eval 2 (block nil (unwind-protect (return-from nil 1) (return-from nil 2))))
+(error-eval '(funcall (block nil #'(lambda () (return-from nil)))))
+
+;; reverse - function
+(setq str (xseq "abc") test str)
+(equal-test "cba" #'reverse str)
+(eq-eval test 'str)
+(equal-eval "cba" '(setq test (nreverse str)))
+(equal-eval "cba" 'test)
+(setq l (list 1 2 3) test l)
+(equal-eval '(3 2 1) '(setq test (nreverse l)))
+(equal-eval '(3 2 1) 'test)
+
+;; rplac? - function
+(eql-eval '*some-list*
+ '(defparameter *some-list* (list* 'one 'two 'three 'four)))
+(equal-eval '(one two three . four) '*some-list*)
+(equal-test '(uno two three . four) #'rplaca *some-list* 'uno)
+(equal-eval '(uno two three . four) '*some-list*)
+(equal-test '(three iv) #'rplacd (last *some-list*) (list 'iv))
+(equal-eval '(uno two three iv) '*some-list*)
+
+;; search - function
+(eql-test 7 #'search "dog" "it's a dog's life")
+(eql-test 2 #'search '(0 1) '(2 4 6 1 3 5) :key #'oddp)
+(eql-test 8 #'search "foo" "foooobarfooooobarfo" :from-end t)
+(eql-test 5
+ #'search "123"
+ (mapcar #'(lambda (x) (+ x (char-code #\0)))
+ '(1 2 34 3 2 1 2 3 4 3 2 1)) :from-end t
+ :key #'(lambda (x) (if (integerp x) (code-char x) x)))
+(eql-test 0 #'search "abc" "abcd" :from-end t)
+(eql-test 3 #'search "bar" "foobar")
+
+;; set - function
+(eql-eval 1 '(setf (symbol-value 'n) 1))
+(eql-test 2 #'set 'n 2)
+(eql-test 2 #'symbol-value 'n)
+(eql-eval 4
+ '(let ((n 3))
+ (setq n (+ n 1))
+ (setf (symbol-value 'n) (* n 10))
+ (set 'n (+ (symbol-value 'n) n))
+ n))
+(eql-eval 44 'n)
+(defvar *n* 2)
+(eql-eval 80
+ '(let ((*n* 3))
+ (setq *n* (+ *n* 1))
+ (setf (symbol-value '*n*) (* *n* 10))
+ (set '*n* (+ (symbol-value '*n*) *n*))
+ *n*))
+(eql-eval 2 '*n*)
+(eq-eval '*even-count* '(defvar *even-count* 0))
+(eq-eval '*odd-count* '(defvar *odd-count* 0))
+(eql-eval 'tally-list
+ '(defun tally-list (list)
+ (dolist (element list)
+ (set (if (evenp element) '*even-count* '*odd-count*)
+ (+ element (if (evenp element) *even-count* *odd-count*))))))
+(eq-eval nil '(tally-list '(1 9 4 3 2 7)))
+(eql-eval 6 '*even-count*)
+(eql-eval 20 '*odd-count*)
+
+;; set-difference - function
+(setq lst1 (list "A" "b" "C" "d") lst2 (list "a" "B" "C" "d"))
+(equal-test '("A" "b" "C" "d") #'set-difference lst1 lst2)
+(equal-test '("A" "b") #'set-difference lst1 lst2 :test 'equal)
+(eq-test nil #'set-difference lst1 lst2 :test #'equalp)
+(equal-test '("A" "b") #'nset-difference lst1 lst2 :test #'string=)
+(setq lst1 '(("a" . "b") ("c" . "d") ("e" . "f"))
+ lst2 '(("c" . "a") ("e" . "b") ("d" . "a")))
+(equal-test '(("c" . "d") ("e" . "f"))
+ #'nset-difference lst1 lst2 :test #'string= :key #'cdr)
+(equal-eval '(("c" . "a") ("e" . "b") ("d" . "a")) 'lst2)
+(equal-test '("banana" "lemon" "rhubarb")
+ #'set-difference
+ '("strawberry" "chocolate" "banana" "lemon" "pistachio" "rhubarb")
+ '(#\c #\w) :test #'(lambda (s c) (find c s)))
+
+;; set-exclusive-or - function
+(setq lst1 (list 1 "a" "b") lst2 (list 1 "A" "b"))
+(equal-test '("a" "b" "A" "b") #'set-exclusive-or lst1 lst2)
+(equal-test '("a" "A") #'set-exclusive-or lst1 lst2 :test #'equal)
+(eq-test nil #'set-exclusive-or lst1 lst2 :test 'equalp)
+(equal-test '("a" "b" "A" "b") #'nset-exclusive-or lst1 lst2)
+(setq lst1 '(("a" . "b") ("c" . "d") ("e" . "f"))
+ lst2 '(("c" . "a") ("e" . "b") ("d" . "a")))
+(equal-test '(("c" . "d") ("e" . "f") ("c" . "a") ("d" . "a"))
+ #'nset-exclusive-or lst1 lst2 :test #'string= :key #'cdr)
+
+;; setf - macro
+(setq x (cons 'a 'b) y (list 1 2 3))
+(equal-eval '(1 x 3) '(setf (car x) 'x (cadr y) (car x) (cdr x) y))
+(equal-eval '(x 1 x 3) 'x)
+(equal-eval '(1 x 3) 'y)
+(setq x (cons 'a 'b) y (list 1 2 3))
+(eq-eval nil '(psetf (car x) 'x (cadr y) (car x) (cdr x) y))
+(equal-eval '(x 1 a 3) 'x)
+(equal-eval '(1 a 3) 'y)
+(error-eval '(setf x))
+(error-eval '(psetf x))
+
+;; setq - special form
+(eql-eval 3 '(setq a 1 b 2 c 3))
+(eql-eval 1 'a)
+(eql-eval 2 'b)
+(eql-eval 3 'c)
+(eql-eval 7 '(setq a (1+ b) b (1+ a) c (+ a b)))
+(eql-eval 3 'a)
+(eql-eval 4 'b)
+(eql-eval 7 'c)
+(eq-eval nil '(psetq a 1 b 2 c 3))
+(eql-eval 1 'a)
+(eql-eval 2 'b)
+(eql-eval 3 'c)
+(equal-eval '(2 1)
+ '(multiple-value-list (let ((a 1) (b 2)) (psetq a b b a) (values a b))))
+(error-eval '(setq x))
+(error-eval '(setq x 1 y))
+
+;; some - function
+(eq-test t #'some #'= '(1 2 3 4 5) '(5 4 3 2 1))
+
+;; sort - function
+(setq tester (copy-seq "lkjashd"))
+(equal-test "adhjkls" #'sort tester #'char-lessp)
+(setq tester (list '(1 2 3) '(4 5 6) '(7 8 9)))
+(equal-test '((7 8 9) (4 5 6) (1 2 3)) #'sort tester #'> :key #'car)
+(setq tester (list 1 2 3 4 5 6 7 8 9 0))
+(equal-test '(1 3 5 7 9 2 4 6 8 0)
+ #'stable-sort tester #'(lambda (x y) (and (oddp x) (evenp y))))
+(equalp-test
+ #((("Kathy" "Chapman") "Editorial")
+ (("Dick" "Gabriel") "Objects")
+ (("Gregor" "Kiczales") "Objects")
+ (("Sandra" "Loosemore") "Compiler")
+ (("Larry" "Masinter") "Cleanup")
+ (("David" "Moon") "Objects")
+ (("Kent" "Pitman") "Conditions")
+ (("Dick" "Waters") "Iteration")
+ (("JonL" "White") "Iteration"))
+ #'sort (setq committee-data
+ (vector (list (list "JonL" "White") "Iteration")
+ (list (list "Dick" "Waters") "Iteration")
+ (list (list "Dick" "Gabriel") "Objects")
+ (list (list "Kent" "Pitman") "Conditions")
+ (list (list "Gregor" "Kiczales") "Objects")
+ (list (list "David" "Moon") "Objects")
+ (list (list "Kathy" "Chapman") "Editorial")
+ (list (list "Larry" "Masinter") "Cleanup")
+ (list (list "Sandra" "Loosemore") "Compiler")))
+ #'string-lessp :key #'cadar)
+(equalp-eval
+ #((("Larry" "Masinter") "Cleanup")
+ (("Sandra" "Loosemore") "Compiler")
+ (("Kent" "Pitman") "Conditions")
+ (("Kathy" "Chapman") "Editorial")
+ (("Dick" "Waters") "Iteration")
+ (("JonL" "White") "Iteration")
+ (("Dick" "Gabriel") "Objects")
+ (("Gregor" "Kiczales") "Objects")
+ (("David" "Moon") "Objects"))
+ '(setq committee-data
+ (stable-sort committee-data #'string-lessp :key #'cadr)))
+(error-test #'sort #c(1 2))
+
+;; string - function
+(setq a "already a string")
+(eq-test a #'string a)
+(equal-test "ELM" #'string 'elm)
+(equal-test "c" #'string #\c)
+
+;; string-* - function
+(eq-test t #'string= "foo" "foo")
+(eq-test nil #'string= "foo" "Foo")
+(eq-test nil #'string= "foo" "bar")
+(eq-test t #'string= "together" "frog" :start1 1 :end1 3 :start2 2)
+(eq-test t #'string-equal "foo" "Foo")
+(eq-test t #'string= "abcd" "01234abcd9012" :start2 5 :end2 9)
+(eql-test 3 #'string< "aaaa" "aaab")
+(eql-test 4 #'string>= "aaaaa" "aaaa")
+(eql-test 5 #'string-not-greaterp "Abcde" "abcdE")
+(eql-test 6 #'string-lessp "012AAAA789" "01aaab6" :start1 3 :end1 7
+ :start2 2 :end2 6)
+(eq-test nil #'string-not-equal "AAAA" "aaaA")
+(error-test #'string= #(1 2 3) '(1 2 3))
+(eql-test 0 #'string< "abcd" "efg")
+(eql-test 1 #'string< "abcd" "afg")
+(eql-test 0 #'string/= "foo" "baar")
+(eql-test nil #'string/= "foobar" "foobar")
+
+;; string-{upcase,downcase,capitalize} - function
+(equal-test "ABCDE" #'string-upcase "abcde")
+(equal-test "aBCDe" #'string-upcase "abcde" :start 1 :end 4)
+(equal-test "aBCDe" #'nstring-upcase (xseq "abcde") :start 1 :end 4)
+(equal-test "DR. LIVINGSTON, I PRESUME?"
+ #'string-upcase "Dr. Livingston, I presume?")
+(equal-test "Dr. LIVINGSTON, I Presume?"
+ #'string-upcase "Dr. Livingston, I presume?" :start 4 :end 19)
+(equal-test "Dr. LIVINGSTON, I Presume?"
+ #'nstring-upcase (xseq "Dr. Livingston, I presume?") :start 4 :end 19)
+(equal-test "Dr. LiVINGston, I presume?"
+ #'string-upcase "Dr. Livingston, I presume?" :start 6 :end 10)
+(equal-test "Dr. LiVINGston, I presume?"
+ #'nstring-upcase (xseq "Dr. Livingston, I presume?") :start 6 :end 10)
+(equal-test "dr. livingston, i presume?"
+ #'string-downcase "Dr. Livingston, I presume?")
+(equal-test "Dr. livingston, i Presume?"
+ #'string-downcase "Dr. Livingston, I Presume?" :start 1 :end 17)
+(equal-test "Dr. livingston, i Presume?"
+ #'nstring-downcase (xseq "Dr. Livingston, I Presume?") :start 1 :end 17)
+(equal-test "Elm 13c Arthur;Fig Don'T"
+ #'string-capitalize "elm 13c arthur;fig don't")
+(equal-test "elm 13C Arthur;Fig Don't"
+ #'string-capitalize "elm 13c arthur;fig don't" :start 6 :end 21)
+(equal-test "elm 13C Arthur;Fig Don't"
+ #'nstring-capitalize (xseq "elm 13c arthur;fig don't") :start 6 :end 21)
+(equal-test " Hello " #'string-capitalize " hello ")
+(equal-test " Hello " #'nstring-capitalize (xseq " hello "))
+(equal-test "Occluded Casements Forestall Inadvertent Defenestration"
+ #'string-capitalize "occlUDeD cASEmenTs FOreSTAll iNADVertent DEFenestraTION")
+(equal-test "Don'T!" #'string-capitalize "DON'T!")
+(equal-test "Pipe 13a, Foo16c" #'string-capitalize "pipe 13a, foo16c")
+(setq str (copy-seq "0123ABCD890a"))
+(equal-test "0123AbcD890a" #'nstring-downcase str :start 5 :end 7)
+(equal-eval "0123AbcD890a" 'str)
+(error-test #'nstring-capitalize 1)
+(error-test #'string-capitalize "foobar" :start 4 :end 2)
+(equal-test "foobar" #'string-capitalize "foobar" :start 0 :end 0)
+
+;; string-{,left-,right-}trim - function
+(equal-test "kaaak" #'string-trim "abc" "abcaakaaakabcaaa")
+#+xedit (equal-test "kaaak" #'nstring-trim "abc" "abcaakaaakabcaaa")
+(equal-test "garbanzo beans"
+ #'string-trim '(#\Space #\Tab #\Newline) " garbanzo beans
+ ")
+#+xedit (equal-test "garbanzo beans"
+ #'nstring-trim '(#\Space #\Tab #\Newline) " garbanzo beans
+ ")
+(equal-test "three (silly) words"
+ #'string-trim " (*)" " ( *three (silly) words* ) ")
+#+xedit (equal-test "three (silly) words"
+ #'nstring-trim " (*)" " ( *three (silly) words* ) ")
+(equal-test "labcabcabc" #'string-left-trim "abc" "labcabcabc")
+#+xedit (equal-test "labcabcabc" #'nstring-left-trim "abc" "labcabcabc")
+(equal-test "three (silly) words* ) "
+ #'string-left-trim " (*)" " ( *three (silly) words* ) ")
+#+xedit (equal-test "three (silly) words* ) "
+ #'nstring-left-trim " (*)" " ( *three (silly) words* ) ")
+(equal-test " ( *three (silly) words"
+ #'string-right-trim " (*)" " ( *three (silly) words* ) ")
+#+xedit (equal-test " ( *three (silly) words"
+ #'nstring-right-trim " (*)" " ( *three (silly) words* ) ")
+(error-test #'string-trim 123 "123")
+(error-test #'string-left-trim 123 "123")
+
+;; stringp - function (predicate)
+(eq-test t #'stringp "abc")
+(eq-test nil #'stringp #\a)
+(eq-test nil #'stringp 1)
+(eq-test nil #'stringp #(#\a #\b #\c))
+
+;; subseq - accessor
+(setq str (xseq "012345"))
+(equal-test "2345" #'subseq str 2)
+(equal-test "34" #'subseq str 3 5)
+(equal-eval "abc" '(setf (subseq str 4) "abc"))
+(equal-eval "0123ab" 'str)
+(equal-eval "A" '(setf (subseq str 0 2) "A"))
+(equal-eval "A123ab" 'str)
+
+;; subsetp - function
+(setq cosmos '(1 "a" (1 2)))
+(eq-test t #'subsetp '(1) cosmos)
+(eq-test nil #'subsetp '((1 2)) cosmos)
+(eq-test t #'subsetp '((1 2)) cosmos :test 'equal)
+(eq-test t #'subsetp '(1 "A") cosmos :test #'equalp)
+(eq-test nil #'subsetp '((1) (2)) '((1) (2)))
+(eq-test t #'subsetp '((1) (2)) '((1) (2)) :key #'car)
+
+;; svref - function
+;; XXX vectors will be reimplemented, just a test for the current implementation
+(setq v (vector 1 2 'sirens))
+(eql-eval 1 '(svref v 0))
+(eql-eval 'sirens '(svref v 2))
+(eql-eval 'newcomer '(setf (svref v 1) 'newcomer))
+(equalp-eval #(1 newcomer sirens) 'v)
+
+;; symbol-name - function
+(equal-test "TEMP" #'symbol-name 'temp)
+(equal-test "START" #'symbol-name :start)
+(error-test #'symbol-name 1)
+
+;; symbol-package - function
+(eq-test (find-package "LISP") #'symbol-package 'car)
+(eql-test *package* #'symbol-package 'bus)
+(eq-test (find-package "KEYWORD") #'symbol-package :optional)
+;; Gensyms are uninterned, so have no home package.
+(eq-test nil #'symbol-package (gensym))
+(setq pk1 (make-package 'pk1))
+(intern "SAMPLE1" "PK1")
+(eq-eval t '(export (find-symbol "SAMPLE1" "PK1") "PK1"))
+(setq pk2 (make-package 'pk2 :use '(pk1)))
+(equal-eval '(pk1:sample1 :inherited)
+ '(multiple-value-list (find-symbol "SAMPLE1" "PK2")))
+(eq-test pk1 #'symbol-package 'pk1::sample1)
+(eq-test pk1 #'symbol-package 'pk2::sample1)
+(eq-test pk1 #'symbol-package 'pk1::sample2)
+(eq-test pk2 #'symbol-package 'pk2::sample2)
+;; The next several forms create a scenario in which a symbol
+;; is not really uninterned, but is "apparently uninterned",
+;; and so SYMBOL-PACKAGE still returns NIL.
+(setq s3 'pk1::sample3)
+(eq-eval t '(import s3 'pk2))
+(eq-eval t '(unintern s3 'pk1)) ;; XXX unintern not yet implemented
+(eq-test nil #'symbol-package s3) ;; fail due to unintern not implemented
+(eq-test t #'eq s3 'pk2::sample3)
+
+;; symbol-plist - accessor
+(setq sym (gensym))
+(eq-eval () '(symbol-plist sym))
+(eq-eval 'val1 '(setf (get sym 'prop1) 'val1))
+(equal-eval '(prop1 val1) '(symbol-plist sym))
+(eq-eval 'val2 '(setf (get sym 'prop2) 'val2))
+(equal-eval '(prop2 val2 prop1 val1) '(symbol-plist sym))
+(setq sym-plist (list 'prop3 'val3))
+(eq-eval sym-plist '(setf (symbol-plist sym) sym-plist))
+(eq-eval sym-plist '(symbol-plist sym))
+
+;; symbol-value - accessor
+(eql-eval 1 '(setf (symbol-value 'a) 1))
+(eql-eval 1 '(symbol-value 'a))
+;; SYMBOL-VALUE cannot see lexical variables.
+(eql-eval 1 '(let ((a 2)) (symbol-value 'a)))
+(eql-eval 1 '(let ((a 2)) (setq a 3) (symbol-value 'a)))
+
+#+xedit ;; incorrect...
+(progn
+ ;; SYMBOL-VALUE can see dynamic variables.
+ ;; declare not yet implemented
+ (proclaim '(special a))
+ (eql-eval 2 '(let ((a 2)) (symbol-value 'a)))
+ (eql-eval 1 'a)
+ (eql-eval 3 '(let ((a 2)) (setq a 3) (symbol-value 'a)))
+ (eql-eval 1 'a)
+ ;; declare not yet implement
+ (makunbound 'a)
+ (eql-eval 2 '(let ((a 2)) (setf (symbol-value 'a) 3) a))
+ (eql-eval 3 'a)
+ (eql-eval 3 '(symbol-value 'a))
+ ;; declare not yet implement
+ (makunbound 'a)
+ (equal-eval '(5 4)
+ '(multiple-value-list
+ (let ((a 4))
+
+ ;; declare not yet implemented
+ (defparameter a 3)
+
+ (let ((b (symbol-value 'a)))
+ (setf (symbol-value 'a) 5)
+ (values a b)))))
+ (eql-eval 3 'a)
+)
+(eq-eval :any-keyword '(symbol-value :any-keyword))
+;; XXX these will fail
+(eq-eval nil '(symbol-value 'nil))
+(eq-eval nil '(symbol-value '()))
+
+;; symbolp - function (predicate)
+(eq-test t #'symbolp 'elephant)
+(eq-test nil #'symbolp 12)
+;; XXX these will fail
+(eq-test t #'symbolp nil)
+(eq-test t #'symbolp '())
+(eq-test t #'symbolp :test)
+(eq-test nil #'symbolp "hello")
+
+;; remprop - function
+(setq test (make-symbol "PSEUDO-PI"))
+(eq-eval () '(symbol-plist test))
+(eq-eval t '(setf (get test 'constant) t))
+(eql-eval 3.14 '(setf (get test 'approximation) 3.14))
+(eql-eval 'noticeable '(setf (get test 'error-range) 'noticeable))
+(equal-eval '(error-range noticeable approximation 3.14 constant t)
+ '(symbol-plist test))
+(eq-eval nil '(setf (get test 'approximation) nil))
+(equal-eval '(error-range noticeable approximation nil constant t)
+ '(symbol-plist test))
+(eq-eval nil (get test 'approximation))
+(eq-test t #'remprop test 'approximation)
+(eq-eval nil '(get test 'approximation))
+(equal-eval '(error-range noticeable constant t) '(symbol-plist test))
+(eq-test nil #'remprop test 'approximation)
+(equal-eval '(error-range noticeable constant t) '(symbol-plist test))
+(eq-test t #'remprop test 'error-range)
+(eql-eval 3 '(setf (get test 'approximation) 3))
+(equal-eval '(approximation 3 constant t) '(symbol-plist test))
+
+;; throw - special operator
+(equal-eval '(3 9)
+ '(multiple-value-list
+ (catch 'result
+ (setq i 0 j 0)
+ (loop (incf j 3) (incf i)
+ (if (= i 3) (throw 'result (values i j)))))))
+(eql-eval 2 '(catch nil (unwind-protect (throw nil 1) (throw nil 2))))
+
+;; XXX undefined consequences
+(eql-eval 2
+ '(catch 'a
+ (catch 'b
+ (unwind-protect (throw 'a 1)
+ (throw 'b 2)))))
+(eq-eval :outer-catch
+ '(catch 'foo
+ (setq string (format nil "The inner catch returns ~s."
+ (catch 'foo
+ (unwind-protect (throw 'foo :first-throw)
+ (throw 'foo :second-throw)))))
+ :outer-catch))
+(equal-eval "The inner catch returns :SECOND-THROW." 'string)
+
+;; tree-equal - function
+(setq tree1 '(1 (1 2))
+ tree2 '(1 (1 2)))
+(eq-test t #'tree-equal tree1 tree2)
+(eq-test nil #'eql tree1 tree2)
+(setq tree1 '('a ('b 'c))
+ tree2 '('a ('b 'c)))
+(eq-test t #'tree-equal tree1 tree2 :test 'eq)
+(eq-test t #'tree-equal 1 1)
+(eq-test nil #'tree-equal (list 1 2) (cons 1 2))
+(eq-test nil #'tree-equal 1 2)
+
+;; union - function
+(equal-test '(b c f a d) #'union '(a b c) '(f a d))
+(equal-test '((y 6) (z 2) (x 4))
+ #'union '((x 5) (y 6)) '((z 2) (x 4)) :key #'car)
+(setq lst1 (list 1 2 '(1 2) "a" "b")
+ lst2 (list 2 3 '(2 3) "B" "C"))
+(equal-test '(1 (1 2) "a" "b" 2 3 (2 3) "B" "C") #'nunion lst1 lst2)
+
+;; unless - macro
+(eq-eval 'hello '(when t 'hello))
+(eq-eval nil '(unless t 'hello))
+(eq-eval nil (when nil 'hello))
+(eq-eval 'hello '(unless nil 'hello))
+(eq-eval nil (when t))
+(eql-eval nil '(unless nil))
+(setq test nil)
+(equal-eval '(3 2 1) '(when t (push 1 test) (push 2 test) (push 3 test)))
+(equal-eval '(3 2 1) 'test)
+(setq test nil)
+(eq-eval nil '(unless t (push 1 test) (push 2 test) (push 3 test)))
+(eq-eval nil 'test)
+(eq-eval nil '(when nil (push 1 test) (push 2 test) (push 3 test)))
+(eq-eval nil 'test)
+(equal-eval '(3 2 1) '(unless nil (push 1 test) (push 2 test) (push 3 test)))
+(equal-eval '(3 2 1) 'test)
+(equal-eval '((4) nil (5) nil 6 (6) 7 (7))
+ '(let ((x 3))
+ (list (when (oddp x) (incf x) (list x))
+ (when (oddp x) (incf x) (list x))
+ (unless (oddp x) (incf x) (list x))
+ (unless (oddp x) (incf x) (list x))
+ (if (oddp x) (incf x) (list x))
+ (if (oddp x) (incf x) (list x))
+ (if (not (oddp x)) (incf x) (list x))
+ (if (not (oddp x)) (incf x) (list x)))))
+
+;; unwind-protect - special operator
+(defun dummy-function (x)
+ (setq state 'running)
+ (unless (numberp x) (throw 'abort 'not-a-number))
+ (setq state (1+ x)))
+(eql-eval 2 '(catch 'abort (dummy-function 1)))
+(eql-eval 2 'state)
+(eq-eval 'not-a-number '(catch 'abort (dummy-function 'trash)))
+(eq-eval 'running 'state)
+(eq-eval 'not-a-number
+ '(catch 'abort (unwind-protect (dummy-function 'trash)
+ (setq state 'aborted))))
+(eq-eval 'aborted 'state)
+(eql-eval 2 '(block nil (unwind-protect (return 1) (return 2))))
+;; XXX undefined consequences
+(eql-eval 2
+ '(block a
+ (block b
+ (unwind-protect (return-from a 1)
+ (return-from b 2)))))
+(eql-eval 2 '(catch nil (unwind-protect (throw nil 1) (throw nil 2))))
+;; XXX undefined consequences
+(eql-eval 2
+ '(catch 'a (catch 'b (unwind-protect (throw 'a 1) (throw 'b 2)))))
+(eq-eval ':outer-catch
+ '(catch 'foo
+ (setq string
+ (format nil "The inner catch returns ~s."
+ (catch 'foo
+ (unwind-protect (throw 'foo :first-throw)
+ (throw 'foo :second-throw)))))
+ :outer-catch))
+(equal-eval "The inner catch returns :SECOND-THROW." 'string)
+(eql-eval 10
+ '(catch 'a
+ (catch 'b
+ (unwind-protect (1+ (catch 'a (throw 'b 1)))
+ (throw 'a 10)))))
+;; XXX undefined consequences
+(eql-eval 4
+ '(catch 'foo
+ (catch 'bar
+ (unwind-protect (throw 'foo 3)
+ (throw 'bar 4)
+ (print 'xxx)))))
+(eql-eval 4
+ '(catch 'bar
+ (catch 'foo
+ (unwind-protect (throw 'foo 3)
+ (throw 'bar 4)
+ (print 'xxx)))))
+(eql-eval 5
+ '(block nil
+ (let ((x 5))
+ (unwind-protect (return)
+ (return x)))))
+
+;; upper-case-p - function
+(eq-test t #'upper-case-p #\A)
+(eq-test nil #'upper-case-p #\a)
+(eq-test nil #'upper-case-p #\5)
+(error-test #'upper-case-p 1)
+
+;; values - accessor
+(eq-eval () '(multiple-value-list (values)))
+(equal-eval '(1) '(multiple-value-list (values 1)))
+(equal-eval '(1 2) '(multiple-value-list (values 1 2)))
+(equal-eval '(1 2 3) '(multiple-value-list (values 1 2 3)))
+(equal-eval '(1 4 5) '(multiple-value-list (values (values 1 2 3) 4 5)))
+
+;; values-list - function
+(eq-eval nil '(multiple-value-list (values-list nil)))
+(equal-eval '(1) '(multiple-value-list (values-list '(1))))
+(equal-eval '(1 2) '(multiple-value-list (values-list '(1 2))))
+(equal-eval '(1 2 3) '(multiple-value-list (values-list '(1 2 3))))
diff --git a/lisp/test/math.lsp b/lisp/test/math.lsp
new file mode 100644
index 0000000..162f73f
--- /dev/null
+++ b/lisp/test/math.lsp
@@ -0,0 +1,982 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/test/math.lsp,v 1.5 2003/01/30 02:46:26 paulo Exp $
+;;
+
+;; basic math tests
+;; This is far from a good regression test, but in the current stage of
+;; the interpreter, this is good enough to make sure it is not "so"
+;; broken. But note that this does not test all cases where there is
+;; change in the type of a numeric object.
+
+(setq *default-float-format* 'double-float)
+
+;; floating point results may differ from implementation to implementation (?!)
+
+(defun test (expect function &rest arguments &aux result (error t))
+ (ignore-errors
+ (setq result (apply function arguments))
+ (setq error nil)
+ )
+ (if error
+ (format t "ERROR: (~A~{ ~A~})~%" function arguments)
+ ;; Use eql to make sure result and expect have the same type
+ (or (eql result expect)
+#-xedit ;; hack...
+ (or
+ (and
+ (floatp result)
+ (floatp expect)
+ (< (abs (- (abs result) (abs expect)))
+ 0.00000000000001d0)
+ )
+ (format t "(~A~{ ~A~}) => should be ~A not ~A~%"
+ function arguments expect result
+ )
+ )
+#+xedit (format t "(~A~{ ~A~}) => should be ~A not ~A~%"
+ function arguments expect result
+ )
+ )
+ )
+)
+
+(defun div-test (quotient remainder function &rest arguments
+ &aux quo rem (error t))
+ (ignore-errors
+ (multiple-value-setq (quo rem) (apply function arguments))
+ (setq error nil)
+ )
+ (if error
+ (format t "ERROR: (~A~{ ~A~})~%" function arguments)
+ (or (and (eql quotient quo) (eql remainder rem))
+#-xedit ;; hack
+ (or
+ (or
+ (eql quotient quo)
+ (and
+ (floatp quotient)
+ (floatp quo)
+ (< (abs (- (abs quotient) (abs quo)))
+ 0.00000000000001d0)
+ )
+ )
+ (or
+ (eql remainder rem)
+ (and
+ (floatp remainder)
+ (floatp rem)
+ (< (abs (- (abs remainder) (abs rem)))
+ 0.00000000000001d0)
+ )
+ )
+ (format t "(~A~{ ~A~}) => should be ~A; ~A not ~A; ~A~%"
+ function arguments quotient remainder quo rem
+ )
+ )
+#+xedit (format t "(~A~{ ~A~}) => should be ~A; ~A not ~A; ~A~%"
+ function arguments quotient remainder quo rem
+ )
+ )
+ )
+)
+
+(defun bool-test (expect function &rest arguments &aux result (error t))
+ (ignore-errors
+ (setq result (apply function arguments))
+ (setq error nil)
+ )
+ (if error
+ (format t "ERROR: (~A~{ ~A~})~%" function arguments)
+ (or (eq result expect)
+ (format t "(~A~{ ~A~}) => should be ~A not ~A~%"
+ function arguments expect result
+ )
+ )
+ )
+)
+
+(defun error-test (function &rest arguments &aux result (error t))
+ (ignore-errors
+ (setq result (apply function arguments))
+ (setq error nil))
+ (unless error
+ (format t "ERROR: no error for (~A~{ ~A}), result was ~A~%"
+ function arguments result)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixnum fixnum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 0 #'+)
+(test 5 #'+ 5)
+(test -2 #'+ -2)
+(test 3 #'+ 2 1)
+(test 134217728 #'+ 134217727 1)
+(test -134217729 #'+ -134217728 -1)
+(test 2147483648 #'+ 2147483647 1)
+(test -2147483649 #'+ -2147483648 -1)
+(test -5 #'- 5)
+(test 6 #'- -6)
+(test 1 #'- 2 1)
+(test 134217728 #'- 134217727 -1)
+(test -2147483649 #'- -2147483648 1)
+(test 4294967295 #'- 2147483647 -2147483648)
+(test 1 #'*)
+(test 4 #'* 4)
+(test -5 #'* -5)
+(test 6 #'* 2 3)
+(test 2147483648 #'* 65536 32768)
+(test 2147418112 #'* 65536 32767)
+(test 134217728 #'* 65536 2048)
+(test -134217728 #'* 65536 -2048)
+(test 1/3 #'/ 3)
+(test -1/4 #'/ -4)
+(test 1/3 #'/ 10 30)
+(test -1/2 #'/ -5 10)
+(test -4 #'/ 20 -5)
+(test 431432412345/32 #'/ 431432412345 32)
+(test -2147483647/2147483648 #'/ 2147483647 -2147483648)
+(test -1 #'/ 2147483648 -2147483648)
+(test 2147483648 #'/ -2147483648 -1)
+(test -1/2147483648 #'/ 1 -2147483648)
+(test 1 #'min 2 3 4 1 5)
+(test 7 #'max 0 -2 7 6 3)
+(test -2147483648 #'min -2147483648 2147483647)
+(test 2147483647 #'max -2147483648 2147483647)
+(bool-test t #'< 1 2)
+(bool-test nil #'< 2 2)
+(bool-test nil #'< 4 3)
+(bool-test t #'< -2147483648 -1)
+(bool-test t #'< -2147483648 2147483648)
+(bool-test t #'<= 3 3)
+(bool-test nil #'<= 3 2)
+(bool-test t #'<= 3 7)
+(bool-test t #'<= -2147483648 2147483648)
+(bool-test t #'= 1 1)
+(bool-test nil #'= 1 -1)
+(bool-test t #'= -2147483648 -2147483648)
+(bool-test t #'>= 4 3)
+(bool-test t #'>= 5 5)
+(bool-test nil #'>= 4 9)
+(bool-test t #'>= 2147483647 -2147483648)
+(bool-test t #'> 7 5)
+(bool-test nil #'> 20 20)
+(bool-test nil #'> 19 31)
+(bool-test nil #'> 2147483647 2147483648)
+(bool-test nil #'> -2147483648 2147483647)
+(bool-test nil #'/= 2147483647 2147483647)
+(bool-test t #'/= 2147483647 -2147483648)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixnum bignum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 4123412341238575768576858308380 #'+
+ 431412 4123412341238575768576857876968)
+(test -653653534554686349560628211 #'-
+ 4231423 653653534554686349564859634)
+(test 17952112630025927929 #'* 4342423 4134123421423)
+(test 412341/766687896595678 #'/ 412341 766687896595678)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixnum flonum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 566594.4123d0 #'+ 43141 523453.4123d0)
+(test -2.106249523586876d9 #'+ -2147483647 41234123.413124d0)
+(test -6530250.653d0 #'- 4314 6534564.653d0)
+(test -358687.653d0 #'- -324123 34564.653d0)
+(test 3.26338916904d67 #'* 431234 756756d56)
+(test 5.731169192902366d-50 #'/ 3 5234534d43)
+(bool-test t #'< 423421 646454d0)
+(bool-test t #'= 43242113 43242113d0)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixnum fixratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 38654705646/17 #'+ 2147483647 2147483647/17)
+(test -2146748499/17 #'+ 43244 -2147483647/17)
+(test 17633127/4232 #'- 4321 653345/4232)
+(test 28227714415090/4323 #'* 4312442 6545645/4323)
+(test 639030/1441 #'* 42 15215/1441)
+(test 924444112/547 #'/ 3432342 1641/808)
+(bool-test t #'> 41342 42423/32)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixnum bigratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 134681902103055335/31231131234 #'+ 4312423 53453535353/31231131234)
+(test 134681795195984629/31231131234 #'- 4312423 53453535353/31231131234)
+(test 230514255287590319/31231131234 #'* 4312423 53453535353/31231131234)
+(test 134681848649519982/53453535353 #'/ 4312423 53453535353/31231131234)
+(bool-test t #'> 4312423 53453535353/31231131234)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bignum fixnum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 4123412341234124068 #'+ 4123412341234123412 656)
+(test 2147483647 #'+ 2147483648 -1)
+(test 2147483648 #'- 2147483647 -1)
+(test 3245393337480 #'* 4242344232 765)
+(test 1414114744/255 #'/ 4242344232 765)
+(bool-test nil #'< 2147483648 1)
+(bool-test t #'> 2147483648 -2147483648)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bignum flonum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 5.452523543454353d15 #'+ 5452523543454353 423d-6)
+(test -3.41423d205 #'- 54235423452345424443423 341423d200)
+(test 2.7061221650759596d89 #'* 413423412341231232 6.545643242d71)
+(test 9.744908405310087d-29 #'/ 41341234214 4242342d32)
+(bool-test t #'< 4314123412312341234123 4234242d46)
+(bool-test nil #'> 42342342142142421412341242 423423.432423d51)
+(bool-test t #'= 100000000000000000000 1d20)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bignum fixratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 3027180466416641662/7 #'+ 432454352345234523 1/7)
+(test 4294967295/2 #'- 2147483648 1/2)
+(test 14113747078041141/152263 #'* 42341241234123423 1/456789)
+(test 475355357536664/19 #'* 43214123412424 11/19)
+(test 143960192608 #'/ 4234123312 1/34)
+(test 15032385536/5 #'/ 2147483648 5/7)
+(bool-test nil #'< 4123412341234123 423424/23)
+(bool-test nil #'= 2147483648 1/3)
+(bool-test t #'> 2147483648 1/3)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bignum bigratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test -493153721444554600746963362777609/11404707804137
+ #'+ -43241241241241234234 18178448448449/11404707804137)
+(test 22573725350444837506376255369215081106984960/431241324242143434377
+ #'- 52345923457394857234895 455/431241324242143434377)
+(test 355905909219316970540364021939287762325439304380984344811607132990/14374707710807
+ #'* 45523452345234790345923405723902389345782390 23454234524234523623623/43124123132421)
+(test -853356237922877963618542794532291751029677352/21566206170617061706171
+ #'/ 4131234123412342 -43132412341234123412342/413124123412312234123412312312)
+(bool-test nil #'< 9482384762389461234892 463124869123897/43124123456678)
+(bool-test t #'/= 4689123469123846123843 4123894623894612/211)
+(bool-test t #'> 90437849234701234891203 4234123423/37)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; flonum fixnum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 4.3291328479d86 #'+ 43291328479d76 431243)
+(test 4.123123123432d58 #'- 4123123123432d46 2147483647)
+(test 4.1974800714094d109 #'* 970874791d96 43234)
+(test -1.0004838618250252d55 #'/ -432423.432d56 4322143)
+(bool-test nil #'< 4324932.342d5 4321421)
+(bool-test t #'> 2147483648d0 2147483647)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; flonum bignum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 4.3124325345d62 #'+ 4312432.5345d56 431241234901234791023479023)
+(test 4.123123443242d39 #'- 41231234.43242d32 -10947390284720389)
+(test 9.81681448753991d48 #'* 42342.89d27 231840917980324712)
+(test 6.837110051466236d49 #'/ -64832d57 -948236894126)
+(bool-test nil #'< 7589079203d56 43214124124312)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; flonum flonum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 4.12685643412d7 #'+ 34442.3412d0 41234122d0)
+(test -4.23432d84 #'- -45523453d56 423432d79)
+(test 2.0000000000000004d0 #'* 1.4142135623730951d0 1.4142135623730951d0)
+(test -1.414213562373095d0 #'/ -2d0 1.4142135623730951d0)
+(test 0.7071067811865476d0 #'/ 1.4142135623730951d0 2d0)
+(bool-test nil #'< 43124123d56 4231412d43)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; flonum fixratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 3.41412d61 #'+ 341412d56 3/652)
+(test 4.312443d72 #'- 43124.43d68 42421/5678)
+(test -4.32112300201218d73 #'* 4321123d67 -2147483648/2147483647)
+(test 3.388443859138533d58 #'/ 432412d54 13744/1077)
+(bool-test t #'> 423194237d43 4231412/23)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; flonum bigratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 4.378904431d62 #'+ 4378904.431d56 49230471923047129/32412341234126)
+(test 0d0 #'- 1.7320508075688772d0 3900231685776981/2251799813685248)
+(test 5.000000000000001d0 #'* 2.23606797749979d0 629397181890197/281474976710656)
+(test 7.000000000000001d0 #'/ 2.6457513110645907d0 1125899906842624/2978851154656373)
+(bool-test nil #'< 790412390412d45 1005712007432/10518078881)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixratio fixnum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 23502480199/57 #'+ 1/57 412324214)
+(test -1608505/39 #'- 11/39 41244)
+(test 241844976595/3121 #'* 45245/3121 5345231)
+(test 4231/30211050 #'/ 4231/67890 445)
+(bool-test nil #'< 43123/12 -3432)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixratio bignum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 290071443963580821646/4115 #'+ -14119/4115 70491237901234711)
+(test 92654360215843653827434431256/1237 #'- 423412/1237 -74902473901247901234789012)
+(test 139081825032265225396/111 #'* 13/777 74890213478912044444)
+(test -22/19000187487170108051697772680759 #'/ -176/31 4903274190237447239147812304712)
+(bool-test t #'< 7094123/312 423412429047)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixratio flonum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 3756.777956289953d0 #'+ 41290/11 3.141592653589793d0)
+(test 3750.494770982774d0 #'- 41290/11 3.141592653589793d0)
+(test 11792.396424247505d0 #'* 41290/11 3.141592653589793d0)
+(test 1194.8195636844289d0 #'/ 41290/11 3.141592653589793d0)
+(bool-test nil #'< 41290/11 3.141592653589793d0)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixratio fixratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test -2/2147483647 #'+ 2147483646/2147483647 -2147483648/2147483647)
+(test 4611686015206162432/2305843005992468481 #'+ 2147483648/2147483646 2147483648/2147483647)
+(test 114/91 #'+ 5/7 7/13)
+(test 2 #'- 2147483646/2147483647 -2147483648/2147483647)
+(test -6442450939/4611686009837453315 #'- 2147483646/2147483647 2147483647/2147483645)
+(test 214/231 #'- 5/7 -7/33)
+(test 183092240452/408559 #'* '432421/3217 423412/127)
+(test 1057751/7345 #'* 34121/65 31/113)
+(test -93866791/102381559 #'/ 143747/107 -956837/653)
+(test 117/517 #'/ 13/33 47/27)
+(bool-test nil #'< 5/3 7/9)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; fixratio bigratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 1211321073398067249731082729214954013/1099249926163926018396018404101914
+ #'+ 23141/21 572903572390457239/52345234579234572304572304957234)
+(test -1210401943424090457832980748892408320175/1099249926163926018396018404101914
+ #'+ -23123441/21 572903572390457239/52345234579234572304572304957234)
+(test -130565585970579643613431728982140/1297324236427391
+ #'- 6/83 1573079349043128237436315709694/15630412487077)
+(test 119377824848653/98027 #'* 4123/61 28954117111/1607)
+(test -533081148/1126543487854337661125 #'/ 4132412/125 -9012347902834701289/129)
+(bool-test nil #'< 4132412/125 -9012347902834701289/129)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bigratio fixnum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 48668779872364438/8438103123 #'+ 49032749012471920/8438103123 -43134)
+(test 49396718152579402/8438103123 #'- 49032749012471920/8438103123 -43134)
+(test -704992865301321265760/2812701041 #'* 49032749012471920/8438103123 -43134)
+(test -24516374506235960/181984570053741 #'/ 49032749012471920/8438103123 -43134)
+(bool-test t #'> 49032749012471920/8438103123 -43134)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bigratio bignum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 22765322736543569109219273030163417097453878379283263605274270/46382946123894712341
+ #'+ 4692318468912374612389461278/46382946123894712341 490812348912346238794612389461238961238912)
+(test -22765322736543569109219273030163417097453878379283263605274270/46382946123894712341
+ #'- -4692318468912374612389461278/46382946123894712341 490812348912346238794612389461238961238912)
+(test -2303047849571666696101160700266058250647016644840659232609643130849536/46382946123894712341
+ #'* 4692318468912374612389461278/46382946123894712341 -490812348912346238794612389461238961238912)
+(test 2346159234456187306194730639/11382661368271784554609636515081706202567704733454325607906496
+ #'/ -4692318468912374612389461278/46382946123894712341 -490812348912346238794612389461238961238912)
+(bool-test t #'< 4692318468912374612389461278/46382946123894712341 490812348912346238794612389461238961238912)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bigratio flonum
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 63.2771680782255d0 #'+ 31.63858403911275d0 4452734852783697/140737488355328)
+(test 0d0 #'+ -31.63858403911275d0 4452734852783697/140737488355328)
+(test -1001.0000000000001d0 #'* -31.63858403911275d0 4452734852783697/140737488355328)
+(test 1d0 #'/ -31.63858403911275d0 -4452734852783697/140737488355328)
+(bool-test nil #'< -31.63858403911275d0 -4452734852783697/140737488355328)
+(bool-test nil #'> -31.63858403911275d0 -4452734852783697/140737488355328)
+(bool-test nil #'/= -31.63858403911275d0 -4452734852783697/140737488355328)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bigratio fixratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 0 #'+ 2147483648/2147483647 -2147483648/2147483647)
+(test 3230093924913437/413416372043776 #'+ 45705840067699/8796093022208 123/47)
+(test 4294967296/2147483647 #'- 2147483648/2147483647 -2147483648/2147483647)
+(test 1066255041450269/413416372043776 #'- 45705840067699/8796093022208 123/47)
+(test -5621818328326977/413416372043776 #'* -45705840067699/8796093022208 123/47)
+(test -2148174483181853/1081919441731584 #'/ 45705840067699/8796093022208 -123/47)
+(bool-test t #'> 45705840067699/8796093022208 123/47)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bigratio bigratio
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 2679495973598190955776211861634126560767052764822779809414184089582/140710542183009389719255843429922029722593
+ #'+ 649812364891236481923461238946128/34124123 -7489023423142/4123491823746192384761238946123891)
+(test 2679495973598190955776211861634126560767052765333892522296541398514/140710542183009389719255843429922029722593
+ #'- 649812364891236481923461238946128/34124123 -7489023423142/4123491823746192384761238946123891)
+(test -4866460021317766216371472892133283923086494176/140710542183009389719255843429922029722593
+ #'* 649812364891236481923461238946128/34124123 -7489023423142/4123491823746192384761238946123891)
+(test -1339747986799095477888105930817063280383526382539168082927681372024/127778178220589327233
+ #'/ 649812364891236481923461238946128/34124123 -7489023423142/4123491823746192384761238946123891)
+(bool-test t #'> 649812364891236481923461238946128/34124123 -7489023423142/4123491823746192384761238946123891)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; complex real
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test #c(2147483648 -1) #'+ #c(1 -1) 2147483647)
+(test #c(2.147483648d9 -1) #'+ #c(2147483647 -1) 1d0)
+(test #c(129642370237029633787/3 0.25d0) #'- #c(-11/3 0.25d0) -43214123412343211266)
+(test #c(23470/21 4.333333333333334d0) #'* #c(2347/7 1.3d0) 10/3)
+(test #c(134217728/11 67108864/11) #'* #c(65536 32768) 2048/11)
+(test #c(1.3133333333333332d0 82304) #'/ #c(1.97d0 123456) 3/2)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; real complex
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test #c(80/7 7/13) #'+ 3/7 #c(11 7/13))
+(test #c(1.2345d47 -1) #'+ 12345d43 #c(-2147483648 -1))
+(test #c(-2147483649 2147483647) #'+ -2147483648 #c(-1 2147483647))
+(test #c(41/15 1.23456d68) #'- #c(7/5 1234.56d65) -4/3)
+(test #c(-41/19 2147483648) #'* #c(41/19 -2147483648) -1)
+(test #c(-88046829568/40802189293 2.147483649d41) #'* #c(41/19 -2147483648d32) -2147483648/2147483647)
+(test #c(-5.0691244239631335d0 1.3911008563333336d16)
+ #'/ #c(-11/7 4312412654633334) 0.31d0)
+(bool-test t #'= #c(1 0.0) 1)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; complex complex
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test #c(-16.0d0 -4.0d0) #'+ #c(-16.0d0 -4.0d0))
+(test #c(0d0 1d0) #'- #c(0d0 -1d0))
+(test #c(1d0 3d0) #'- #c(-1d0 -3d0))
+(test #c(-16.0d0 -4.0d0) #'* #c(-16.0d0 -4.0d0))
+(test #c(-0.058823529411764705d0 0.014705882352941176d0) #'/ #c(-16d0 -4d0))
+(test #c(1.94d0 301868863889/7) #'+ #c(3/5 5/7) #c(1.34d0 43124123412))
+(test #c(8641975242/7 -3.4596d0) #'- #c(1234567890 0.0004d0) #c(-12/7 3.46d0))
+(test #c(2944.315858312371d0 5.59002d13) #'* #c(-11/7 -1234d9) #c(-45.3d0 5/2147483647))
+(test #c(1.9635384272224412d-8 -0.33333333317811176d0)
+ #'/ #c(2147483647/3 -0.5d0) #c(128 2147483648.0d0))
+(test #c(8.154945137073864d11 2.621232365490813d12)
+ #'/ #c(-1.3d0 4312412654633) #c(3/2 7/15))
+(test #c(0.003674737027278924d0 -257.6948748113586d0)
+ #'/ #c(1.5d0 -432412) #c(1678 -567/31313))
+(bool-test t #'= #c(1 2d0) #c(1 2))
+(bool-test nil #'/= #c(1 2) #c(1d0 2d0))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; abs
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 2147483648 #'abs -2147483648)
+(test 2147483647 #'abs -2147483647)
+(test 2147483647 #'abs 2147483647)
+(test 1 #'abs 1)
+(test 5/7 #'abs -5/7)
+(test 2147483648/2147483647 #'abs -2147483648/2147483647)
+(test 3.12d0 #'abs -3.12d0)
+(test 4312412341234124124123412 #'abs 4312412341234124124123412)
+(test 4312412341234124124123412 #'abs -4312412341234124124123412)
+(test 1.0 #'abs #c(1 0.0))
+(test 11.40175425099138d0 #'abs #c(-11 3d0))
+(test 4.47213595499958d0 #'abs #c(-4 -2))
+(test 1.0 #'abs #c(0.0 -1.0))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; sqrt
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 3.4641016151377544d0 #'sqrt 12)
+(test #c(0 12) #'sqrt -144)
+(test 6.429728792199102d18 #'sqrt 41341412341234123412490123470912347210)
+(test 41341412341234123412490123470912347210
+ #'sqrt 1709112374367945085349927261774254951456404621200206927501652414831594784100)
+(test 46340.95001184158d0 #'sqrt 2147483648)
+(test 0.7071067811865476d0 #'sqrt 0.5d0)
+(test 0 #'sqrt 0)
+(test 0d0 #'sqrt 0d0)
+(test 111.1106106544285d0 #'sqrt 12345.5678d0)
+(test #c(0 11.119982014373944d0) #'sqrt -123.654d0)
+(test 3/8 #'sqrt 9/64)
+(test #c(0 1.1832159566199232d0) #'sqrt -7/5)
+(test 514.7536007118473d0 #'sqrt 821974900428408092/3102128401119)
+(test 413412341293461238946192384612893/314212341412341246128361289
+ #'sqrt 170909763933741276657131032282211169869649489782500833989461829449/98729395495825697643724477479624921705328808513741521)
+;; check for overflow
+(error-test #'sqrt 402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; mod
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 5 #'mod 5 9)
+(test 4 #'mod -5 9)
+(test -4 #'mod 5 -9)
+(test -5 #'mod -5 -9)
+(test 2147483646 #'mod -2147483648 2147483647)
+(test -1 #'mod -2147483648 -2147483647)
+(test 1 #'mod 2147483648 2147483647)
+(test 0 #'mod -170909763933741276657131032282211169869649489782500833989461829449 413412341293461238946192384612893)
+(test -1709112374367945085349927261774254951415063208858972804089162291360682436890
+ #'mod 41341412341234123412490123470912347210 -1709112374367945085349927261774254951456404621200206927501652414831594784100)
+(test 9.666666666666666d0 #'mod -1/3 10d0)
+(test -9.666666666666666d0 #'mod 1/3 -10d0)
+(test -0.3333333333333333d0 #'mod -1/3 -10d0)
+(test 0.3333333333333333d0 #'mod 1/3 10d0)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; rem
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 2 #'rem 11 3)
+(test 2 #'rem 11 -3)
+(test -2 #'rem -11 3)
+(test -2 #'rem -11 -3)
+(test -1 #'rem -2147483648 2147483647)
+(test 0.1499999999999999d0 #'rem 1.35d0 1/5)
+(test -0.1499999999999999d0 #'rem -1.35d0 1/5)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; gcd
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 11 #'gcd 33 11)
+(test 7 #'gcd 91 -49)
+(test 4 #'gcd -4)
+(test 0 #'gcd)
+(test 11 #'gcd 3333 -33 1002001)
+(test 1 #'gcd -2147483648 2147483647)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; lcm
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 1 #'lcm)
+(test 10 #'lcm 10)
+(test 5 #'lcm -5)
+(test 4611686016279904256 #'lcm -2147483648 2147483647)
+(test 0 #'lcm 0 5)
+(test 60 #'lcm 1 2 3 4 5 6)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; and
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test -1 #'logand)
+(test 0 #'logand 1 2)
+(test -2147483648 #'logand -2147483648 -1)
+(test 2147483647 #'logand 2147483647 -1)
+(test 2147479552 #'logand 8796093018112 2147483647)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; eqv
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test -1 #'logeqv)
+(test -4 #'logeqv 1 2)
+(test -2147483648 #'logeqv -2147483648 -1)
+(test 2147483647 #'logeqv 2147483647 -1)
+(test -8793945542656 #'logeqv 8796093018112 2147483647)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; or
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 0 #'logior)
+(test 3 #'logior 1 2)
+(test -1 #'logior -2147483648 -1)
+(test -1 #'logior 2147483647 -1)
+(test 8796093022207 #'logior 8796093018112 2147483647)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; xor
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test 0 #'logxor)
+(test 3 #'logxor 1 2)
+(test 2147483647 #'logxor -2147483648 -1)
+(test -2147483648 #'logxor 2147483647 -1)
+(test 8793945542655 #'logxor 8796093018112 2147483647)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; not
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test -1 #'lognot 0)
+(test 0 #'lognot -1)
+(test -2 #'lognot 1)
+(test 1 #'lognot -2)
+(test -3 #'lognot 2)
+(test 2 #'lognot -3)
+(test -2147483648 #'lognot 2147483647)
+(test 2147483647 #'lognot -2147483648)
+(test -8793945542656 #'lognot 8793945542655)
+(test -8796093018113 #'lognot 8796093018112)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; floor
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(div-test 1 1/2 #'floor 3/2)
+(div-test 1d0 1 #'ffloor 3 2)
+(div-test -2 2147483646 #'floor -2147483648 2147483647)
+(div-test 2147483648 0 #'floor -2147483648 -1)
+(div-test 17179869184 0 #'floor 18446744073709551616 1073741824)
+(div-test -17179869201 -1073741807 #'floor 18446744073709551616 -1073741823)
+(div-test 2147483648 0d0 #'floor -2147483648 -1d0)
+(div-test -2 2147483646/2147483647 #'floor -2147483648/2147483647)
+(div-test 32768 32768/2147483647 #'floor 2147483648/2147483647 65535/2147483647)
+(div-test -32769 -32767/2147483647 #'floor 2147483648/2147483647 -65535/2147483647)
+(div-test -32769 32767/2147483647 #'floor -2147483648/2147483647 65535/2147483647)
+(div-test 32768 -32768/2147483647 #'floor -2147483648/2147483647 -65535/2147483647)
+(div-test 2 0.5d0 #'floor 3d0 1.25d0)
+(div-test 2 1d0 #'floor 4d0 1.5d0)
+(div-test -3 -0.5d0 #'floor 4d0 -1.5d0)
+(div-test -3 0.5d0 #'floor -4d0 1.5d0)
+(div-test 2 -1d0 #'floor -4d0 -1.5d0)
+(div-test 1 2/91 #'floor 5/7 9/13)
+(div-test -2 -61/91 #'floor 5/7 -9/13)
+(div-test -2 61/91 #'floor -5/7 9/13)
+(div-test 1 -2/91 #'floor -5/7 -9/13)
+(div-test 1 0 #'floor 2147483648/2147483647 2147483648/2147483647)
+(div-test -1 0 #'floor 2147483648/2147483647 -2147483648/2147483647)
+(div-test -1 0 #'floor -2147483648/2147483647 2147483648/2147483647)
+(div-test 1 0 #'floor -2147483648/2147483647 -2147483648/2147483647)
+(div-test 9437 1416337955817765/144137437447079
+ #'floor 16324116304212832041/144137437447079 12)
+(div-test -9438 -313311293547183/144137437447079
+ #'floor 16324116304212832041/144137437447079 -12)
+(div-test -9438 313311293547183/144137437447079
+ #'floor -16324116304212832041/144137437447079 12)
+(div-test 9437 -1416337955817765/144137437447079
+ #'floor -16324116304212832041/144137437447079 -12)
+(div-test 8081 1138147903718848755797/4324123123412370
+ #'floor 2147483648 1148972348912638496123/4324123123412370)
+(div-test -8082 -1804074198964956721/720687187235395
+ #'floor 2147483648 -1148972348912638496123/4324123123412370)
+(div-test -8082 1804074198964956721/720687187235395
+ #'floor -2147483648 1148972348912638496123/4324123123412370)
+(div-test 8081 -1138147903718848755797/4324123123412370
+ #'floor -2147483648 -1148972348912638496123/4324123123412370)
+(div-test 0 1148972348912638496123/4324123123412370111
+ #'floor 1148972348912638496123/4324123123412370111 2147483648)
+(div-test -1 -9285982550494401861657948805/4324123123412370111
+ #'floor 1148972348912638496123/4324123123412370111 -2147483648)
+(div-test -1 9285982550494401861657948805/4324123123412370111
+ #'floor -1148972348912638496123/4324123123412370111 2147483648)
+(div-test 0 -1148972348912638496123/4324123123412370111
+ #'floor -1148972348912638496123/4324123123412370111 -2147483648)
+(div-test 0.0d0 1.0000000004656613d0 #'ffloor 2147483648/2147483647 2147483648d0)
+(div-test -1.0d0 -2.147483647d9 #'ffloor 2147483648/2147483647 -2147483648d0)
+(div-test -1.0d0 2.147483647d9 #'ffloor -2147483648/2147483647 2147483648d0)
+(div-test 0.0d0 -1.0000000004656613d0 #'ffloor -2147483648/2147483647 -2147483648d0)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; ceiling
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(div-test 2 -1/2 #'ceiling 3/2)
+(div-test 2d0 -1 #'fceiling 3 2)
+(div-test -1 -1 #'ceiling -2147483648 2147483647)
+(div-test 2147483648 0 #'ceiling -2147483648 -1)
+(div-test 17179869184 0 #'ceiling 18446744073709551616 1073741824)
+(div-test -17179869200 16 #'ceiling 18446744073709551616 -1073741823)
+(div-test 2147483648 0d0 #'ceiling -2147483648 -1d0)
+(div-test -1 -1/2147483647 #'ceiling -2147483648/2147483647)
+(div-test 32769 -32767/2147483647 #'ceiling 2147483648/2147483647 65535/2147483647)
+(div-test -32768 32768/2147483647 #'ceiling 2147483648/2147483647 -65535/2147483647)
+(div-test -32768 -32768/2147483647 #'ceiling -2147483648/2147483647 65535/2147483647)
+(div-test 32769 32767/2147483647 #'ceiling -2147483648/2147483647 -65535/2147483647)
+(div-test 3 -0.75d0 #'ceiling 3d0 1.25d0)
+(div-test 3 -0.5d0 #'ceiling 4d0 1.5d0)
+(div-test -2 1d0 #'ceiling 4d0 -1.5d0)
+(div-test -2 -1d0 #'ceiling -4d0 1.5d0)
+(div-test 3 0.5d0 #'ceiling -4d0 -1.5d0)
+(div-test 2 -61/91 #'ceiling 5/7 9/13)
+(div-test -1 2/91 #'ceiling 5/7 -9/13)
+(div-test -1 -2/91 #'ceiling -5/7 9/13)
+(div-test 2 61/91 #'ceiling -5/7 -9/13)
+(div-test 1 0 #'ceiling 2147483648/2147483647 2147483648/2147483647)
+(div-test -1 0 #'ceiling 2147483648/2147483647 -2147483648/2147483647)
+(div-test -1 0 #'ceiling -2147483648/2147483647 2147483648/2147483647)
+(div-test 1 0 #'ceiling -2147483648/2147483647 -2147483648/2147483647)
+(div-test 9438 -313311293547183/144137437447079
+ #'ceiling 16324116304212832041/144137437447079 12)
+(div-test -9437 1416337955817765/144137437447079
+ #'ceiling 16324116304212832041/144137437447079 -12)
+(div-test -9437 -1416337955817765/144137437447079
+ #'ceiling -16324116304212832041/144137437447079 12)
+(div-test 9438 313311293547183/144137437447079
+ #'ceiling -16324116304212832041/144137437447079 -12)
+(div-test 8082 -1804074198964956721/720687187235395
+ #'ceiling 2147483648 1148972348912638496123/4324123123412370)
+(div-test -8081 1138147903718848755797/4324123123412370
+ #'ceiling 2147483648 -1148972348912638496123/4324123123412370)
+(div-test -8081 -1138147903718848755797/4324123123412370
+ #'ceiling -2147483648 1148972348912638496123/4324123123412370)
+(div-test 8082 1804074198964956721/720687187235395
+ #'ceiling -2147483648 -1148972348912638496123/4324123123412370)
+(div-test 1 -9285982550494401861657948805/4324123123412370111
+ #'ceiling 1148972348912638496123/4324123123412370111 2147483648)
+(div-test 0 1148972348912638496123/4324123123412370111
+ #'ceiling 1148972348912638496123/4324123123412370111 -2147483648)
+(div-test 0 -1148972348912638496123/4324123123412370111
+ #'ceiling -1148972348912638496123/4324123123412370111 2147483648)
+(div-test 1 9285982550494401861657948805/4324123123412370111
+ #'ceiling -1148972348912638496123/4324123123412370111 -2147483648)
+(div-test 1.0d0 -2.147483647d9 #'fceiling 2147483648/2147483647 2147483648d0)
+(div-test 0d0 1.0000000004656613d0 #'fceiling 2147483648/2147483647 -2147483648d0)
+(div-test 0d0 -1.0000000004656613d0 #'fceiling -2147483648/2147483647 2147483648d0)
+(div-test 1d0 2.147483647d9 #'fceiling -2147483648/2147483647 -2147483648d0)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; truncate
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(div-test 1 1/2 #'truncate 3/2)
+(div-test 1d0 1 #'ftruncate 3 2)
+(div-test -1 -1 #'truncate -2147483648 2147483647)
+(div-test 2147483648 0 #'truncate -2147483648 -1)
+(div-test 17179869184 0 #'truncate 18446744073709551616 1073741824)
+(div-test -17179869200 16 #'truncate 18446744073709551616 -1073741823)
+(div-test 2147483648 0d0 #'truncate -2147483648 -1d0)
+(div-test -1 -1/2147483647 #'truncate -2147483648/2147483647)
+(div-test 32768 32768/2147483647 #'truncate 2147483648/2147483647 65535/2147483647)
+(div-test -32768 32768/2147483647 #'truncate 2147483648/2147483647 -65535/2147483647)
+(div-test -32768 -32768/2147483647 #'truncate -2147483648/2147483647 65535/2147483647)
+(div-test 32768 -32768/2147483647 #'truncate -2147483648/2147483647 -65535/2147483647)
+(div-test 2 0.5d0 #'truncate 3d0 1.25d0)
+(div-test 2 1d0 #'truncate 4d0 1.5d0)
+(div-test -2 1d0 #'truncate 4d0 -1.5d0)
+(div-test -2 -1d0 #'truncate -4d0 1.5d0)
+(div-test 2 -1d0 #'truncate -4d0 -1.5d0)
+(div-test 1 2/91 #'truncate 5/7 9/13)
+(div-test -1 2/91 #'truncate 5/7 -9/13)
+(div-test -1 -2/91 #'truncate -5/7 9/13)
+(div-test 1 -2/91 #'truncate -5/7 -9/13)
+(div-test 1 0 #'truncate 2147483648/2147483647 2147483648/2147483647)
+(div-test -1 0 #'truncate 2147483648/2147483647 -2147483648/2147483647)
+(div-test -1 0 #'truncate -2147483648/2147483647 2147483648/2147483647)
+(div-test 1 0 #'truncate -2147483648/2147483647 -2147483648/2147483647)
+(div-test 9437 1416337955817765/144137437447079
+ #'truncate 16324116304212832041/144137437447079 12)
+(div-test -9437 1416337955817765/144137437447079
+ #'truncate 16324116304212832041/144137437447079 -12)
+(div-test -9437 -1416337955817765/144137437447079
+ #'truncate -16324116304212832041/144137437447079 12)
+(div-test 9437 -1416337955817765/144137437447079
+ #'truncate -16324116304212832041/144137437447079 -12)
+(div-test 8081 1138147903718848755797/4324123123412370
+ #'truncate 2147483648 1148972348912638496123/4324123123412370)
+(div-test -8081 1138147903718848755797/4324123123412370
+ #'truncate 2147483648 -1148972348912638496123/4324123123412370)
+(div-test -8081 -1138147903718848755797/4324123123412370
+ #'truncate -2147483648 1148972348912638496123/4324123123412370)
+(div-test 8081 -1138147903718848755797/4324123123412370
+ #'truncate -2147483648 -1148972348912638496123/4324123123412370)
+(div-test 0 1148972348912638496123/4324123123412370111
+ #'truncate 1148972348912638496123/4324123123412370111 2147483648)
+(div-test 0 1148972348912638496123/4324123123412370111
+ #'truncate 1148972348912638496123/4324123123412370111 -2147483648)
+(div-test 0 -1148972348912638496123/4324123123412370111
+ #'truncate -1148972348912638496123/4324123123412370111 2147483648)
+(div-test 0 -1148972348912638496123/4324123123412370111
+ #'truncate -1148972348912638496123/4324123123412370111 -2147483648)
+(div-test 0d0 1.0000000004656613d0 #'ftruncate 2147483648/2147483647 2147483648d0)
+(div-test 0d0 1.0000000004656613d0 #'ftruncate 2147483648/2147483647 -2147483648d0)
+(div-test 0d0 -1.0000000004656613d0 #'ftruncate -2147483648/2147483647 2147483648d0)
+(div-test 0d0 -1.0000000004656613d0 #'ftruncate -2147483648/2147483647 -2147483648d0)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; round
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(div-test 2 -1/2 #'round 3/2)
+(div-test 2d0 -1 #'fround 3 2)
+(div-test -1 -1 #'round -2147483648 2147483647)
+(div-test 2147483648 0 #'round -2147483648 -1)
+(div-test 17179869184 0 #'round 18446744073709551616 1073741824)
+(div-test -17179869200 16 #'round 18446744073709551616 -1073741823)
+(div-test 2147483648 0d0 #'round -2147483648 -1d0)
+(div-test -1 -1/2147483647 #'round -2147483648/2147483647)
+(div-test 32769 -32767/2147483647 #'round 2147483648/2147483647 65535/2147483647)
+(div-test -32769 -32767/2147483647 #'round 2147483648/2147483647 -65535/2147483647)
+(div-test -32769 32767/2147483647 #'round -2147483648/2147483647 65535/2147483647)
+(div-test 32769 32767/2147483647 #'round -2147483648/2147483647 -65535/2147483647)
+(div-test 2 0.5d0 #'round 3d0 1.25d0)
+(div-test 3 -0.5d0 #'round 4d0 1.5d0)
+(div-test -3 -0.5d0 #'round 4d0 -1.5d0)
+(div-test -3 0.5d0 #'round -4d0 1.5d0)
+(div-test 3 0.5d0 #'round -4d0 -1.5d0)
+(div-test 1 2/91 #'round 5/7 9/13)
+(div-test -1 2/91 #'round 5/7 -9/13)
+(div-test -1 -2/91 #'round -5/7 9/13)
+(div-test 1 -2/91 #'round -5/7 -9/13)
+(div-test 1 0 #'round 2147483648/2147483647 2147483648/2147483647)
+(div-test -1 0 #'round 2147483648/2147483647 -2147483648/2147483647)
+(div-test -1 0 #'round -2147483648/2147483647 2147483648/2147483647)
+(div-test 1 0 #'round -2147483648/2147483647 -2147483648/2147483647)
+(div-test 9438 -313311293547183/144137437447079
+ #'round 16324116304212832041/144137437447079 12)
+(div-test -9438 -313311293547183/144137437447079
+ #'round 16324116304212832041/144137437447079 -12)
+(div-test -9438 313311293547183/144137437447079
+ #'round -16324116304212832041/144137437447079 12)
+(div-test 9438 313311293547183/144137437447079
+ #'round -16324116304212832041/144137437447079 -12)
+(div-test 8082 -1804074198964956721/720687187235395
+ #'round 2147483648 1148972348912638496123/4324123123412370)
+(div-test -8082 -1804074198964956721/720687187235395
+ #'round 2147483648 -1148972348912638496123/4324123123412370)
+(div-test -8082 1804074198964956721/720687187235395
+ #'round -2147483648 1148972348912638496123/4324123123412370)
+(div-test 8082 1804074198964956721/720687187235395
+ #'round -2147483648 -1148972348912638496123/4324123123412370)
+(div-test 0 1148972348912638496123/4324123123412370111
+ #'round 1148972348912638496123/4324123123412370111 2147483648)
+(div-test 0 1148972348912638496123/4324123123412370111
+ #'round 1148972348912638496123/4324123123412370111 -2147483648)
+(div-test 0 -1148972348912638496123/4324123123412370111
+ #'round -1148972348912638496123/4324123123412370111 2147483648)
+(div-test 0 -1148972348912638496123/4324123123412370111
+ #'round -1148972348912638496123/4324123123412370111 -2147483648)
+(div-test 0d0 1.0000000004656613d0 #'fround 2147483648/2147483647 2147483648d0)
+(div-test 0d0 1.0000000004656613d0 #'fround 2147483648/2147483647 -2147483648d0)
+(div-test 0d0 -1.0000000004656613d0 #'fround -2147483648/2147483647 2147483648d0)
+(div-test 0d0 -1.0000000004656613d0 #'fround -2147483648/2147483647 -2147483648d0)
+(div-test 2 0.5d0 #'round 2.5d0)
+(div-test -2 -0.5d0 #'round -2.5d0)
+(div-test 5 0d0 #'round 2.5d0 0.5d0)
+(div-test -5 0d0 #'round 2.5d0 -0.5d0)
+(div-test -5 0d0 #'round 2.5d0 -0.5d0)
+(div-test -5 0d0 #'round -2.5d0 0.5d0)
+(div-test 5 0d0 #'round -2.5d0 -0.5d0)
+(div-test 1 -2/7 #'round 5/7)
+(div-test -1 2/7 #'round -5/7)
+(div-test 2 -1/2 #'round 3/2)
+(div-test -2 1/2 #'round -3/2)
+(div-test 2 1 #'round 30/2 7)
+(div-test -2 1 #'round 30/2 -7)
+(div-test -2 -1 #'round -30/2 7)
+(div-test 2 -1 #'round -30/2 -7)
+(div-test 1073741824 -1/2 #'round 2147483647/2)
+(div-test -1073741824 1/2 #'round -2147483647/2)
+(div-test 1 -2147483645/2 #'round 2147483647/2 2147483646)
+(div-test -1 -2147483645/2 #'round 2147483647/2 -2147483646)
+(div-test -1 2147483645/2 #'round -2147483647/2 2147483646)
+(div-test 1 -2147483645/2 #'round 2147483647/2 2147483646)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; misc
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(test #c(5 -5) #'conjugate #c(5 5))
+(test #c(5 5) #'conjugate #c(5 -5))
+(test #c(-5 -5) #'conjugate #c(-5 5))
+(test #c(-5 5) #'conjugate #c(-5 -5))
+
+(test 1 #'denominator 10)
+(test 3 #'denominator 10/3)
+(test 3 #'denominator 1804074198964956721/3)
+(test 4324123123412370111 #'denominator -1148972348912638496123/4324123123412370111)
+
+(bool-test nil #'evenp -1)
+(bool-test t #'evenp -2147483648)
+(bool-test t #'evenp -4294967296)
+(bool-test nil #'evenp -4294967295)
+
+(test 0.5d0 #'float 1/2)
+(test 10.0d0 #'float 10)
+(test 4.978341823462786d22 #'float 49783418234627861238926)
+(test 1.845867531346429d12 #'float 643827946123846123984/348794231)
+
+(bool-test t #'floatp 0.3d0)
+(bool-test nil #'floatp 1/3)
+
+(test 0 #'imagpart 1)
+(test -5 #'imagpart #c(1 -5))
+
+(bool-test t #'integerp 12)
+(bool-test nil #'integerp 1/2)
+(bool-test nil #'integerp :test)
+(bool-test nil #'integerp 0d0)
+(bool-test t #'integerp 49783418234627861238926)
+
+(test 3 #'isqrt 12)
+(test 46340 #'isqrt 2147483648)
+(test 46340 #'isqrt 2147483647)
+(test 25373764918 #'isqrt 643827946123846123984)
+
+(bool-test nil #'logtest 1 2)
+(bool-test t #'logtest 1 3)
+(bool-test t #'logtest 7 -1)
+
+(bool-test nil #'minusp 0)
+(bool-test nil #'minusp 2147483648)
+(bool-test t #'minusp -2147483648)
+(bool-test t #'minusp -1/4)
+(bool-test nil #'minusp 0.2d0)
+(bool-test nil #'minusp 0d0)
+(bool-test nil #'minusp 984723891462817946123897416)
+(bool-test t #'minusp -1148972348912638496123/4324123123412370111)
+
+(bool-test t #'numberp #c(1 2))
+(bool-test t #'numberp -200)
+(bool-test nil #'numberp :test)
+
+(test 10 #'numerator 10)
+(test 10 #'numerator 10/3)
+(test 1804074198964956721 #'numerator 1804074198964956721/3)
+(test -1148972348912638496123 #'numerator -1148972348912638496123/4324123123412370111)
+
+(bool-test t #'oddp -1)
+(bool-test nil #'oddp -2147483648)
+(bool-test nil #'oddp -4294967296)
+(bool-test t #'oddp -4294967295)
+
+(bool-test nil #'plusp 0)
+(bool-test t #'plusp 2147483648)
+(bool-test nil #'plusp -2147483648)
+(bool-test nil #'plusp -1/4)
+(bool-test t #'plusp 0.2d0)
+(bool-test nil #'plusp 0d0)
+(bool-test t #'plusp 984723891462817946123897416)
+(bool-test nil #'plusp -1148972348912638496123/4324123123412370111)
+
+(test 1/4 #'rational 0.25d0)
+(test 5/2 #'rational 2.5d0)
+(test 1/8 #'rational 0.125d0)
+(test -5/8 #'rational -0.625d0)
+(test 524293/8 #'rational 65536.625d0)
+(test 17179869181/8 #'rational 2147483647.625d0)
+
+(bool-test t #'rationalp -3)
+(bool-test t #'rationalp 1/2)
+(bool-test t #'rationalp 1/2412341242424122412)
+(bool-test nil #'rationalp :test)
+(bool-test nil #'rationalp 0d0)
+(bool-test t #'rationalp 49783418234627861238926)
+
+(test -1 #'realpart #c(-1 0.5d0))
+
+(test 1 #'signum 123/5)
+(test 0d0 #'signum 0d0)
+(test -1d0 #'signum -7.3d0)
+
+(bool-test nil #'zerop 1)
+(bool-test nil #'zerop 1/4312412341234123412)
+(bool-test nil #'zerop 0.000003d0)
+(bool-test t #'zerop 0)
+(bool-test t #'zerop 0d0)
+(bool-test t #'zerop #c(0 0d0))
+
+(bool-test t #'= 10 #c(10 0d0))
+
diff --git a/lisp/test/psql-1.lsp b/lisp/test/psql-1.lsp
new file mode 100644
index 0000000..2410fd8
--- /dev/null
+++ b/lisp/test/psql-1.lsp
@@ -0,0 +1,80 @@
+;; Postgresql C library interface, example program 1, using the xedit
+;; lisp interface
+
+;; Test the C version of libpq, the PostgreSQL frontend library.
+(require "psql")
+
+(defun exit-nicely (conn)
+ (pq-finish conn)
+ (quit 1)
+)
+
+;; begin, by setting the parameters for a backend connection if the
+;; parameters are null, then the system will try to use reasonable
+;; defaults by looking up environment variables or, failing that,
+;; using hardwired constants
+(setq pghost nil) ; host name of the backend server
+(setq pgport nil) ; port of the backend server
+(setq pgoptions nil) ; special options to start up the backend server
+(setq pgtty nil) ; debugging tty for the backend server
+(setq pgdbname "template1")
+
+;; make a connection to the database
+(setq conn (pq-setdb pghost pgport pgoptions pgtty pgdbname))
+
+;; check to see that the backend connection was successfully made
+(when (= (pq-status conn) pg-connection-bad)
+ (format t "Connection to database '~A' failed.~%" pgdbname)
+ (format t "~A" (pq-error-message conn))
+ (exit-nicely conn))
+
+;; start a transaction block
+(setq res (pq-exec conn "BEGIN"))
+(when (or (null res) (not (= (pq-result-status res) pgres-command-ok)))
+ (format t "BEGIN command failed~%")
+ (pq-clear res)
+ (exit-nicely conn))
+
+;; Should PQclear PGresult whenever it is no longer needed to avoid memory leaks
+(pq-clear res)
+
+;; fetch rows from the pg_database, the system catalog of databases
+(setq res (pq-exec conn "DECLARE mycursor CURSOR FOR select * from pg_database"))
+(when (or (null res) (not (= (pq-result-status res) pgres-command-ok)))
+ (format t "DECLARE CURSOR command failed~%")
+ (pq-clear res)
+ (exit-nicely conn))
+(pq-clear res)
+(setq res (pq-exec conn "FETCH ALL in mycursor"))
+(when (or (null res) (not (= (pq-result-status res) pgres-tuples-ok)))
+ (format t "FETCH ALL command didn't return tuples properly~%")
+ (pq-clear res)
+ (exit-nicely conn))
+
+;; first, print out the attribute names
+(setq nfields (pq-nfields res))
+(dotimes (i nfields)
+ (format t "~15@<~A~>" (pq-fname res i))
+)
+(format t "~%")
+
+;; next, print out the rows
+(setq ntuples (pq-ntuples res))
+(dotimes (i ntuples)
+ (dotimes (j nfields)
+ (format t "~15@<~A~>" (pq-getvalue res i j))
+ )
+ (format t "~%")
+)
+(pq-clear res)
+
+;; close the cursor
+(setq res (pq-exec conn "CLOSE mycursor"))
+(pq-clear res)
+
+;; commit the transaction
+(setq res (pq-exec conn "COMMIT"))
+(pq-clear res)
+
+;; close the connection to the database and cleanup
+(pq-finish conn)
diff --git a/lisp/test/psql-2.lsp b/lisp/test/psql-2.lsp
new file mode 100644
index 0000000..011512c
--- /dev/null
+++ b/lisp/test/psql-2.lsp
@@ -0,0 +1,74 @@
+;; Postgresql C library interface, example program 2, using the xedit
+;; lisp interface
+
+;; Test of the asynchronous notification interface
+;;
+;; Start this program, then from psql in another window do
+;; NOTIFY TBL2;
+;;
+;; Or, if you want to get fancy, try this:
+;; Populate a database with the following:
+;;
+;; CREATE TABLE TBL1 (i int4);
+;;
+;; CREATE TABLE TBL2 (i int4);
+;;
+;; CREATE RULE r1 AS ON INSERT TO TBL1 DO
+;; (INSERT INTO TBL2 values (new.i); NOTIFY TBL2);
+;;
+;; and do
+;;
+;; INSERT INTO TBL1 values (10);
+(require "psql")
+
+(defun exit-nicely (conn)
+ (pq-finish conn)
+ (quit 1)
+)
+
+;; begin, by setting the parameters for a backend connection if the
+;; parameters are null, then the system will try to use reasonable
+;; defaults by looking up environment variables or, failing that,
+;; using hardwired constants
+(setq pghost nil) ; host name of the backend server
+(setq pgport nil) ; port of the backend server
+(setq pgoptions nil) ; special options to start up the backend server
+(setq pgtty nil) ; debugging tty for the backend server
+(setq pgdbname "test") ; change this to the name of your test database
+ ;; XXX Note: getenv not yet implemented in the
+ ; lisp interpreter
+
+;; make a connection to the database
+(setq conn (pq-setdb pghost pgport pgoptions pgtty pgdbname))
+
+;; check to see that the backend connection was successfully made
+(when (= (pq-status conn) pg-connection-bad)
+ (format t "Connection to database '~A' failed.~%" pgdbname)
+ (format t "~A" (pq-error-message conn))
+ (exit-nicely conn))
+
+(setq res (pq-exec conn "LISTEN TBL2"))
+(when (= (pq-status conn) pg-connection-bad)
+ (format t "LISTEN command failed~%")
+ (format t "~A" (pq-error-message conn))
+ (exit-nicely conn))
+
+;; Should PQclear PGresult whenever it is no longer needed to avoid memory leaks
+(pq-clear res)
+
+(loop
+ ;; wait a little bit between checks; waiting with select()
+ ;; would be more efficient.
+ ;; XXX Note: sleep not yet implemented in the lisp interpreter
+
+ ;; collect any asynchronous backend messages
+ (pq-consume-input conn)
+
+ ;; check for asynchronous notify messages
+ (when (setq notifies (pq-notifies conn))
+ (format t "ASYNC NOTIFY of '~A' from backend pid '~D' received~%"
+ (pg-notify-relname notifies) (pg-notify-be-pid notifies))
+ )
+)
+
+(pq-finish conn)
diff --git a/lisp/test/psql-3.lsp b/lisp/test/psql-3.lsp
new file mode 100644
index 0000000..bb172c9
--- /dev/null
+++ b/lisp/test/psql-3.lsp
@@ -0,0 +1,118 @@
+;; Postgresql C library interface, example program 3, using the xedit
+;; lisp interface
+
+;; Test the binary cursor interface
+;;
+;; populate a database by doing the following:
+;;
+;; CREATE TABLE test1 (i int4, d real, p polygon);
+;;
+;; INSERT INTO test1 values (1, 3.567, polygon '(3.0, 4.0, 1.0, 2.0)');
+;;
+;; INSERT INTO test1 values (2, 89.05, polygon '(4.0, 3.0, 2.0, 1.0)');
+;;
+;; the expected output is:
+;;
+;; tuple 0: got i = (4 bytes) 1, d = (4 bytes) 3.567000, p = (4
+;; bytes) 2 points boundbox = (hi=3.000000/4.000000, lo =
+;; 1.000000,2.000000) tuple 1: got i = (4 bytes) 2, d = (4 bytes)
+;; 89.050003, p = (4 bytes) 2 points boundbox =
+;; (hi=4.000000/3.000000, lo = 2.000000,1.000000)
+
+;; Output of the lisp code:
+;;
+;; type[0] = 23, size[0] = 4
+;; type[1] = 700, size[1] = 4
+;; type[2] = 604, size[2] = -1
+;; tuple 0: got
+;; i = (4 bytes) 1
+;; d = (4 bytes) 3.567
+;; p = (4 bytes) 2 points boundbox = (hi=3.0/4.0, lo = 1.0/2.0)
+;; tuple 1: got
+;; i = (4 bytes) 2
+;; d = (4 bytes) 89.05
+;; p = (4 bytes) 2 points boundbox = (hi=4.0/3.0, lo = 2.0/1.0)
+
+
+(require "psql")
+
+(defun exit-nicely (conn)
+ (pq-finish conn)
+ (quit 1)
+)
+
+;; begin, by setting the parameters for a backend connection if the
+;; parameters are null, then the system will try to use reasonable
+;; defaults by looking up environment variables or, failing that,
+;; using hardwired constants
+(setq pghost nil) ; host name of the backend server
+(setq pgport nil) ; port of the backend server
+(setq pgoptions nil) ; special options to start up the backend server
+(setq pgtty nil) ; debugging tty for the backend server
+(setq pgdbname "test") ; change this to the name of your test database
+ ;; XXX Note: getenv not yet implemented in the
+ ; lisp interpreter
+
+;; make a connection to the database
+(setq conn (pq-setdb pghost pgport pgoptions pgtty pgdbname))
+
+;; check to see that the backend connection was successfully made
+(when (= (pq-status conn) pg-connection-bad)
+ (format t "Connection to database '~A' failed.~%" pgdbname)
+ (format t "~A" (pq-error-message conn))
+ (exit-nicely conn))
+
+(setq res (pq-exec conn "BEGIN"))
+(when (= (pq-status conn) pg-connection-bad)
+ (format t "BEGIN command failed~%")
+ (pq-clear res)
+ (exit-nicely conn))
+
+;; Should PQclear PGresult whenever it is no longer needed to avoid memory leaks
+(pq-clear res)
+
+(setq res (pq-exec conn "DECLARE mycursor BINARY CURSOR FOR select * from test1"))
+(when (= (pq-status conn) pg-connection-bad)
+ (format t "DECLARE CURSOR command failed~%")
+ (pq-clear res)
+ (exit-nicely conn))
+(pq-clear res)
+
+(setq res (pq-exec conn "FETCH ALL in mycursor"))
+(when (or (null res) (not (= (pq-result-status res) pgres-tuples-ok)))
+ (format t "FETCH ALL command didn't return tuples properly~%")
+ (pq-clear res)
+ (exit-nicely conn))
+
+(setq i-fnum (pq-fnumber res "i"))
+(setq d-fnum (pq-fnumber res "d"))
+(setq p-fnum (pq-fnumber res "p"))
+
+(dotimes (i 3)
+ (format t "type[~D] = ~D, size[~D] = ~D~%"
+ i (pq-ftype res i) i (pq-fsize res i))
+)
+
+(dotimes (i (pq-ntuples res))
+ (setq i-val (pq-getvalue res i i-fnum 'int32))
+ (setq d-val (pq-getvalue res i d-fnum 'float))
+ (setq p-val (pq-getvalue res i p-fnum 'pg-polygon))
+ (format t "tuple ~D: got~%" i)
+ (format t " i = (~D bytes) ~D~%" (pq-getlength res i i-fnum) i-val)
+ (format t " d = (~D bytes) ~D~%" (pq-getlength res i d-fnum) d-val)
+ (format t " p = (~D bytes) ~D points~,8@Tboundbox = (hi=~F/~F, lo = ~F/~F)~%"
+ (pq-getlength res i d-fnum) (pg-polygon-num-points p-val)
+ (pg-point-x (pg-box-high (pg-polygon-boundbox p-val)))
+ (pg-point-y (pg-box-high (pg-polygon-boundbox p-val)))
+ (pg-point-x (pg-box-low (pg-polygon-boundbox p-val)))
+ (pg-point-y (pg-box-low (pg-polygon-boundbox p-val))))
+)
+(pq-clear res)
+
+(setq res (pq-exec conn "CLOSE mycursor"))
+(pq-clear res)
+
+(setq res (pq-exec conn "COMMIT"))
+(pq-clear res)
+
+(pq-finish conn)
diff --git a/lisp/test/regex.lsp b/lisp/test/regex.lsp
new file mode 100644
index 0000000..64ba572
--- /dev/null
+++ b/lisp/test/regex.lsp
@@ -0,0 +1,440 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/test/regex.lsp,v 1.2 2002/12/11 04:44:28 paulo Exp $
+;;
+
+;; Basic regex tests. This file is only for xedit lisp and for it's regex
+;; library. Note that the regex library used by xedit lisp is not mean't
+;; to be fully compatible with most regexes, but to be as fast as possible.
+;; This means that some patterns that looks basic may never be matched,
+;; but it is expected that almost any pattern can be rewritten to be
+;; matched, or in the worst case, it may be required to search in the
+;; regions matched by a previous regex.
+
+(defun re-test (expect &rest arguments &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (apply #'re-exec arguments))
+ (setq error nil)))
+ (if error
+ (format t "ERROR: (re-exec~{ ~S~}) => ~S~%" arguments error-value)
+ (or (equal result expect)
+ (format t "(re-exec~{ ~S~}) => should be ~S not ~S~%"
+ arguments expect result))))
+
+;; errors only generated for regex compilation (or incorrect arguments)
+(defun re-error (&rest arguments &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (apply #'re-comp arguments))
+ (setq error nil)))
+ (or error
+ (format t "ERROR: no error for (re-comp~{ ~S~})" arguments)))
+
+(re-error "")
+(re-error "a**")
+(re-error "[a")
+(re-error "a{")
+(re-error "a(")
+(re-error "a||b")
+(re-error "|b|c")
+(re-error "a|b|")
+
+(setq re (re-comp "abc"))
+(re-test '((0 . 3)) re "abc")
+(re-test '((0 . 3)) re "abc" :notbol t)
+(re-test '((0 . 3)) re "abc" :noteol t)
+(re-test '((0 . 3)) re "abc" :notbol t :noteol t)
+(re-test '((14 . 17)) re "aaaaaaaaaaaaaaabc")
+(re-test '((14 . 17)) re "aaaaaaaaaaaaaaabc" :start 12 :end 17)
+(re-test '((30 . 33)) re "xxxxxxxxxxxxxxaaaaaaaaaaaaaaaaabcxx")
+(re-test '((30 . 33)) re "xxxxxxxxxxxxxxaaaaaaaaaaaaaaaaabcxx" :start 28 :end 34)
+
+(setq re (re-comp "^abc"))
+(re-test '((0 . 3)) re "abc")
+(re-test :nomatch re "xabc")
+(re-test '((1 . 4)) re "xabc" :start 1)
+(re-test :nomatch re "xabc" :start 1 :notbol t)
+
+(setq re (re-comp "abc$"))
+(re-test '((0 . 3)) re "abc")
+(re-test :nomatch re "xabcx")
+(re-test '((1 . 4)) re "xabcx" :end 4)
+(re-test :nomatch re "xabc" :end 4 :noteol t)
+
+(setq re (re-comp "^abc$"))
+(re-test '((0 . 3)) re "abc")
+(re-test :nomatch re "xabcx")
+(re-test '((1 . 4)) re "xabcx" :start 1 :end 4)
+(re-test :nomatch re "xabcx" :start 1 :end 4 :notbol t)
+(re-test :nomatch re "xabcx" :start 1 :end 4 :noteol t)
+(re-test :nomatch re "xabcx" :start 1 :end 4 :notbol t :noteol t)
+(re-test nil re "abc" :count 0)
+
+(setq re (re-comp "abc|bcd|cde"))
+(re-test '((0 . 3)) re "abc")
+(re-test '((1 . 4)) re "aabc")
+(re-test '((3 . 6)) re "xxxbcdef")
+(re-test '((8 . 11)) re "abdzzzcdabcde")
+(re-test '((13 . 16)) re "xxxxabdecdabdcde")
+
+(setq re (re-comp "^abc|bcd$|cde"))
+(re-test '((0 . 3)) re "abcde")
+(re-test '((3 . 6)) re "xabcde")
+(re-test '((1 . 4)) re "xabcde" :start 1)
+(re-test '((3 . 6)) re "xabcde" :start 1 :notbol t)
+(re-test '((2 . 5)) re "xabcd")
+(re-test :nomatch re "xabcd" :noteol t)
+(re-test nil re "xabcd" :count 0)
+(re-test :nomatch re "abcdx" :notbol t)
+
+(setq re (re-comp "a?bc|ab?c|abc?"))
+(re-test '((0 . 3)) re "abc")
+(re-test :nomatch re "xxxb")
+(re-test '((3 . 5)) re "xxxbc")
+(re-test '((5 . 7)) re "sssssab")
+(re-test '((0 . 3)) re "abcd")
+(re-test '((1 . 4)) re "aabcdef")
+(re-test '((1 . 3)) re "aabbccdef") ;; ab matches abc?
+
+(setq re (re-comp "a?bc"))
+(re-test '((2 . 4)) re "acbcd")
+(re-test '((2 . 5)) re "acabcd")
+
+(setq re (re-comp "ab?c"))
+(re-test '((1 . 3)) re "xacc")
+(re-test '((2 . 5)) re "xxabcc")
+
+(setq re (re-comp "abc?"))
+(re-test '((1 . 3)) re "xababc")
+(re-test '((2 . 5)) re "xxabccabc")
+
+(setq re (re-comp "a*bc|ab*c|abc*"))
+(re-test '((0 . 9)) re "aaaaaaabc")
+(re-test '((1 . 10)) re "xaaaaaaabc")
+(re-test '((3 . 12)) re "xyzaaaaaaabc")
+(re-test '((0 . 4)) re "abbc")
+(re-test '((2 . 9)) re "xxabbbbbc")
+(re-test '((0 . 12)) re "abcccccccccc")
+(re-test '((0 . 12)) re "abccccccccccd")
+(re-test '((16 . 29)) re "xxxxxxxaaaaaaaaaabbbbbbbbbbbccccccccccc")
+(re-test '((11 . 13)) re "xxxbbbbbbbbbc")
+(re-test '((8 . 10)) re "aaaaazbxacd")
+
+(setq re (re-comp "a*bc"))
+(re-test '((2 . 4)) re "acbcd")
+(re-test '((2 . 5)) re "acabcd")
+(re-test '((2 . 8)) re "acaaaabcd")
+
+(setq re (re-comp "ab*c"))
+(re-test '((1 . 3)) re "xacc")
+(re-test '((2 . 5)) re "xxabcc")
+(re-test '((3 . 8)) re "xxaabbbcc")
+
+(setq re (re-comp "abc*"))
+(re-test '((1 . 3)) re "xababc")
+(re-test '((2 . 5)) re "xxabcbabccc")
+(re-test '((3 . 7)) re "axxabccabc")
+
+(setq re (re-comp "a+bc|ab+c|abc+"))
+(re-test :nomatch re "xxxbc")
+(re-test '((1 . 6)) re "xaaabc")
+(re-test '((8 . 12)) re "zzzzaaaaabbc")
+(re-test '((7 . 15)) re "zzzzaaaabbbbbbcccc")
+
+(setq re (re-comp "a.c"))
+(re-test '((0 . 3)) re "abc")
+(re-test '((1 . 4)) re "aaac")
+(re-test :nomatch re "xac")
+(re-test '((3 . 6)) re "xaxaac")
+(re-test '((2 . 5)) re "xxabc")
+(re-test '((3 . 6)) re "acxaxc")
+
+(setq re (re-comp "a*c"))
+(re-test '((0 . 1)) re "c")
+(re-test '((5 . 6)) re "xxxxxc")
+(re-test '((8 . 9)) re "xxxxxxxxc")
+(re-test '((7 . 8)) re "xxxxxxxcc")
+(re-test '((0 . 2)) re "ac")
+(re-test '((0 . 5)) re "aaaac")
+(re-test '((1 . 3)) re "xac")
+(re-test '((3 . 6)) re "xxxaac")
+(re-test '((2 . 4)) re "xxac")
+(re-test '((4 . 6)) re "xxxxac")
+
+(setq re (re-comp "a+c"))
+(re-test '((2 . 5)) re "xxaac")
+(re-test '((3 . 8)) re "xxxaaaac")
+(re-test '((6 . 8)) re "xaaaabac")
+(re-test :nomatch re "xxxc")
+(re-test '((4 . 9)) re "xxxxaaaaccc")
+
+(setq re (re-comp "a{4}b"))
+(re-test '((19 . 24)) re "xabxxaabxxxaaabxxxxaaaab")
+(re-test '((4 . 9)) re "aaabaaaab")
+
+(setq re (re-comp "a{4,}b"))
+(re-test '((3 . 8)) re "xxxaaaab")
+(re-test '((8 . 25)) re "zaaabzzzaaaaaaaaaaaaaaaab")
+
+(setq re (re-comp "a{,4}b"))
+(re-test '((0 . 1)) re "b")
+(re-test '((8 . 9)) re "xxxxxxxxb")
+(re-test '((6 . 11)) re "xaaaaaaaaab")
+(re-test '((3 . 5)) re "xxxab")
+(re-test '((6 . 10)) re "aaaaaxaaab")
+
+(setq re (re-comp "a{2,4}b"))
+(re-test :nomatch re "xab")
+(re-test '((1 . 4)) re "xaab")
+(re-test '((1 . 5)) re "xaaab")
+(re-test '((2 . 7)) re "xxaaaab")
+(re-test '((4 . 9)) re "xxxaaaaab")
+
+(setq re (re-comp "foo(bar|baz)fee"))
+(re-test '((9 . 18)) re "feebarbazfoobarfee")
+(re-test '((9 . 18) (12 . 15)) re "feebarbazfoobarfee" :count 2)
+(re-test '((13 . 22)) re "foofooobazfeefoobazfee")
+(re-test '((13 . 22) (16 . 19)) re "foofooobazfeefoobazfee" :count 3)
+
+(setq re (re-comp "foo(bar|baz)fee" :nosub t))
+(re-test '((9 . 18)) re "feebarbazfoobarfee")
+(re-test '((9 . 18)) re "feebarbazfoobarfee" :count 2)
+(re-test '((13 . 22)) re "foofooobazfeefoobazfee")
+(re-test '((13 . 22)) re "foofooobazfeefoobazfee" :count 3)
+
+(setq re (re-comp "f(oo|ee)ba[rz]"))
+(re-test :nomatch re "barfoebaz")
+(re-test '((3 . 9) (4 . 6)) re "bazfoobar" :count 2)
+(re-test '((3 . 9) (4 . 6)) re "barfeebaz" :count 2)
+
+(setq re (re-comp "f(oo|ee)ba[rz]" :nosub t))
+(re-test :nomatch re "barfoebaz")
+(re-test '((3 . 9)) re "bazfoobar" :count 2)
+(re-test '((3 . 9)) re "barfeebaz" :count 2)
+
+(setq re (re-comp "\\<(int|char)\\>"))
+(re-test '((15 . 18)) re "aint character int foo")
+(re-test '((15 . 18) (15 . 18)) re "aint character int foo" :count 2)
+
+(setq re (re-comp "\\<(int|char)\\>" :nosub t))
+(re-test '((15 . 18)) re "aint character int foo" :count 2)
+
+(setq re (re-comp "foo.*bar"))
+(re-test '((11 . 17)) re "barfoblaboofoobarfoobarfoobar")
+
+(setq re (re-comp "foo.+bar"))
+(re-test :nomatch re "foobar")
+(re-test '((6 . 13)) re "fobbarfooxbarfooybar")
+
+(setq re (re-comp "foo.?bar"))
+(re-test '((1 . 7)) re "xfoobar")
+(re-test :nomatch re "xxfooxxbar")
+(re-test '((3 . 10)) re "yyyfootbar")
+
+(setq re (re-comp "a.*b.*c"))
+(re-test '((0 . 3)) re "abc")
+(re-test '((9 . 18)) re "xxxxxxxxxabbbbbbbccaaaaabbbc")
+
+(setq re (re-comp "a.+b.*c"))
+(re-test :nomatch re "xxxabc")
+(re-test '((2 . 7)) re "xxaxbbc")
+
+(setq re (re-comp "a.+b.?c"))
+(re-test '((1 . 5)) re "xaabc")
+(re-test '((2 . 7)) re "xxaabbc")
+
+(setq re (re-comp "(foo.*|bar)fee"))
+(re-test '((3 . 9) (3 . 6)) re "barfoofee" :count 2)
+(re-test '((0 . 9) (0 . 6)) re "foobarfee" :count 2)
+(re-test '((4 . 10) (4 . 7)) re "xxfobarfee" :count 2)
+(re-test '((3 . 17) (3 . 14)) re "barfooooooobarfee" :count 2)
+(re-test '((4 . 10) (4 . 7)) re "xxfobarfeefoobar" :count 2)
+
+(setq re (re-comp "(foo.+|bar)fee"))
+(re-test :nomatch re "barfoofee" :count 2)
+(re-test '((3 . 10) (3 . 7)) re "barfooxfee" :count 2)
+
+(setq re (re-comp "(foo.?|bar)fee"))
+(re-test :nomatch re "foobar" :count 2)
+(re-test '((2 . 8) (2 . 5)) re "bafoofee" :count 2)
+(re-test '((2 . 9) (2 . 6)) re "bafooofeebarfee" :count 4)
+(re-test '((2 . 8) (2 . 5)) re "bafoofeebarfee" :count 2)
+(re-test nil re "bafoofeebarfee" :count 0)
+(re-test '((2 . 8)) re "bafoofeebarfee" :count 1)
+
+(setq re (re-comp "(a|b|c)\\1"))
+(re-test '((0 . 2) (0 . 1)) re "aa" :count 2)
+
+(setq re (re-comp "(a|b|c)(a|b|c)\\1\\2"))
+(re-test '((0 . 4) (0 . 1) (1 . 2)) re "acac" :count 5)
+(re-test '((4 . 8) (4 . 5) (5 . 6)) re "xxxxacac" :count 4)
+(re-test '((24 . 28) (24 . 25) (25 . 26)) re "xxacabacbcacbbacbcaaccabcaca" :count 3)
+(re-test '((4 . 8) (4 . 5) (5 . 6)) re "xyabcccc" :count 3)
+(re-test '((4 . 8) (4 . 5)) re "xyabcccc" :count 2)
+(re-test '((4 . 8)) re "xyabcccc" :count 1)
+(re-test nil re "xyabcccc" :count 0)
+
+(setq re (re-comp "(a*b)\\1"))
+(re-test '((3 . 15) (3 . 9)) re "xxxaaaaabaaaaab" :count 2)
+(re-test '((7 . 9) (7 . 8)) re "abaabaxbb" :count 2)
+
+(setq re (re-comp "(ab+c)\\1"))
+(re-test '((3 . 13) (3 . 8)) re "xaaabbbcabbbc" :count 3)
+
+(setq re (re-comp "(ab?c)\\1"))
+(re-test :nomatch re "abcac" :count 2)
+(re-test '((4 . 8) (4 . 6)) re "acabacac" :count 2)
+(re-test '((5 . 11) (5 . 8)) re "abcacabcabc" :count 2)
+(re-test '((3 . 7) (3 . 5)) re "abcacac" :count 2)
+
+(setq re (re-comp "a(.*)b\\1"))
+(re-test '((3 . 5) (4 . 4)) re "xxxab" :count 2)
+(re-test '((4 . 12) (5 . 8)) re "xxxxazzzbzzz" :count 2)
+
+(setq re (re-comp "abc" :icase t))
+(re-test '((0 . 3)) re "AbC")
+
+(setq re (re-comp "[0-9][a-z]+" :icase t))
+(re-test '((3 . 10)) re "xxx0aaZxYT9")
+
+(setq re (re-comp "a.b" :icase t))
+(re-test '((10 . 13)) re "aaaaaaaaaaaxB")
+
+(setq re (re-comp "a.*z" :icase t))
+(re-test '((3 . 9)) re "xxxAaaaaZ")
+(re-test '((2 . 6)) re "xxaaaZaaa")
+
+(setq re (re-comp "\\<(lambda|defun|defmacro)\\>" :icase t))
+(re-test '((5 . 11)) re " (lambda")
+(re-test '((5 . 11) (5 . 11)) re " (lambda" :count 2)
+(re-test :nomatch re "lamda defunn deffmacro")
+
+(setq re (re-comp "\\<(nil|t)\\>" :icase t))
+(re-test '((3 . 6)) re "it Nil")
+(re-test '((3 . 6) (3 . 6)) re "it Nil" :count 6)
+(re-test :nomatch re "nilo")
+
+(setq re (re-comp "\\<(begin|end)\\>" :icase t))
+(re-test '((21 . 24) (21 . 24)) re "beginning the ending EnD" :count 7)
+
+(setq re (re-comp "a.*" :newline t))
+(re-test '((0 . 1)) re "a
+aaa")
+(re-test '((3 . 4)) re "xyza
+aa")
+
+(setq re (re-comp "a.+" :newline t))
+(re-test '((2 . 5)) re "a
+aaa")
+(re-test '((5 . 7)) re "xyza
+aa")
+
+(setq re (re-comp "a.?" :newline t))
+(re-test '((0 . 1)) re "a
+aaa")
+(re-test '((3 . 4)) re "xyza
+aa")
+
+(setq re (re-comp "a.*b.*c" :newline t))
+(re-test '((11 . 14)) re "xxaa
+zyacb
+abc")
+(re-test '((6 . 9)) re "xxxab
+abc
+c")
+
+(setq re (re-comp "a.+b.*c" :newline t))
+(re-test '((6 . 10)) re "ab
+bc
+abbc")
+
+(setq re (re-comp "a.?b.*c" :newline t))
+(re-test '((4 . 8)) re "ab
+cabbc
+cc")
+
+(setq re (re-comp "^foo$" :newline t))
+(re-test '((11 . 14)) re "bar
+foobar
+foo")
+(re-test '((0 . 3)) re "foo
+bar
+foo
+bar")
+(re-test '((8 . 11)) re "foo
+bar
+foo
+bar" :notbol t)
+(re-test '((8 . 11)) re "foo
+bar
+foo" :notbol t)
+(re-test :nomatch re "foo
+bar
+foo" :notbol t :noteol t)
+
+(setq re (re-comp "^\\s*#\\s*(define|include)\\s+.+" :newline t))
+(re-test '((8 . 18)) re "#define
+#include x")
+(re-test '((8 . 18) (9 . 16)) re "#define
+#include x" :count 2)
+
+(setq re (re-comp "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"))
+(re-test '((3 . 259)) re "zzzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzz")
+
+(setq re (re-comp "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~"))
+(re-test '((13 . 333)) re "String here: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890~/")
+
+(setq re (re-comp "(.*)\\D(\\d+)"))
+(re-test '((0 . 6) (0 . 3) (4 . 6)) re "abcW12" :count 3)
+(re-test '((0 . 6) (0 . 3)) re "abcW12" :count 2)
+(re-test '((0 . 6)) re "abcW12" :count 1)
+(re-test nil re "abcW12" :count 0)
+(re-test '((0 . 6) (0 . 3) (4 . 6)) re "abcW12abcW12" :count 3)
+(re-test '((0 . 6) (0 . 3) (4 . 6)) re "abcW12abcW12a" :count 3)
+
+(setq re (re-comp ".*\\d"))
+(re-test '((0 . 2)) re "a1a1a1aaaaaaa") ; minimal match only
+
+(setq re (re-comp "(.*)\\d"))
+(re-test '((0 . 2) (0 . 1)) re "a1a1a1aaaaaaa" :count 2); minimal match only
+
+(setq re (re-comp ".*(\\d)"))
+(re-test '((0 . 2) (1 . 2)) re "a1a1a1aaaaaaa" :count 2); minimal match only
+
+;; XXX this very simple pattern was entering an infinite loop
+;; actually, this pattern is not supported, just test if is not
+;; crashing (not supported because it is not cheap to match variations
+;; of the pattern)
+(setq re (re-comp "(.*a)?"))
+(re-test '((0 . 1)) re "aaaa") ; expected, minimal match
+(re-test '((0 . 1) (0 . 1)) re "aaaa" :count 2)
diff --git a/lisp/test/stream.lsp b/lisp/test/stream.lsp
new file mode 100644
index 0000000..8af4ff6
--- /dev/null
+++ b/lisp/test/stream.lsp
@@ -0,0 +1,807 @@
+;;
+;; Copyright (c) 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/test/stream.lsp,v 1.4 2002/12/10 03:59:04 paulo Exp $
+;;
+
+;; most format tests from the cltl second edition samples
+
+;; basic io/format/pathname/stream tests
+
+(defun do-format-test (error-test expect arguments
+ &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (apply #'format nil arguments))
+ (setq error nil)
+ )
+ )
+ (if error-test
+ (or error
+ (format t "ERROR: no error for (format nil~{ ~S~}), result was ~S~%"
+ arguments result))
+ (if error
+ (format t "ERROR: (format nil~{ ~S~}) => ~S~%" arguments error-value)
+ (or (string= result expect)
+ (format t "(format nil~{ ~S~}) => should be ~S not ~S~%"
+ arguments expect result)))
+ )
+)
+
+(defun format-test (expect &rest arguments)
+ (do-format-test nil expect arguments))
+
+(defun format-error (&rest arguments)
+ (do-format-test t nil arguments))
+
+
+
+(defun compare-test (test expect function arguments
+ &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (apply function arguments))
+ (setq error nil)
+ )
+ )
+ (if error
+ (format t "ERROR: (~S~{ ~S~}) => ~S~%" function arguments error-value)
+ (or (funcall test result expect)
+ (format t "(~S~{ ~S~}) => should be ~S not ~S~%"
+ function arguments expect result
+ )
+ )
+ )
+)
+
+(defun compare-eval (test expect form
+ &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (eval form))
+ (setq error nil)
+ )
+ )
+ (if error
+ (format t "ERROR: ~S => ~S~%" form error-value)
+ (or (funcall test result expect)
+ (format t "~S => should be ~S not ~S~%"
+ form expect result
+ )
+ )
+ )
+)
+
+(defun error-test (function &rest arguments &aux result (error t))
+ (ignore-errors
+ (setq result (apply function arguments))
+ (setq error nil)
+ )
+ (or error
+ (format t "ERROR: no error for (~S~{ ~S~}), result was ~S~%"
+ function arguments result)
+ )
+)
+
+(defun error-eval (form &aux result (error t))
+ (ignore-errors
+ (setq result (eval form))
+ (setq error nil)
+ )
+ (or error
+ (format t "ERROR: no error for ~S, result was ~S~%" form result)
+ )
+)
+
+(defun eq-test (expect function &rest arguments)
+ (compare-test #'eq expect function arguments))
+
+(defun eql-test (expect function &rest arguments)
+ (compare-test #'eql expect function arguments))
+
+(defun equal-test (expect function &rest arguments)
+ (compare-test #'equal expect function arguments))
+
+(defun equalp-test (expect function &rest arguments)
+ (compare-test #'equalp expect function arguments))
+
+(defun eq-eval (expect form)
+ (compare-eval #'eq expect form))
+
+(defun eql-eval (expect form)
+ (compare-eval #'eql expect form))
+
+(defun equal-eval (expect form)
+ (compare-eval #'equal expect form))
+
+(defun equalp-eval (expect form)
+ (compare-eval #'equalp expect form))
+
+(defun bool-test (expect function &rest arguments
+ &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (apply function arguments))
+ (setq error nil)
+ )
+ )
+ (if error
+ (format t "ERROR: (~S~{ ~S~}) => ~S~%" function arguments error-value)
+ (or (eq (null result) (null expect))
+ (format t "(~S~{ ~S~}) => should be ~A not ~A~%"
+ function arguments expect result
+ )
+ )
+ )
+)
+
+(defun bool-eval (expect form &aux result (error t) unused error-value)
+ (multiple-value-setq
+ (unused error-value)
+ (ignore-errors
+ (setq result (eval form))
+ (setq error nil)
+ )
+ )
+ (if error
+ (format t "ERROR: ~S => ~S~%" form error-value)
+ (or (eq (null result) (null expect))
+ (format t "~S => should be ~A not ~A~%"
+ form expect result
+ )
+ )
+ )
+)
+
+
+;; format - function
+
+;; ~c
+(format-test "A" "~C" #\A)
+(format-test " " "~C" #\Space)
+(format-test "A" "~:C" #\A)
+(format-test "Space" "~:C" #\Space)
+(format-test "#\\A" "~@C" #\A)
+(format-test "#\\Space" "~@C" #\Space)
+(format-test " " "~A" #\Space)
+(let ((*print-escape* t)) (format-test " " "~A" #\Space))
+(format-test "#\\Space" "~S" #\Space)
+(let ((*print-escape* nil)) (format-test "#\\Space" "~S" #\Space))
+
+;; ~%
+(format-test "
+" "~%")
+(format-test "
+
+
+" "~3%")
+
+;; ~&
+(format-test "" "~&")
+(format-test "
+" "~2&")
+
+;; ~|
+(format-test " " "~|")
+
+;; ~~
+(format-test "~~~" "~3~")
+
+;; radix
+(format-test "1101" "~,,' ,4:B" 13)
+(format-test "1 0001" "~,,' ,4:B" 17)
+(format-test "1101 0000 0101" "~14,,' ,4:B" 3333)
+(format-test "1 22" "~3,,,' ,2:R" 17)
+(format-test "6|55|35" "~,,'|,2:D" #xFFFF)
+(format-test "1,000,000" "~,,,3:D" 1000000)
+(format-test "one hundred and twenty-three thousand, four hundred and fifty-six"
+ "~R" 123456)
+(format-test "six hundred and fifty-four thousand, three hundred twenty-first"
+ "~:R" 654321)
+(format-test "MCCXXXIV" "~@R" 1234)
+(format-test "MCCXXXXVIIII" "~@:R" 1249)
+(format-test "3039" "~X" 12345)
+(format-test "30071" "~O" 12345)
+(format-test "9IX" "~36R" 12345)
+(format-test "11000000111001" "~B" 12345)
+(format-test "The answer is 5." "The answer is ~D." 5)
+(format-test "The answer is 5." "The answer is ~3D." 5)
+(format-test "The answer is 005." "The answer is ~3,'0D." 5)
+(format-test "1111 1010 1100 1110" "~,,' ,4:B" #xFACE)
+(format-test "1 1100 1110" "~,,' ,4:B" #x1CE)
+(format-test "1111 1010 1100 1110" "~19,,' ,4:B" #xFACE)
+(format-test " 1 1100 1110" "~19,,' ,4:B" #x1CE)
+
+;; 6.37 and 6.38 are correct
+#+xedit (format-test "6.38" "~4,2F" 6.375d0)
+(format-test "10.0" "~,1F" 9.995d0)
+;; 6.37E+2 and 6.38E+2 are correct
+#+xedit (format-test " 6.38E+2" "~8,2E" 637.5)
+(do*
+ (
+ (n '(3.14159 -3.14159 100.0 1234.0 0.006) (cdr n))
+ (r '(" 3.14| 31.42| 3.14|3.1416|3.14|3.14159"
+ " -3.14|-31.42| -3.14|-3.142|-3.14|-3.14159"
+ "100.00|******|100.00| 100.0|100.00|100.0"
+ "1234.00|******|??????|1234.0|1234.00|1234.0"
+ " 0.01| 0.06| 0.01| 0.006|0.01|0.006") (cdr r))
+ (x (car n) (car n))
+ )
+ ((endp n))
+ (format-test (car r)
+ "~6,2F|~6,2,1,'*F|~6,2,,'?F|~6F|~,2F|~F" x x x x x x)
+)
+(do*
+ (
+ (n '(3.14159 -3.14159 1100.0 1.1e13 #+xedit 1.1e120) (cdr n))
+ (r '(" 3.14E+0| 31.42$-01|+.003E+03| 3.14E+0"
+ " -3.14E+0|-31.42$-01|-.003E+03| -3.14E+0"
+ " 1.10E+3| 11.00$+02|+.001E+06| 1.10E+3"
+ "*********| 11.00$+12|+.001E+16| 1.10E+13"
+ #+xedit
+ "*********|??????????|%%%%%%%%%|1.10E+120") (cdr r))
+ (x (car n) (car n))
+ )
+ ((endp n))
+ (format-test (car r)
+ "~9,2,1,,'*E|~10,3,2,2,'?,,'$E|~9,3,2,-2,'%@E|~9,2E" x x x x)
+)
+(do
+ (
+ (k -5 (1+ k))
+ (r '("Scale factor -5: | 0.000003E+06|"
+ "Scale factor -4: | 0.000031E+05|"
+ "Scale factor -3: | 0.000314E+04|"
+ "Scale factor -2: | 0.003142E+03|"
+ "Scale factor -1: | 0.031416E+02|"
+ "Scale factor 0: | 0.314159E+01|"
+ "Scale factor 1: | 3.141590E+00|"
+ "Scale factor 2: | 31.41590E-01|"
+ "Scale factor 3: | 314.1590E-02|"
+ "Scale factor 4: | 3141.590E-03|"
+ "Scale factor 5: | 31415.90E-04|"
+ "Scale factor 6: | 314159.0E-05|"
+ "Scale factor 7: | 3141590.E-06|") (cdr r))
+ )
+ ((endp r))
+ (format-test (car r) "Scale factor ~2D: | ~12,6,2,VE|" k k 3.14159)
+)
+(do*
+ (
+ (n '(0.0314159 0.314159 3.14159 31.4159 314.159 3141.59 3.14E12
+ #+xedit 3.14d120) (cdr n))
+ (r '(" 3.14E-2|314.2$-04|0.314E-01| 3.14E-2"
+ " 0.31 |0.314 |0.314 | 0.31 "
+ " 3.1 | 3.14 | 3.14 | 3.1 "
+ " 31. | 31.4 | 31.4 | 31. "
+ " 3.14E+2| 314. | 314. | 3.14E+2"
+ " 3.14E+3|314.2$+01|0.314E+04| 3.14E+3"
+ "*********|314.0$+10|0.314E+13| 3.14E+12"
+ #+xedit "*********|?????????|%%%%%%%%%|3.14E+120") (cdr r))
+ (x (car n) (car n))
+ )
+ ((endp n))
+ (format-test (car r) "~9,2,1,,'*G|~9,3,2,3,'?,,'$G|~9,3,2,0,'%G|~9,2G"
+ x x x x)
+)
+(format-test " 1." "~4,0f" 0.5)
+(format-test " 0." "~4,0f" 0.4)
+
+;; ~p
+(setq n 3)
+(format-test "3 items found.""~D item~:P found." n)
+(format-test "three dogs are here." "~R dog~:[s are~; is~] here." n (= n 1))
+(format-test "three dogs are here." "~R dog~:*~[s are~; is~:;s are~] here." n)
+(format-test "Here are three puppies.""Here ~[are~;is~:;are~] ~:*~R pupp~:@P." n)
+(format-test "7 tries/1 win" "~D tr~:@P/~D win~:P" 7 1)
+(format-test "1 try/0 wins" "~D tr~:@P/~D win~:P" 1 0)
+(format-test "1 try/3 wins" "~D tr~:@P/~D win~:P" 1 3)
+
+;; ~t
+(format-test " foo" "~8Tfoo")
+#+xedit (format-test " foo" "~8,3Tfoo")
+(format-test " foo" "~8,3@Tfoo")
+(format-test " foo" "~1,3@Tfoo")
+
+;; ~*
+(format-test "2" "~*~D" 1 2 3 4)
+(format-test "4" "~3*~D" 1 2 3 4)
+(format-test "2" "~3*~2:*~D" 1 2 3 4)
+(format-test "4 3 2 1 2 3 4" "~3@*~D ~2@*~D ~1@*~D ~0@*~D ~D ~D ~D" 1 2 3 4)
+
+;; ~?
+(format-test "<Foo 5> 7" "~? ~D" "<~A ~D>" '("Foo" 5) 7)
+(format-test "<Foo 5> 7" "~? ~D" "<~A ~D>" '("Foo" 5 14) 7)
+(format-test "<Foo 5> 7" "~@? ~D" "<~A ~D>" "Foo" 5 7)
+(format-test "<Foo 5> 14" "~@? ~D" "<~A ~D>" "Foo" 5 14 7)
+
+
+(format-error "~:[abc~:@(def~;ghi~:@(jkl~]mno~)" 1)
+(format-error "~?ghi~)" "abc~@(def")
+
+
+;; ~(...~)
+(format-test "XIV xiv" "~@R ~(~@R~)" 14 14)
+(format-test "Zero errors detected." "~@(~R~) error~:P detected." 0)
+(format-test "One error detected." "~@(~R~) error~:P detected." 1)
+(format-test "Twenty-three errors detected." "~@(~R~) error~:P detected." 23)
+
+;; ~[...~]
+(format-test "Persian Cat" "~[Siamese~;Manx~;Persian~] Cat" 2)
+(format-test " Cat" "~[Siamese~;Manx~;Persian~] Cat" 3)
+(format-test "Siamese Cat" "~[Siamese~;Manx~;Persian~] Cat" 0)
+(setq *print-level* nil *print-length* 5)
+(format-test " print length = 5"
+ "~@[ print level = ~D~]~@[ print length = ~D~]" *print-level* *print-length*)
+(setq foo "Items:~#[ none~; ~S~; ~S and ~S~:;~@{ ~#[~;and ~]~S~^,~}~].")
+(format-test "Items: none." foo)
+(format-test "Items: FOO." foo 'foo)
+(format-test "Items: FOO and BAR." foo 'foo 'bar)
+(format-test "Items: FOO, BAR, and BAZ." foo 'foo 'bar 'baz)
+(format-test "Items: FOO, BAR, BAZ, and QUUX." foo 'foo 'bar 'baz 'quux)
+
+;; ~{...~}
+(format-test "The winners are: FRED HARRY JILL."
+ "The winners are:~{ ~S~}." '(fred harry jill))
+(format-test "Pairs: <A,1> <B,2> <C,3>." "Pairs:~{ <~S,~S>~}." '(a 1 b 2 c 3))
+(format-test "Pairs: <A,1> <B,2> <C,3>."
+ "Pairs:~:{ <~S,~S>~}." '((a 1) (b 2) (c 3)))
+(format-test "Pairs: <A,1> <B,2> <C,3>."
+ "Pairs:~:@{ <~S,~S>~}." '(a 1) '(b 2) '(c 3))
+
+;; ~<...~>
+(format-test "foo bar" "~10<foo~;bar~>")
+(format-test " foo bar" "~10:<foo~;bar~>")
+(format-test " foo bar " "~10:@<foo~;bar~>")
+(format-test " foobar" "~10<foobar~>")
+(format-test " foobar" "~10:<foobar~>")
+(format-test "foobar " "~10@<foobar~>")
+(format-test " foobar " "~10:@<foobar~>")
+
+;; ~^
+(setq donestr "Done.~^ ~D warning~:P.~^ ~D error~:P.")
+(format-test "Done." donestr)
+(format-test "Done. 3 warnings." donestr 3)
+(format-test "Done. 1 warning. 5 errors." donestr 1 5)
+(format-test "/HOT .../HAMBURGER/ICE .../FRENCH ..."
+ "~:{/~S~^ ...~}" '((hot dog) (hamburger) (ice cream) (french fries)))
+(format-test "/HOT .../HAMBURGER .../ICE .../FRENCH"
+ "~:{/~S~:^ ...~}" '((hot dog) (hamburger) (ice cream) (french fries)))
+(format-test "/HOT .../HAMBURGER"
+ "~:{/~S~:#^ ...~}" '((hot dog) (hamburger) (ice cream) (french fries)))
+(setq tellstr "~@(~@[~R~]~^ ~A.~)")
+(format-test "Twenty-three" tellstr 23)
+(format-test " Losers." tellstr nil "losers")
+(format-test "Twenty-three losers." tellstr 23 "losers")
+(format-test " FOO" "~15<~S~;~^~S~;~^~S~>" 'foo)
+(format-test "FOO BAR" "~15<~S~;~^~S~;~^~S~>" 'foo 'bar)
+(format-test "FOO BAR BAZ" "~15<~S~;~^~S~;~^~S~>" 'foo 'bar 'baz)
+
+
+;; make-pathname - function
+(equal-test #P"/public/games/chess.db"
+ #'make-pathname :directory '(:absolute "public" "games")
+ :name "chess" :type "db")
+(equal-test #P"/etc/passwd" #'list* #P"/etc/passwd")
+(setq path (make-pathname :directory '(:absolute "public" "games")
+ :name "chess" :type "db"))
+(eq-test path #'pathname path)
+(eq-test nil #'pathname-host path)
+(eq-test nil #'pathname-device path)
+(equal-test '(:absolute "public" "games") #'pathname-directory path)
+(equal-test "chess" #'pathname-name path)
+(equal-test "db" #'pathname-type path)
+(eq-test nil #'pathname-version path)
+(equal-test #P"/tmp/foo.txt" #'make-pathname :defaults "/tmp/foo.txt")
+
+#+xedit (equal-test #P"/tmp/foo.txt" #'pathname "///tmp///foo.txt")
+;; XXX changed to remove extra separators
+;; (equal-test #P"///tmp///foo.txt" #'pathname "///tmp///foo.txt")
+
+
+;; merge-pathnames - function
+(equal-test #P"/tmp/foo.txt" #'merge-pathnames "/tmp/foo" "/tmp/foo.txt")
+(equal-test #P"/tmp/foo.txt" #'merge-pathnames "foo" "/tmp/foo.txt")
+(equal-test #P"/tmp/foo/bar.txt" #'merge-pathnames "foo/bar" "/tmp/foo.txt")
+
+;; namestring - function
+(setq path (merge-pathnames "foo/bar" "/tmp/foo.txt"))
+(equal-test "/tmp/foo/bar.txt" #'namestring path)
+(equal-test "" #'host-namestring path)
+(equal-test "/tmp/foo/" #'directory-namestring path)
+(equal-test "bar.txt" #'file-namestring path)
+(equal-test "/tmp/foo/bar.txt" #'enough-namestring path)
+(equal-test "foo/bar.txt" #'enough-namestring path "/tmp/")
+(equal-test "bar.txt" #'enough-namestring path "/tmp/foo/")
+(equal-test "foo/bar.txt" #'enough-namestring path "/tmp/foo")
+
+;; parse-namestring - function
+(equal-eval '(#P"foo" 3) '(multiple-value-list (parse-namestring "foo")))
+(equal-eval '(#P"foo" 0) '(multiple-value-list (parse-namestring #P"foo")))
+
+
+
+;; read - function
+(setq is (make-string-input-stream " foo "))
+(eq-test t #'streamp is)
+(eq-test t #'input-stream-p is)
+(eq-test nil #'output-stream-p is)
+(eq-test 'foo #'read is)
+(eq-test t #'close is)
+(setq is (make-string-input-stream "xfooy" 1 4))
+(eq-test 'foo #'read is)
+(eq-test t #'close is)
+(setq is (make-string-input-stream ""))
+(eq-test nil #'read is nil)
+(eq-test 'end-of-string #'read is nil 'end-of-string)
+(close is)
+(error-test #'read is)
+(error-test #'read is nil)
+(error-test #'read is nil 'end-of-string)
+(eq-test t #'streamp is)
+(eq-test nil #'input-stream-p is)
+(eq-test nil #'streamp "test")
+(error-test #'input-stream-p "test")
+
+;; read-char - function
+(setq is (make-string-input-stream "0123"))
+(setq test nil)
+(equal-eval '(#\0 #\1 #\2 #\3)
+ '(do ((c (read-char is) (read-char is nil 'the-end)))
+ ((not (characterp c)) test)
+ (setq test (append test (list c)))))
+(close is)
+(setq is (make-string-input-stream "abc"))
+(eql-test #\a #'read-char is)
+(eql-test #\b #'read-char is)
+(eql-test #\c #'read-char is)
+(error-test #'read-char is)
+(eq-test nil #'read-char is nil)
+(eq-test :end-of-string #'read-char is nil :end-of-string)
+(eq-test t #'close is)
+
+;; read-char-no-hang - function
+(setq is (make-string-input-stream "0123"))
+(setq test nil)
+(equal-eval '(#\0 #\1 #\2 #\3)
+ '(do ((c (read-char-no-hang is) (read-char-no-hang is nil 'the-end)))
+ ((not (characterp c)) test)
+ (setq test (append test (list c)))))
+(close is)
+(setq is (make-string-input-stream "abc"))
+(eql-test #\a #'read-char-no-hang is)
+(eql-test #\b #'read-char-no-hang is)
+(eql-test #\c #'read-char-no-hang is)
+(error-test #'read-char-no-hang is)
+(eq-test nil #'read-char-no-hang is nil)
+(eq-test :end-of-string #'read-char-no-hang is nil :end-of-string)
+(eq-test t #'close is)
+#+(and xedit unix)
+(progn
+ ;; wait one second for input pooling every 0.1 seconds
+ (defun wait-for-cat ()
+ (let ((time 0.0))
+ (loop
+ (and (listen is) (return))
+ (sleep 0.1)
+ (when (>= (incf time 0.1) 1.0)
+ (format t "Cat is sleeping~%")
+ (return)))))
+ (setq is (make-pipe "/bin/cat" :direction :io))
+ (equal-test "dog" #'write-line "dog" is)
+ (wait-for-cat)
+ (eql-test #\d #'read-char-no-hang is)
+ (eql-test #\o #'read-char-no-hang is)
+ (eql-test #\g #'read-char-no-hang is)
+ (eql-test #\Newline #'read-char-no-hang is)
+ (eq-test nil #'read-char-no-hang is)
+ (eq-test nil #'read-char-no-hang is)
+ (equal-test "mouse" #'write-line "mouse" is)
+ (wait-for-cat)
+ (eql-test #\m #'read-char-no-hang is)
+ (eql-test #\o #'read-char-no-hang is)
+ (eql-test #\u #'read-char-no-hang is)
+ (eql-test #\s #'read-char-no-hang is)
+ (eql-test #\e #'read-char-no-hang is)
+ (eql-test #\Newline #'read-char-no-hang is)
+ (eq-test nil #'read-char-no-hang is)
+ (eq-test t #'close is)
+ (error-test #'read-char-no-hang is)
+ (error-test #'read-char-no-hang is nil)
+ (error-test #'read-char-no-hang is nil t)
+)
+
+;; read-from-string - function
+(equal-eval '(3 5)
+ '(multiple-value-list (read-from-string " 1 3 5" t nil :start 2)))
+(equal-eval '((a b c) 7)
+ '(multiple-value-list (read-from-string "(a b c)")))
+(error-test #'read-from-string "")
+(eq-test nil #'read-from-string "" nil)
+(eq-test 'end-of-file #'read-from-string "" nil 'end-of-file)
+
+;; read-line - function
+(setq is (make-string-input-stream "line 1
+line 2"))
+(equal-eval '("line 1" nil) '(multiple-value-list (read-line is)))
+(equal-eval '("line 2" t) '(multiple-value-list (read-line is)))
+(error-test #'read-line is)
+(equal-eval '(nil t) '(multiple-value-list (read-line is nil)))
+(equal-eval '(end-of-string t)
+ '(multiple-value-list (read-line is nil 'end-of-string)))
+
+
+;; write - function
+;; XXX several write options still missing
+(setq os (make-string-output-stream))
+(equal-test '(1 2 3 4) #'write '(1 2 3 4) :stream os)
+(equal-test "(1 2 3 4)" #'get-output-stream-string os)
+(eq-test t #'streamp os)
+(eq-test t #'output-stream-p os)
+(eq-test nil #'input-stream-p os)
+(equal-test '(:foo :bar) #'write '(:foo :bar) :case :downcase :stream os)
+(equal-test "(:foo :bar)" #'get-output-stream-string os)
+(equal-test '(:foo :bar) #'write '(:foo :bar) :case :capitalize :stream os)
+(equal-test "(:Foo :Bar)" #'get-output-stream-string os)
+(equal-test '(:foo :bar) #'write '(:foo :bar) :case :upcase :stream os)
+(equal-test "(:FOO :BAR)" #'get-output-stream-string os)
+(equal-test '(foo bar baz) #'write '(foo bar baz) :length 2 :stream os)
+(equal-test "(FOO BAR ...)" #'get-output-stream-string os)
+(equal-test '(foo (bar) baz) #'write '(foo (bar) baz) :level 1 :stream os)
+(equal-test "(FOO # BAZ)" #'get-output-stream-string os)
+(setq circle '#1=(1 #1#))
+(eq-test circle #'write circle :circle t :stream os)
+(equal-test "#1=(1 #1#)" #'get-output-stream-string os)
+(eql-test #\Space #'write #\Space :stream os)
+(equal-test "#\\Space" #'get-output-stream-string os)
+(eql-test #\Space #'write #\Space :escape nil :stream os)
+(equal-test " " #'get-output-stream-string os)
+(eq-test t #'close os)
+(eq-test nil #'output-stream-p os)
+(error-test #'output-stream-p "test")
+(error-test #'write 'foo :stream "bar")
+
+;; fresh-line - function
+(setq os (make-string-output-stream))
+(equal-test "some text" #'write-string "some text" os)
+(eq-test t #'fresh-line os)
+(eq-test nil #'fresh-line os)
+(equal-test "more text" #'write-string "more text" os)
+(equal-test "some text
+more text" #'get-output-stream-string os)
+(equal-test nil #'fresh-line os)
+(equal-test nil #'fresh-line os)
+(equal-test "" #'get-output-stream-string os)
+(close os)
+(error-test #'fresh-line 1)
+
+;; prin1 - function
+;; (prin1 object stream) ==
+;; (write object :stream stream :escape t)
+(setq p-os (make-string-output-stream) w-os (make-string-output-stream))
+(dolist (object (list #\a 1 "string" 2.5d0 '(a . b) '(a b c) #P"foo"
+ *package* *standard-input* #c(1 2) #(1 2 3)
+ (make-hash-table)))
+ (eq-test object #'prin1 object p-os)
+ (eq-test object #'write object :stream w-os :escape t)
+ (equal-test (get-output-stream-string p-os)
+ #'get-output-stream-string w-os))
+(close p-os)
+(close w-os)
+(error-test #'prin1 1 1)
+
+;; princ - function
+;; (princ object stream) ==
+;; (write object :stream stream :escape nil :readably nil)
+;; XXX readably not yet implemented
+(setq p-os (make-string-output-stream) w-os (make-string-output-stream))
+(dolist (object (list #\a 1 "string" 2.5d0 '(a . b) '(a b c) #P"foo"
+ *package* *standard-input* #c(1 2) #(1 2 3)
+ (make-hash-table)))
+ (eq-test object #'princ object p-os)
+ (eq-test object #'write object :stream w-os :escape nil)
+ (equal-test (get-output-stream-string p-os)
+ #'get-output-stream-string w-os))
+(close p-os)
+(close w-os)
+(error-test #'princ 1 1)
+
+;; print - function
+;; (print object stream) ==
+;; (progn
+;; (terpri stream)
+;; (write object :stream stream :escape t)
+;; (write-char #\Space stream))
+(setq p-os (make-string-output-stream) w-os (make-string-output-stream))
+(dolist (object (list #\a 1 "string" 2.5d0 '(a . b) '(a b c) #P"foo"
+ *package* *standard-input* #c(1 2) #(1 2 3)
+ (make-hash-table)))
+ (eq-test object #'print object p-os)
+ (progn
+ (eq-test nil #'terpri w-os)
+ (eq-test object #'write object :stream w-os :escape t)
+ (eql-test #\Space #'write-char #\Space w-os))
+ (equal-test (get-output-stream-string p-os)
+ #'get-output-stream-string w-os))
+(close p-os)
+(close w-os)
+(error-test #'print 1 1)
+
+;; terpri - function
+(setq os (make-string-output-stream))
+(equal-test "some text" #'write-string "some text" os)
+(eq-test nil #'terpri os)
+(eq-test nil #'terpri os)
+(equal-test "more text" #'write-string "more text" os)
+(equal-test "some text
+
+more text" #'get-output-stream-string os)
+(equal-test nil #'terpri os)
+(equal-test nil #'terpri os)
+(equal-test "
+
+" #'get-output-stream-string os)
+(close os)
+(error-test #'terpri 1)
+
+;; write-char - function
+(equal-eval "a b"
+ '(with-output-to-string (s)
+ (write-char #\a s)
+ (write-char #\Space s)
+ (write-char #\b s)))
+(error-test #'write-char 1)
+
+;; write-line - function
+(setq os (make-string-output-stream))
+(equal-test "text" #'write-line "text" os)
+(equal-test "text
+" #'get-output-stream-string os)
+(eql-test #\< #'write-char #\< os)
+(equal-test "text" #'write-line "text" os :start 1 :end 3)
+(eql-test #\> #'write-char #\> os)
+(equal-test "<ex
+>" #'get-output-stream-string os)
+(error-test #'write-line 1)
+(close os)
+
+;; write-string - function
+(setq os (make-string-output-stream))
+(equal-test "text" #'write-string "text" os)
+(equal-test "text" #'get-output-stream-string os)
+(eql-test #\< #'write-char #\< os)
+(equal-test "text" #'write-string "text" os :start 1 :end 3)
+(eql-test #\> #'write-char #\> os)
+(equal-test "<ex>" #'get-output-stream-string os)
+(error-test #'write-string #\a)
+(close os)
+
+
+;; open - function
+(setq name #P"delete-me.text")
+(bool-eval t '(setq file (open name :direction :output)))
+(equal-test "some text" #'write-line "some text" file)
+(close file)
+(equal-test "delete-me.text" #'file-namestring (truename name))
+(setq file (open name :direction :output :if-exists :rename))
+(equal-test "other text" #'write-line "other text" file)
+(close file)
+(equal-test "delete-me.text" #'file-namestring (truename name))
+;; Clisp returns the pathname if the file exists
+#+xedit (eq-test t #'delete-file name)
+#+clisp (bool-test t #'delete-file name)
+(setq backup
+ #+xedit "delete-me.text~"
+ #+clisp "delete-me.text%"
+ #+cmu "delete-me.text.BAK")
+(bool-test t #'delete-file backup)
+(eq-test nil #'delete-file name)
+(eq-test nil #'directory name)
+(eq-test nil #'directory backup)
+;; test append
+(with-open-file (s name :direction :output :if-exists :error)
+ (write-line "line 1" s))
+(with-open-file (s name :direction :output :if-exists :append)
+ (write-line "line 2" s))
+(with-open-file (s name :direction :input)
+ (equal-test "line 1" #'read-line s)
+ (equal-test "line 2" #'read-line s)
+ (eq-test 'eof #'read-line s nil 'eof)
+)
+(bool-test t #'delete-file name)
+;; test overwrite
+(with-open-file (s name :direction :output :if-exists :error)
+ (write-line "overwrite-me" s))
+(with-open-file (s name :direction :output :if-exists :overwrite)
+ (write-line "some-text" s))
+(with-open-file (s name :direction :input)
+ (equal-test "some-text" #'read-line s)
+ (eq-test 'eof #'read-line s nil 'eof))
+;; test check for file existence
+(eq-test nil #'open name :direction :output :if-exists nil)
+(error-test #'open name :direction :output :if-exists :error)
+(bool-test t #'delete-file name)
+;; test check for no file existence
+(eq-test nil #'open name :direction :output :if-does-not-exist nil)
+(error-test #'open name :direction :output :if-does-not-exist :error)
+#+xedit ;; test io -- not sure if this is the expected behaviour
+(progn
+ (with-open-file (s name :direction :io)
+ (write-line "foo" s)
+ (write-line "bar" s))
+ (with-open-file (s name :direction :io :if-exists :append)
+ (equal-test "foo" #'read-line s)
+ (equal-test "bar" #'read-line s)
+ (eq-test 'eof #'read-line s nil 'eof)
+ (write-line "baz" s))
+ (with-open-file (s name :direction :io :if-exists :append)
+ (equal-test "foo" #'read-line s)
+ (equal-test "bar" #'read-line s)
+ (equal-test "baz" #'read-line s)
+ (eq-test 'eof #'read-line s nil 'eof))
+ (bool-test t #'delete-file name)
+)
+
+;; delete-file - function
+(eq-eval nil
+ '(with-open-file (s "delete-me.text" :direction :output :if-exists :error)))
+(eq-test t #'pathnamep (setq p (probe-file "delete-me.text")))
+(bool-test t #'delete-file p)
+(eq-test nil #'probe-file "delete-me.text")
+(bool-eval t
+ '(with-open-file (s "delete-me.text" :direction :output :if-exists :error)
+ (delete-file s)))
+(bool-test nil #'probe-file "delete-me.text")
+
+;; rename-file - function
+(setq name "foo.bar")
+(bool-eval t '(setq file (open name :direction :output :if-exists :error)))
+(eq-test t #'close file)
+(setq result (multiple-value-list (rename-file name "bar.foo")))
+(eql-test 3 #'length result)
+(eq-test t #'pathnamep (first result))
+(eq-test t #'pathnamep (second result))
+(eq-test t #'pathnamep (third result))
+(equal-test (third result) #'truename "bar.foo")
+(eq-test nil #'directory name)
+(eq-test nil #'directory (second result))
+(equal-test (list (third result)) #'directory (third result))
+(error-test #'truename name)
+(error-test #'truename (second result))
+(eq-test nil #'probe-file name)
+(bool-test t #'probe-file (first result))
+(eq-test nil #'probe-file (second result))
+(bool-test t #'probe-file (third result))
+(bool-test t #'delete-file "bar.foo")
+(eq-test nil #'delete-file (third result))
+(eq-test nil #'delete-file (second result))
diff --git a/lisp/test/widgets.lsp b/lisp/test/widgets.lsp
new file mode 100644
index 0000000..be68788
--- /dev/null
+++ b/lisp/test/widgets.lsp
@@ -0,0 +1,71 @@
+;;
+;; Copyright (c) 2001 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.
+;;
+;; Author: Paulo César Pereira de Andrade
+;;
+;;
+;; $XFree86: xc/programs/xedit/lisp/test/widgets.lsp,v 1.3 2002/11/08 08:01:01 paulo Exp $
+;;
+(require "xaw")
+(require "xt")
+
+(defun quit-callback (widget user call) (quit))
+
+(setq toplevel
+ (xt-app-initialize 'appcontext "Widgets"
+ '(("title" . "Widgets (without customization)"))))
+
+(setq vpane
+ (xt-create-managed-widget "vpane" paned-widget-class toplevel))
+(setq form
+ (xt-create-managed-widget "form" form-widget-class vpane))
+(xt-create-managed-widget "command" command-widget-class form
+ '(("label" . "Command Widget")))
+(xt-create-managed-widget "label" label-widget-class form
+ '(("label" . "Label Widget") ("fromVert" . "command")))
+(xt-create-managed-widget "button" menu-button-widget-class form
+ '(("label" . "MenuButton Widget") ("fromVert" . "label")))
+
+(setq popup
+ (xt-create-managed-widget "menu" simple-menu-widget-class toplevel))
+(xt-create-managed-widget "smebsb" sme-bsb-object-class popup
+ '(("label" . "SmeBSB Object")))
+(xt-create-managed-widget "smeline" sme-line-object-class popup)
+(xt-create-managed-widget "smebsb2" sme-bsb-object-class popup
+ '(("label" . "SmeBSB Object two")))
+
+(xt-create-managed-widget "toggle" toggle-widget-class form
+ '(("label" . "Toggle Widget") ("fromVert" . "button")))
+(xt-create-managed-widget "repeater" repeater-widget-class form
+ '(("label" . "Repeater Widget") ("fromVert" . "toggle")))
+
+(setq quit
+ (xt-create-managed-widget "quit" command-widget-class vpane
+ '(("label" . "Quit"))))
+(xt-add-callback quit "callback" 'quit-callback)
+
+(xt-realize-widget toplevel)
+(xt-app-main-loop appcontext)
diff --git a/lisp/time.c b/lisp/time.c
new file mode 100644
index 0000000..3c21d00
--- /dev/null
+++ b/lisp/time.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/time.c,v 1.7 2002/11/08 08:00:57 paulo Exp $ */
+
+#include "time.h"
+#include "bytecode.h"
+
+/*
+ * Implementation
+*/
+LispObj *
+Lisp_Time(LispBuiltin *builtin)
+/*
+ time form
+ */
+{
+ struct itimerval real, virt, prof;
+ unsigned long count;
+ long sec, usec;
+ LispObj *result;
+#define MONTH 60 * 60 * 31
+
+ LispObj *form;
+
+ form = ARGUMENT(0);
+
+ real.it_value.tv_sec =
+ virt.it_value.tv_sec =
+ prof.it_value.tv_sec =
+ real.it_interval.tv_sec =
+ virt.it_interval.tv_sec =
+ prof.it_interval.tv_sec = MONTH;
+ real.it_value.tv_usec =
+ virt.it_value.tv_usec =
+ prof.it_value.tv_usec =
+ real.it_interval.tv_usec =
+ virt.it_interval.tv_usec =
+ prof.it_interval.tv_usec = 0;
+
+ setitimer(ITIMER_REAL, &real, NULL);
+ setitimer(ITIMER_VIRTUAL, &virt, NULL);
+ setitimer(ITIMER_PROF, &prof, NULL);
+
+ getitimer(ITIMER_REAL, &real);
+ getitimer(ITIMER_VIRTUAL, &virt);
+ getitimer(ITIMER_PROF, &prof);
+
+ lisp__data.gc.gctime = 0;
+ lisp__data.gc.timebits = 1;
+
+ count = lisp__data.gc.count;
+
+#if 0
+ form = CONS(form, NIL);
+ COD = CONS(form, COD);
+ result = LispExecuteBytecode(LispCompileForm(form));
+#else
+ result = EVAL(form);
+#endif
+
+ getitimer(ITIMER_REAL, &real);
+ getitimer(ITIMER_VIRTUAL, &virt);
+ getitimer(ITIMER_PROF, &prof);
+
+ sec = real.it_interval.tv_sec - real.it_value.tv_sec;
+ usec = real.it_interval.tv_usec - real.it_value.tv_usec;
+ if (usec < 0) {
+ --sec;
+ usec += 1000000;
+ }
+ LispMessage("Real time : %g sec", sec + usec / 1000000.0);
+
+ sec = virt.it_interval.tv_sec - virt.it_value.tv_sec;
+ usec = virt.it_interval.tv_usec - virt.it_value.tv_usec + 10000;
+ if (usec < 0) {
+ --sec;
+ usec += 1000000;
+ }
+ LispMessage("Virtual time: %g sec", sec + usec / 1000000.0);
+
+ sec = prof.it_interval.tv_sec - prof.it_value.tv_sec;
+ usec = prof.it_interval.tv_usec - prof.it_value.tv_usec + 10000;
+ if (usec < 0) {
+ --sec;
+ usec += 1000000;
+ }
+ LispMessage("Profile time: %g sec", sec + usec / 1000000.0);
+
+ real.it_value.tv_sec =
+ virt.it_value.tv_sec =
+ prof.it_value.tv_sec =
+ real.it_interval.tv_sec =
+ virt.it_interval.tv_sec =
+ prof.it_interval.tv_sec =
+ real.it_value.tv_usec =
+ virt.it_value.tv_usec =
+ prof.it_value.tv_usec =
+ real.it_interval.tv_usec =
+ virt.it_interval.tv_usec =
+ prof.it_interval.tv_usec = 0;
+
+ setitimer(ITIMER_REAL, &real, NULL);
+ setitimer(ITIMER_VIRTUAL, &virt, NULL);
+ setitimer(ITIMER_PROF, &prof, NULL);
+
+ LispMessage("GC: %ld times, %g sec",
+ lisp__data.gc.count - count, lisp__data.gc.gctime / 1000000.0);
+ lisp__data.gc.timebits = 0;
+
+ return (result);
+}
diff --git a/lisp/time.h b/lisp/time.h
new file mode 100644
index 0000000..3d07916
--- /dev/null
+++ b/lisp/time.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2001 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/time.h,v 1.3 2002/11/08 08:00:57 paulo Exp $ */
+
+#ifndef Lisp_time_h
+#define Lisp_time_h
+
+#include "private.h"
+
+LispObj *Lisp_Time(LispBuiltin*);
+
+#endif /* Lisp_time_h */
diff --git a/lisp/write.c b/lisp/write.c
new file mode 100644
index 0000000..4952119
--- /dev/null
+++ b/lisp/write.c
@@ -0,0 +1,2411 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/write.c,v 1.30 2002/12/04 18:43:19 paulo Exp $ */
+
+#include "write.h"
+#include "hash.h"
+#include <math.h>
+#include <ctype.h>
+
+#define FLOAT_PREC 17
+
+#define UPCASE 0
+#define DOWNCASE 1
+#define CAPITALIZE 2
+
+#define INCDEPTH() \
+ if (++info->depth > MAX_STACK_DEPTH / 2) \
+ LispDestroy("stack overflow")
+#define DECDEPTH() --info->depth
+
+/*
+ * Types
+ */
+typedef struct _circle_info {
+ long circle_nth; /* nth circular list */
+ LispObj *object; /* the circular object */
+} circle_info;
+
+typedef struct _write_info {
+ long depth;
+ long level; /* current level */
+ long length; /* current length */
+ long print_level; /* *print-level* when started printing */
+ long print_length; /* *print-length* when started printing */
+
+ int print_escape;
+ int print_case;
+
+ long circle_count;
+ /* used while building circle info */
+ LispObj **objects;
+ long num_objects;
+ /* the circular lists */
+ circle_info *circles;
+ long num_circles;
+} write_info;
+
+/*
+ * Prototypes
+ */
+static void check_stream(LispObj*, LispFile**, LispString**, int);
+static void parse_double(char*, int*, double, int);
+static int float_string_inc(char*, int);
+static void format_integer(char*, long, int);
+static int LispWriteCPointer(LispObj*, void*);
+static int LispWriteCString(LispObj*, char*, long, write_info*);
+static int LispDoFormatExponentialFloat(LispObj*, LispObj*,
+ int, int, int*, int, int,
+ int, int, int, int);
+
+static int LispWriteInteger(LispObj*, LispObj*);
+static int LispWriteCharacter(LispObj*, LispObj*, write_info*);
+static int LispWriteString(LispObj*, LispObj*, write_info*);
+static int LispWriteFloat(LispObj*, LispObj*);
+static int LispWriteAtom(LispObj*, LispObj*, write_info*);
+static int LispDoWriteAtom(LispObj*, char*, int, int);
+static int LispWriteList(LispObj*, LispObj*, write_info*, int);
+static int LispWriteArray(LispObj*, LispObj*, write_info*);
+static int LispWriteStruct(LispObj*, LispObj*, write_info*);
+static int LispDoWriteObject(LispObj*, LispObj*, write_info*, int);
+static void LispBuildCircle(LispObj*, write_info*);
+static void LispDoBuildCircle(LispObj*, write_info*);
+static long LispCheckCircle(LispObj*, write_info*);
+static int LispPrintCircle(LispObj*, LispObj*, long, int*, write_info*);
+static int LispWriteAlist(LispObj*, LispArgList*, write_info*);
+
+/*
+ * Initialization
+ */
+LispObj *Oprint_level, *Oprint_length, *Oprint_circle,
+ *Oprint_escape, *Oprint_case;
+LispObj *Kupcase, *Kdowncase, *Kcapitalize;
+
+/*
+ * Implementation
+ */
+void
+LispWriteInit(void)
+{
+ Oprint_level = STATIC_ATOM("*PRINT-LEVEL*");
+ LispProclaimSpecial(Oprint_level, NIL, NIL);
+ LispExportSymbol(Oprint_level);
+
+ Oprint_length = STATIC_ATOM("*PRINT-LENGTH*");
+ LispProclaimSpecial(Oprint_length, NIL, NIL);
+ LispExportSymbol(Oprint_length);
+
+ Oprint_circle = STATIC_ATOM("*PRINT-CIRCLE*");
+ LispProclaimSpecial(Oprint_circle, T, NIL);
+ LispExportSymbol(Oprint_circle);
+
+ Oprint_escape = STATIC_ATOM("*PRINT-ESCAPE*");
+ LispProclaimSpecial(Oprint_escape, T, NIL);
+ LispExportSymbol(Oprint_escape);
+
+ Kupcase = KEYWORD("UPCASE");
+ Kdowncase = KEYWORD("DOWNCASE");
+ Kcapitalize = KEYWORD("CAPITALIZE");
+ Oprint_case = STATIC_ATOM("*PRINT-CASE*");
+ LispProclaimSpecial(Oprint_case, Kupcase, NIL);
+ LispExportSymbol(Oprint_case);
+}
+
+LispObj *
+Lisp_FreshLine(LispBuiltin *builtin)
+/*
+ fresh-line &optional output-stream
+ */
+{
+ LispObj *output_stream;
+
+ output_stream = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ else if (output_stream != NIL) {
+ CHECK_STREAM(output_stream);
+ }
+ if (LispGetColumn(output_stream)) {
+ LispWriteChar(output_stream, '\n');
+ if (output_stream == NIL ||
+ (output_stream->data.stream.type == LispStreamStandard &&
+ output_stream->data.stream.source.file == Stdout))
+ LispFflush(Stdout);
+ return (T);
+ }
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Prin1(LispBuiltin *builtin)
+/*
+ prin1 object &optional output-stream
+ */
+{
+ LispObj *object, *output_stream;
+
+ output_stream = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ LispPrint(object, output_stream, 0);
+
+ return (object);
+}
+
+LispObj *
+Lisp_Princ(LispBuiltin *builtin)
+/*
+ princ object &optional output-stream
+ */
+{
+ int head;
+ LispObj *object, *output_stream;
+
+ output_stream = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ head = lisp__data.env.length;
+ LispAddVar(Oprint_escape, NIL);
+ ++lisp__data.env.head;
+ LispPrint(object, output_stream, 0);
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (object);
+}
+
+LispObj *
+Lisp_Print(LispBuiltin *builtin)
+/*
+ print object &optional output-stream
+ */
+{
+ LispObj *object, *output_stream;
+
+ output_stream = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ LispWriteChar(output_stream, '\n');
+ LispPrint(object, output_stream, 0);
+ LispWriteChar(output_stream, ' ');
+
+ return (object);
+}
+
+LispObj *
+Lisp_Terpri(LispBuiltin *builtin)
+/*
+ terpri &optional output-stream
+ */
+{
+ LispObj *output_stream;
+
+ output_stream = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ else if (output_stream != NIL) {
+ CHECK_STREAM(output_stream);
+ }
+ LispWriteChar(output_stream, '\n');
+ if (output_stream == NIL ||
+ (output_stream->data.stream.type == LispStreamStandard &&
+ output_stream->data.stream.source.file == Stdout))
+ LispFflush(Stdout);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Write(LispBuiltin *builtin)
+/*
+ write object &key case circle escape length level lines pretty readably right-margin stream
+ */
+{
+ int head = lisp__data.env.length;
+
+ LispObj *object, *ocase, *circle, *escape, *length, *level,
+ *lines, *pretty, *readably, *right_margin, *stream;
+
+ stream = ARGUMENT(10);
+ right_margin = ARGUMENT(9); /* yet unused */
+ readably = ARGUMENT(8); /* yet unused */
+ pretty = ARGUMENT(7); /* yet unused */
+ lines = ARGUMENT(6); /* yet unused */
+ level = ARGUMENT(5);
+ length = ARGUMENT(4);
+ escape = ARGUMENT(3);
+ circle = ARGUMENT(2);
+ ocase = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (stream == UNSPEC)
+ stream = NIL;
+ else if (stream != NIL) {
+ CHECK_STREAM(stream);
+ }
+
+ /* prepare the printer environment */
+ if (circle != UNSPEC)
+ LispAddVar(Oprint_circle, circle);
+ if (length != UNSPEC)
+ LispAddVar(Oprint_length, length);
+ if (level != UNSPEC)
+ LispAddVar(Oprint_level, level);
+ if (ocase != UNSPEC)
+ LispAddVar(Oprint_case, ocase);
+ if (escape != UNSPEC)
+ LispAddVar(Oprint_escape, escape);
+
+ lisp__data.env.head = lisp__data.env.length;
+
+ (void)LispWriteObject(stream, object);
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (object);
+}
+
+LispObj *
+Lisp_WriteChar(LispBuiltin *builtin)
+/*
+ write-char character &optional output-stream
+ */
+{
+ int ch;
+
+ LispObj *character, *output_stream;
+
+ output_stream = ARGUMENT(1);
+ character = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ CHECK_SCHAR(character);
+ ch = SCHAR_VALUE(character);
+
+ LispWriteChar(output_stream, ch);
+
+ return (character);
+}
+
+LispObj *
+Lisp_WriteLine(LispBuiltin *builtin)
+/*
+ write-line string &optional output-stream &key start end
+ */
+{
+ return (LispWriteString_(builtin, 1));
+}
+
+LispObj *
+Lisp_WriteString(LispBuiltin *builtin)
+/*
+ write-string string &optional output-stream &key start end
+ */
+{
+ return (LispWriteString_(builtin, 0));
+}
+
+
+int
+LispWriteObject(LispObj *stream, LispObj *object)
+{
+ write_info info;
+ int bytes;
+ LispObj *level, *length, *circle, *oescape, *ocase;
+
+ /* current state */
+ info.depth = info.level = info.length = 0;
+
+ /* maximum level to descend */
+ level = LispGetVar(Oprint_level);
+ if (level && INDEXP(level))
+ info.print_level = FIXNUM_VALUE(level);
+ else
+ info.print_level = -1;
+
+ /* maximum list length */
+ length = LispGetVar(Oprint_length);
+ if (length && INDEXP(length))
+ info.print_length = FIXNUM_VALUE(length);
+ else
+ info.print_length = -1;
+
+ /* detect circular/shared objects? */
+ circle = LispGetVar(Oprint_circle);
+ info.circle_count = 0;
+ info.objects = NULL;
+ info.num_objects = 0;
+ info.circles = NULL;
+ info.num_circles = 0;
+ if (circle && circle != NIL) {
+ LispBuildCircle(object, &info);
+ /* free this data now */
+ if (info.num_objects) {
+ LispFree(info.objects);
+ info.num_objects = 0;
+ }
+ }
+
+ /* escape characters and strings? */
+ oescape = LispGetVar(Oprint_escape);
+ if (oescape != NULL)
+ info.print_escape = oescape == NIL;
+ else
+ info.print_escape = -1;
+
+ /* don't use the default case printing? */
+ ocase = LispGetVar(Oprint_case);
+ if (ocase == Kdowncase)
+ info.print_case = DOWNCASE;
+ else if (ocase == Kcapitalize)
+ info.print_case = CAPITALIZE;
+ else
+ info.print_case = UPCASE;
+
+ bytes = LispDoWriteObject(stream, object, &info, 1);
+ if (circle && circle != NIL && info.num_circles)
+ LispFree(info.circles);
+
+ return (bytes);
+}
+
+static void
+LispBuildCircle(LispObj *object, write_info *info)
+{
+ LispObj *list;
+
+ switch (OBJECT_TYPE(object)) {
+ case LispCons_t:
+ LispDoBuildCircle(object, info);
+ break;
+ case LispArray_t:
+ /* Currently arrays are implemented as lists, but only
+ * the elements could/should be circular */
+ if (LispCheckCircle(object, info) >= 0)
+ return;
+ LispDoBuildCircle(object, info);
+ for (list = object->data.array.list;
+ CONSP(list); list = CDR(list))
+ LispBuildCircle(CAR(list), info);
+ break;
+ case LispStruct_t:
+ /* Like arrays, structs are currently implemented as lists,
+ * but only the elements could/should be circular */
+ if (LispCheckCircle(object, info) >= 0)
+ return;
+ LispDoBuildCircle(object, info);
+ for (list = object->data.struc.fields;
+ CONSP(list); list = CDR(list))
+ LispBuildCircle(CAR(list), info);
+ break;
+ case LispQuote_t:
+ case LispBackquote_t:
+ case LispFunctionQuote_t:
+ LispDoBuildCircle(object, info);
+ LispBuildCircle(object->data.quote, info);
+ break;
+ case LispComma_t:
+ LispDoBuildCircle(object, info);
+ LispBuildCircle(object->data.comma.eval, info);
+ break;
+ case LispLambda_t:
+ /* Circularity in a function body should fail elsewhere... */
+ if (LispCheckCircle(object, info) >= 0)
+ return;
+ LispDoBuildCircle(object, info);
+ LispBuildCircle(object->data.lambda.code, info);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+LispDoBuildCircle(LispObj *object, write_info *info)
+{
+ long i;
+
+ if (LispCheckCircle(object, info) >= 0)
+ return;
+
+ for (i = 0; i < info->num_objects; i++)
+ if (info->objects[i] == object) {
+ /* circularity found */
+ info->circles = LispRealloc(info->circles, sizeof(circle_info) *
+ (info->num_circles + 1));
+ info->circles[info->num_circles].circle_nth = 0;
+ info->circles[info->num_circles].object = object;
+ ++info->num_circles;
+ return;
+ }
+
+ /* object pointer not yet recorded */
+ if ((i % 16) == 0)
+ info->objects = LispRealloc(info->objects, sizeof(LispObj*) *
+ (info->num_objects + 16));
+ info->objects[info->num_objects++] = object;
+
+ if (CONSP(object)) {
+ if (CONSP(CAR(object)))
+ LispDoBuildCircle(CAR(object), info);
+ else
+ LispBuildCircle(CAR(object), info);
+ if (CONSP(CDR(object)))
+ LispDoBuildCircle(CDR(object), info);
+ else
+ LispBuildCircle(CDR(object), info);
+ }
+}
+
+static long
+LispCheckCircle(LispObj *object, write_info *info)
+{
+ long i;
+
+ for (i = 0; i < info->num_circles; i++)
+ if (info->circles[i].object == object)
+ return (i);
+
+ return (-1);
+}
+
+static int
+LispPrintCircle(LispObj *stream, LispObj *object, long circle,
+ int *length, write_info *info)
+{
+ char stk[32];
+
+ if (!info->circles[circle].circle_nth) {
+ sprintf(stk, "#%ld=", ++info->circle_count);
+ *length += LispWriteStr(stream, stk, strlen(stk));
+ info->circles[circle].circle_nth = info->circle_count;
+
+ return (1);
+ }
+ sprintf(stk, "#%ld#", info->circles[circle].circle_nth);
+ *length += LispWriteStr(stream, stk, strlen(stk));
+
+ return (0);
+}
+
+static int
+LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info)
+{
+ char *name;
+ int i, length = 0, need_space = 0;
+
+#define WRITE_ATOM(object) \
+ name = ATOMID(object); \
+ length += LispDoWriteAtom(stream, name, strlen(name), \
+ info->print_case)
+#define WRITE_STRING(string) \
+ length += LispDoWriteAtom(stream, string, strlen(string), \
+ info->print_case)
+#define WRITE_OBJECT(object) \
+ length += LispDoWriteObject(stream, object, info, 1)
+#define WRITE_OPAREN() \
+ length += LispWriteChar(stream, '(')
+#define WRITE_SPACE() \
+ length += LispWriteChar(stream, ' ')
+#define WRITE_CPAREN() \
+ length += LispWriteChar(stream, ')')
+
+ WRITE_OPAREN();
+ for (i = 0; i < alist->normals.num_symbols; i++) {
+ WRITE_ATOM(alist->normals.symbols[i]);
+ if (i + 1 < alist->normals.num_symbols)
+ WRITE_SPACE();
+ else
+ need_space = 1;
+ }
+ if (alist->optionals.num_symbols) {
+ if (need_space)
+ WRITE_SPACE();
+ WRITE_STRING(Soptional);
+ WRITE_SPACE();
+ for (i = 0; i < alist->optionals.num_symbols; i++) {
+ WRITE_OPAREN();
+ WRITE_ATOM(alist->optionals.symbols[i]);
+ WRITE_SPACE();
+ WRITE_OBJECT(alist->optionals.defaults[i]);
+ if (alist->optionals.sforms[i]) {
+ WRITE_SPACE();
+ WRITE_ATOM(alist->optionals.sforms[i]);
+ }
+ WRITE_CPAREN();
+ if (i + 1 < alist->optionals.num_symbols)
+ WRITE_SPACE();
+ }
+ need_space = 1;
+ }
+ if (alist->keys.num_symbols) {
+ if (need_space)
+ WRITE_SPACE();
+ length += LispDoWriteAtom(stream, Skey, 4, info->print_case);
+ WRITE_SPACE();
+ for (i = 0; i < alist->keys.num_symbols; i++) {
+ WRITE_OPAREN();
+ if (alist->keys.keys[i]) {
+ WRITE_OPAREN();
+ WRITE_ATOM(alist->keys.keys[i]);
+ WRITE_SPACE();
+ }
+ WRITE_ATOM(alist->keys.symbols[i]);
+ if (alist->keys.keys[i])
+ WRITE_CPAREN();
+ WRITE_SPACE();
+ WRITE_OBJECT(alist->keys.defaults[i]);
+ if (alist->keys.sforms[i]) {
+ WRITE_SPACE();
+ WRITE_ATOM(alist->keys.sforms[i]);
+ }
+ WRITE_CPAREN();
+ if (i + 1 < alist->keys.num_symbols)
+ WRITE_SPACE();
+ }
+ need_space = 1;
+ }
+ if (alist->rest) {
+ if (need_space)
+ WRITE_SPACE();
+ WRITE_STRING(Srest);
+ WRITE_SPACE();
+ WRITE_ATOM(alist->rest);
+ need_space = 1;
+ }
+ if (alist->auxs.num_symbols) {
+ if (need_space)
+ WRITE_SPACE();
+ WRITE_STRING(Saux);
+ WRITE_SPACE();
+ for (i = 0; i < alist->auxs.num_symbols; i++) {
+ WRITE_OPAREN();
+ WRITE_ATOM(alist->auxs.symbols[i]);
+ WRITE_SPACE();
+ WRITE_OBJECT(alist->auxs.initials[i]);
+ WRITE_CPAREN();
+ if (i + 1 < alist->auxs.num_symbols)
+ WRITE_SPACE();
+ }
+ }
+ WRITE_CPAREN();
+
+#undef WRITE_ATOM
+#undef WRITE_STRING
+#undef WRITE_OBJECT
+#undef WRITE_OPAREN
+#undef WRITE_SPACE
+#undef WRITE_CPAREN
+
+ return (length);
+}
+
+static void
+check_stream(LispObj *stream,
+ LispFile **file, LispString **string, int check_writable)
+{
+ /* NIL is UNIX stdout, *STANDARD-OUTPUT* may not be UNIX stdout */
+ if (stream == NIL) {
+ *file = Stdout;
+ *string = NULL;
+ }
+ else {
+ if (!STREAMP(stream))
+ LispDestroy("%s is not a stream", STROBJ(stream));
+ if (check_writable && !stream->data.stream.writable)
+ LispDestroy("%s is not writable", STROBJ(stream));
+ else if (stream->data.stream.type == LispStreamString) {
+ *string = SSTREAMP(stream);
+ *file = NULL;
+ }
+ else {
+ if (stream->data.stream.type == LispStreamPipe)
+ *file = OPSTREAMP(stream);
+ else
+ *file = stream->data.stream.source.file;
+ *string = NULL;
+ }
+ }
+}
+
+/* Assumes buffer has enough storage, 64 bytes should be more than enough */
+static void
+parse_double(char *buffer, int *exponent, double value, int d)
+{
+ char stk[64], fmt[32], *ptr, *fract = NULL;
+ int positive = value >= 0.0;
+
+parse_double_again:
+ if (d >= 8) {
+ double dcheck;
+ int icheck, count;
+
+ /* this should to do the correct rounding */
+ for (count = 2; count >= 0; count--) {
+ icheck = d <= 0 ? 0 : d > FLOAT_PREC ? FLOAT_PREC - count : d - count;
+ sprintf(fmt, "%%.%de", icheck);
+ sprintf(stk, fmt, value);
+ if (count) {
+ /* if the value read back is the same formatted */
+ sscanf(stk, "%lf", &dcheck);
+ if (dcheck == value)
+ break;
+ }
+ }
+ }
+ else {
+ sprintf(fmt, "%%.%de", d <= 0 ? 0 : d > FLOAT_PREC ? FLOAT_PREC : d);
+ sprintf(stk, fmt, value);
+ }
+
+ /* this "should" never fail */
+ ptr = strchr(stk, 'e');
+ if (ptr) {
+ *ptr++ = '\0';
+ *exponent = atoi(ptr);
+ }
+ else
+ *exponent = 0;
+
+ /* find start of number representation */
+ for (ptr = stk; *ptr && !isdigit(*ptr); ptr++)
+ ;
+
+ /* check if did not trim any significant digit,
+ * this may happen because '%.e' puts only one digit before the '.' */
+ if (d > 0 && d < FLOAT_PREC && fabs(value) >= 10.0 &&
+ strlen(ptr) - 1 - !positive <= *exponent) {
+ d += *exponent - (strlen(ptr) - 1 - !positive) + 1;
+ goto parse_double_again;
+ }
+
+ /* this "should" never fail */
+ fract = strchr(ptr, '.');
+ if (fract)
+ *fract++ = '\0';
+
+ /* store number representation in buffer */
+ *buffer = positive ? '+' : '-';
+ strcpy(buffer + 1, ptr);
+ if (fract)
+ strcpy(buffer + strlen(buffer), fract);
+}
+
+static void
+format_integer(char *buffer, long value, int radix)
+{
+ if (radix == 10)
+ sprintf(buffer, "%ld", value);
+ else if (radix == 16)
+ sprintf(buffer, "%lx", value);
+ else if (radix == 8)
+ sprintf(buffer, "%lo", value);
+ else {
+ /* use bignum routine to convert number to string */
+ mpi integer;
+
+ mpi_init(&integer);
+ mpi_seti(&integer, value);
+ mpi_getstr(buffer, &integer, radix);
+ mpi_clear(&integer);
+ }
+}
+
+static int
+LispWriteCPointer(LispObj *stream, void *data)
+{
+ char stk[32];
+
+#ifdef LONG64
+ sprintf(stk, "0x%016lx", (long)data);
+#else
+ sprintf(stk, "0x%08lx", (long)data);
+#endif
+
+ return (LispWriteStr(stream, stk, strlen(stk)));
+}
+
+static int
+LispWriteCString(LispObj *stream, char *string, long length, write_info *info)
+{
+ int result;
+
+ if (!info->print_escape) {
+ char *base, *ptr, *end;
+
+ result = LispWriteChar(stream, '"');
+ for (base = ptr = string, end = string + length; ptr < end; ptr++) {
+ if (*ptr == '\\' || *ptr == '"') {
+ result += LispWriteStr(stream, base, ptr - base);
+ result += LispWriteChar(stream, '\\');
+ result += LispWriteChar(stream, *ptr);
+ base = ptr + 1;
+ }
+ }
+ result += LispWriteStr(stream, base, end - base);
+ result += LispWriteChar(stream, '"');
+ }
+ else
+ result = LispWriteStr(stream, string, length);
+
+ return (result);
+}
+
+static int
+LispWriteList(LispObj *stream, LispObj *object, write_info *info, int paren)
+{
+ int length = 0;
+ long circle = 0;
+
+ INCDEPTH();
+ if (info->print_level < 0 || info->level <= info->print_level) {
+ LispObj *car, *cdr;
+ long print_length = info->length;
+
+ if (info->circles && (circle = LispCheckCircle(object, info)) >= 0) {
+ if (!paren) {
+ length += LispWriteStr(stream, ". ", 2);
+ paren = 1;
+ }
+ if (LispPrintCircle(stream, object, circle, &length, info) == 0) {
+ DECDEPTH();
+
+ return (length);
+ }
+ }
+
+ car = CAR(object);
+ cdr = CDR(object);
+
+ if (cdr == NIL) {
+ if (paren)
+ length += LispWriteChar(stream, '(');
+ if (info->print_length < 0 || info->length < info->print_length) {
+ info->length = 0;
+ length += LispDoWriteObject(stream, car, info, 1);
+ info->length = print_length + 1;
+ }
+ else
+ length += LispWriteStr(stream, "...", 3);
+ if (paren)
+ length += LispWriteChar(stream, ')');
+ }
+ else {
+ if (paren)
+ length += LispWriteChar(stream, '(');
+ if (info->print_length < 0 || info->length < info->print_length) {
+ info->length = 0;
+ length += LispDoWriteObject(stream, car, info, 1);
+ info->length = print_length + 1;
+ if (!CONSP(cdr)) {
+ length += LispWriteStr(stream, " . ", 3);
+ info->length = 0;
+ length += LispDoWriteObject(stream, cdr, info, 0);
+ }
+ else {
+ length += LispWriteChar(stream, ' ');
+ if (info->print_length < 0 ||
+ info->length < info->print_length)
+ length += LispWriteList(stream, cdr, info, 0);
+ else
+ length += LispWriteStr(stream, "...", 3);
+ }
+ }
+ else
+ length += LispWriteStr(stream, "...", 3);
+ if (paren)
+ length += LispWriteChar(stream, ')');
+ }
+ info->length = print_length;
+ }
+ else
+ length += LispWriteChar(stream, '#');
+ DECDEPTH();
+
+ return (length);
+}
+
+static int
+LispDoWriteObject(LispObj *stream, LispObj *object, write_info *info, int paren)
+{
+ long print_level;
+ int length = 0;
+ char stk[64], *string = NULL;
+
+write_again:
+ switch (OBJECT_TYPE(object)) {
+ case LispNil_t:
+ if (object == NIL)
+ string = Snil;
+ else if (object == T)
+ string = St;
+ else if (object == DOT)
+ string = "#<DOT>";
+ else if (object == UNSPEC)
+ string = "#<UNSPEC>";
+ else if (object == UNBOUND)
+ string = "#<UNBOUND>";
+ else
+ string = "#<ERROR>";
+ length += LispDoWriteAtom(stream, string, strlen(string),
+ info->print_case);
+ break;
+ case LispOpaque_t: {
+ char *desc = LispIntToOpaqueType(object->data.opaque.type);
+
+ length += LispWriteChar(stream, '#');
+ length += LispWriteCPointer(stream, object->data.opaque.data);
+ length += LispWriteStr(stream, desc, strlen(desc));
+ } break;
+ case LispAtom_t:
+ length += LispWriteAtom(stream, object, info);
+ break;
+ case LispFunction_t:
+ if (object->data.atom->a_function) {
+ object = object->data.atom->property->fun.function;
+ goto write_lambda;
+ }
+ length += LispWriteStr(stream, "#<", 2);
+ if (object->data.atom->a_compiled)
+ LispDoWriteAtom(stream, "COMPILED", 8, info->print_case);
+ else if (object->data.atom->a_builtin)
+ LispDoWriteAtom(stream, "BUILTIN", 7, info->print_case);
+ /* XXX the function does not exist anymore */
+ /* FIXME not sure if I want this fixed... */
+ else
+ LispDoWriteAtom(stream, "UNBOUND", 7, info->print_case);
+ LispDoWriteAtom(stream, "-FUNCTION", 9, info->print_case);
+ length += LispWriteChar(stream, ' ');
+ length += LispWriteAtom(stream, object->data.atom->object, info);
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispString_t:
+ length += LispWriteString(stream, object, info);
+ break;
+ case LispSChar_t:
+ length += LispWriteCharacter(stream, object, info);
+ break;
+ case LispDFloat_t:
+ length += LispWriteFloat(stream, object);
+ break;
+ case LispFixnum_t:
+ case LispInteger_t:
+ case LispBignum_t:
+ length += LispWriteInteger(stream, object);
+ break;
+ case LispRatio_t:
+ format_integer(stk, object->data.ratio.numerator, 10);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ length += LispWriteChar(stream, '/');
+ format_integer(stk, object->data.ratio.denominator, 10);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ break;
+ case LispBigratio_t: {
+ int sz;
+ char *ptr;
+
+ sz = mpi_getsize(mpr_num(object->data.mp.ratio), 10) + 1 +
+ mpi_getsize(mpr_den(object->data.mp.ratio), 10) + 1 +
+ (mpi_sgn(mpr_num(object->data.mp.ratio)) < 0);
+ if (sz > sizeof(stk))
+ ptr = LispMalloc(sz);
+ else
+ ptr = stk;
+ mpr_getstr(ptr, object->data.mp.ratio, 10);
+ length += LispWriteStr(stream, ptr, sz - 1);
+ if (ptr != stk)
+ LispFree(ptr);
+ } break;
+ case LispComplex_t:
+ length += LispWriteStr(stream, "#C(", 3);
+ length += LispDoWriteObject(stream,
+ object->data.complex.real, info, 0);
+ length += LispWriteChar(stream, ' ');
+ length += LispDoWriteObject(stream,
+ object->data.complex.imag, info, 0);
+ length += LispWriteChar(stream, ')');
+ break;
+ case LispCons_t:
+ print_level = info->level;
+ ++info->level;
+ length += LispWriteList(stream, object, info, paren);
+ info->level = print_level;
+ break;
+ case LispQuote_t:
+ length += LispWriteChar(stream, '\'');
+ paren = 1;
+ object = object->data.quote;
+ goto write_again;
+ case LispBackquote_t:
+ length += LispWriteChar(stream, '`');
+ paren = 1;
+ object = object->data.quote;
+ goto write_again;
+ case LispComma_t:
+ if (object->data.comma.atlist)
+ length += LispWriteStr(stream, ",@", 2);
+ else
+ length += LispWriteChar(stream, ',');
+ paren = 1;
+ object = object->data.comma.eval;
+ goto write_again;
+ break;
+ case LispFunctionQuote_t:
+ length += LispWriteStr(stream, "#'", 2);
+ paren = 1;
+ object = object->data.quote;
+ goto write_again;
+ case LispArray_t:
+ length += LispWriteArray(stream, object, info);
+ break;
+ case LispStruct_t:
+ length += LispWriteStruct(stream, object, info);
+ break;
+ case LispLambda_t:
+ write_lambda:
+ switch (object->funtype) {
+ case LispLambda:
+ string = "#<LAMBDA ";
+ break;
+ case LispFunction:
+ string = "#<FUNCTION ";
+ break;
+ case LispMacro:
+ string = "#<MACRO ";
+ break;
+ case LispSetf:
+ string = "#<SETF ";
+ break;
+ }
+ length += LispDoWriteAtom(stream, string, strlen(string),
+ info->print_case);
+ if (object->funtype != LispLambda) {
+ length += LispWriteAtom(stream, object->data.lambda.name, info);
+ length += LispWriteChar(stream, ' ');
+ length += LispWriteAlist(stream, object->data.lambda.name
+ ->data.atom->property->alist, info);
+ }
+ else {
+ length += LispDoWriteAtom(stream, Snil, 3, info->print_case);
+ length += LispWriteChar(stream, ' ');
+ length += LispWriteAlist(stream, (LispArgList*)object->
+ data.lambda.name->data.opaque.data,
+ info);
+ }
+ length += LispWriteChar(stream, ' ');
+ length += LispDoWriteObject(stream,
+ object->data.lambda.code, info, 0);
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispStream_t:
+ length += LispWriteStr(stream, "#<", 2);
+ if (object->data.stream.type == LispStreamFile)
+ string = "FILE-STREAM ";
+ else if (object->data.stream.type == LispStreamString)
+ string = "STRING-STREAM ";
+ else if (object->data.stream.type == LispStreamStandard)
+ string = "STANDARD-STREAM ";
+ else if (object->data.stream.type == LispStreamPipe)
+ string = "PIPE-STREAM ";
+ length += LispDoWriteAtom(stream, string, strlen(string),
+ info->print_case);
+
+ if (!object->data.stream.readable && !object->data.stream.writable)
+ length += LispDoWriteAtom(stream, "CLOSED",
+ 6, info->print_case);
+ else {
+ if (object->data.stream.readable)
+ length += LispDoWriteAtom(stream, "READ",
+ 4, info->print_case);
+ if (object->data.stream.writable) {
+ if (object->data.stream.readable)
+ length += LispWriteChar(stream, '-');
+ length += LispDoWriteAtom(stream, "WRITE",
+ 5, info->print_case);
+ }
+ }
+ if (object->data.stream.type != LispStreamString) {
+ length += LispWriteChar(stream, ' ');
+ length += LispDoWriteObject(stream,
+ object->data.stream.pathname,
+ info, 1);
+ /* same address/size for pipes */
+ length += LispWriteChar(stream, ' ');
+ length += LispWriteCPointer(stream,
+ object->data.stream.source.file);
+ if (object->data.stream.readable &&
+ object->data.stream.type == LispStreamFile &&
+ !object->data.stream.source.file->binary) {
+ length += LispWriteStr(stream, " @", 2);
+ format_integer(stk, object->data.stream.source.file->line, 10);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ }
+ }
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispPathname_t:
+ length += LispWriteStr(stream, "#P", 2);
+ paren = 1;
+ object = CAR(object->data.quote);
+ goto write_again;
+ case LispPackage_t:
+ length += LispDoWriteAtom(stream, "#<PACKAGE ",
+ 10, info->print_case);
+ length += LispWriteStr(stream,
+ THESTR(object->data.package.name),
+ STRLEN(object->data.package.name));
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispRegex_t:
+ length += LispDoWriteAtom(stream, "#<REGEX ",
+ 8, info->print_case);
+ length += LispDoWriteObject(stream,
+ object->data.regex.pattern, info, 1);
+ if (object->data.regex.options & RE_NOSPEC)
+ length += LispDoWriteAtom(stream, " :NOSPEC",
+ 8, info->print_case);
+ if (object->data.regex.options & RE_ICASE)
+ length += LispDoWriteAtom(stream, " :ICASE",
+ 7, info->print_case);
+ if (object->data.regex.options & RE_NOSUB)
+ length += LispDoWriteAtom(stream, " :NOSUB",
+ 7, info->print_case);
+ if (object->data.regex.options & RE_NEWLINE)
+ length += LispDoWriteAtom(stream, " :NEWLINE",
+ 9, info->print_case);
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispBytecode_t:
+ length += LispDoWriteAtom(stream, "#<BYTECODE ",
+ 11, info->print_case);
+ length += LispWriteCPointer(stream,
+ object->data.bytecode.bytecode);
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispHashTable_t:
+ length += LispDoWriteAtom(stream, "#<HASH-TABLE ",
+ 13, info->print_case);
+ length += LispWriteAtom(stream, object->data.hash.test, info);
+ snprintf(stk, sizeof(stk), " %g %g",
+ object->data.hash.table->rehash_size,
+ object->data.hash.table->rehash_threshold);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ snprintf(stk, sizeof(stk), " %ld/%ld>",
+ object->data.hash.table->count,
+ object->data.hash.table->num_entries);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ break;
+ }
+
+ return (length);
+}
+
+/* return current column number in stream */
+int
+LispGetColumn(LispObj *stream)
+{
+ LispFile *file;
+ LispString *string;
+
+ check_stream(stream, &file, &string, 0);
+ if (file != NULL)
+ return (file->column);
+ return (string->column);
+}
+
+/* write a character to stream */
+int
+LispWriteChar(LispObj *stream, int character)
+{
+ LispFile *file;
+ LispString *string;
+
+ check_stream(stream, &file, &string, 1);
+ if (file != NULL)
+ return (LispFputc(file, character));
+
+ return (LispSputc(string, character));
+}
+
+/* write a character count times to stream */
+int
+LispWriteChars(LispObj *stream, int character, int count)
+{
+ int length = 0;
+
+ if (count > 0) {
+ char stk[64];
+ LispFile *file;
+ LispString *string;
+
+ check_stream(stream, &file, &string, 1);
+ if (count >= sizeof(stk)) {
+ memset(stk, character, sizeof(stk));
+ for (; count >= sizeof(stk); count -= sizeof(stk)) {
+ if (file != NULL)
+ length += LispFwrite(file, stk, sizeof(stk));
+ else
+ length += LispSwrite(string, stk, sizeof(stk));
+ }
+ }
+ else
+ memset(stk, character, count);
+
+ if (count) {
+ if (file != NULL)
+ length += LispFwrite(file, stk, count);
+ else
+ length += LispSwrite(string, stk, count);
+ }
+ }
+
+ return (length);
+}
+
+/* write a string to stream */
+int
+LispWriteStr(LispObj *stream, char *buffer, long length)
+{
+ LispFile *file;
+ LispString *string;
+
+ check_stream(stream, &file, &string, 1);
+ if (file != NULL)
+ return (LispFwrite(file, buffer, length));
+ return (LispSwrite(string, buffer, length));
+}
+
+static int
+LispDoWriteAtom(LispObj *stream, char *string, int length, int print_case)
+{
+ int bytes = 0, cap = 0;
+ char buffer[128], *ptr;
+
+ switch (print_case) {
+ case DOWNCASE:
+ for (ptr = buffer; length > 0; length--, string++) {
+ if (isupper(*string))
+ *ptr = tolower(*string);
+ else
+ *ptr = *string;
+ ++ptr;
+ if (ptr - buffer >= sizeof(buffer)) {
+ bytes += LispWriteStr(stream, buffer, ptr - buffer);
+ ptr = buffer;
+ }
+ }
+ if (ptr > buffer)
+ bytes += LispWriteStr(stream, buffer, ptr - buffer);
+ break;
+ case CAPITALIZE:
+ for (ptr = buffer; length > 0; length--, string++) {
+ if (isalnum(*string)) {
+ if (cap && isupper(*string))
+ *ptr = tolower(*string);
+ else
+ *ptr = *string;
+ cap = 1;
+ }
+ else {
+ *ptr = *string;
+ cap = 0;
+ }
+ ++ptr;
+ if (ptr - buffer >= sizeof(buffer)) {
+ bytes += LispWriteStr(stream, buffer, ptr - buffer);
+ ptr = buffer;
+ }
+ }
+ if (ptr > buffer)
+ bytes += LispWriteStr(stream, buffer, ptr - buffer);
+ break;
+ default:
+ /* Strings are already stored upcase/quoted */
+ bytes += LispWriteStr(stream, string, length);
+ break;
+ }
+
+ return (bytes);
+}
+
+static int
+LispWriteAtom(LispObj *stream, LispObj *object, write_info *info)
+{
+ int length = 0;
+ LispAtom *atom = object->data.atom;
+ Atom_id id = atom->string;
+
+ if (atom->package != PACKAGE) {
+ if (atom->package == lisp__data.keyword)
+ length += LispWriteChar(stream, ':');
+ else if (atom->package == NULL)
+ length += LispWriteStr(stream, "#:", 2);
+ else {
+ /* Check if the symbol is visible */
+ int i, visible = 0;
+
+ if (atom->ext) {
+ for (i = lisp__data.pack->use.length - 1; i >= 0; i--) {
+ if (lisp__data.pack->use.pairs[i] == atom->package) {
+ visible = 1;
+ break;
+ }
+ }
+ }
+
+ if (!visible) {
+ /* XXX this assumes that package names are always "readable" */
+ length +=
+ LispDoWriteAtom(stream,
+ THESTR(atom->package->data.package.name),
+ STRLEN(atom->package->data.package.name),
+ info->print_case);
+ length += LispWriteChar(stream, ':');
+ if (!atom->ext)
+ length += LispWriteChar(stream, ':');
+ }
+ }
+ }
+ if (atom->unreadable)
+ length += LispWriteChar(stream, '|');
+ length += LispDoWriteAtom(stream, id, strlen(id),
+ atom->unreadable ? UPCASE : info->print_case);
+ if (atom->unreadable)
+ length += LispWriteChar(stream, '|');
+
+ return (length);
+}
+
+static int
+LispWriteInteger(LispObj *stream, LispObj *object)
+{
+ return (LispFormatInteger(stream, object, 10, 0, 0, 0, 0, 0, 0));
+}
+
+static int
+LispWriteCharacter(LispObj *stream, LispObj *object, write_info *info)
+{
+ return (LispFormatCharacter(stream, object, !info->print_escape, 0));
+}
+
+static int
+LispWriteString(LispObj *stream, LispObj *object, write_info *info)
+{
+ return (LispWriteCString(stream, THESTR(object), STRLEN(object), info));
+}
+
+static int
+LispWriteFloat(LispObj *stream, LispObj *object)
+{
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0 || (fabs(value) < 1.0E7 && fabs(value) > 1.0E-4))
+ return (LispFormatFixedFloat(stream, object, 0, 0, NULL, 0, 0, 0));
+
+ return (LispDoFormatExponentialFloat(stream, object, 0, 0, NULL,
+ 0, 1, 0, ' ', 'E', 0));
+}
+
+static int
+LispWriteArray(LispObj *stream, LispObj *object, write_info *info)
+{
+ int length = 0;
+ long print_level = info->level, circle;
+
+ if (info->circles && (circle = LispCheckCircle(object, info)) >= 0 &&
+ LispPrintCircle(stream, object, circle, &length, info) == 0)
+ return (length);
+
+ if (object->data.array.rank == 0) {
+ length += LispWriteStr(stream, "#0A", 3);
+ length += LispDoWriteObject(stream, object->data.array.list, info, 1);
+ return (length);
+ }
+
+ INCDEPTH();
+ ++info->level;
+ if (info->print_level < 0 || info->level <= info->print_level) {
+ if (object->data.array.rank == 1)
+ length += LispWriteStr(stream, "#(", 2);
+ else {
+ char stk[32];
+
+ format_integer(stk, object->data.array.rank, 10);
+ length += LispWriteChar(stream, '#');
+ length += LispWriteStr(stream, stk, strlen(stk));
+ length += LispWriteStr(stream, "A(", 2);
+ }
+
+ if (!object->data.array.zero) {
+ long print_length = info->length, local_length = 0;
+
+ if (object->data.array.rank == 1) {
+ LispObj *ary;
+ long count;
+
+ for (ary = object->data.array.dim, count = 1;
+ ary != NIL; ary = CDR(ary))
+ count *= FIXNUM_VALUE(CAR(ary));
+ for (ary = object->data.array.list; count > 0;
+ ary = CDR(ary), count--) {
+ if (info->print_length < 0 ||
+ ++local_length <= info->print_length) {
+ info->length = 0;
+ length += LispDoWriteObject(stream, CAR(ary), info, 1);
+ }
+ else {
+ length += LispWriteStr(stream, "...", 3);
+ break;
+ }
+ if (count - 1 > 0)
+ length += LispWriteChar(stream, ' ');
+ }
+ }
+ else {
+ LispObj *ary;
+ int i, k, rank, *dims, *loop;
+
+ rank = object->data.array.rank;
+ dims = LispMalloc(sizeof(int) * rank);
+ loop = LispCalloc(1, sizeof(int) * (rank - 1));
+
+ /* fill dim */
+ for (i = 0, ary = object->data.array.dim; ary != NIL;
+ i++, ary = CDR(ary))
+ dims[i] = FIXNUM_VALUE(CAR(ary));
+
+ i = 0;
+ ary = object->data.array.list;
+ while (loop[0] < dims[0]) {
+ if (info->print_length < 0 ||
+ local_length < info->print_length) {
+ for (; i < rank - 1; i++)
+ length += LispWriteChar(stream, '(');
+ --i;
+ for (;;) {
+ ++loop[i];
+ if (i && loop[i] >= dims[i])
+ loop[i] = 0;
+ else
+ break;
+ --i;
+ }
+ for (k = 0; k < dims[rank - 1] - 1;
+ k++, ary = CDR(ary)) {
+ if (info->print_length < 0 ||
+ k < info->print_length) {
+ ++local_length;
+ info->length = 0;
+ length += LispDoWriteObject(stream,
+ CAR(ary), info, 1);
+ length += LispWriteChar(stream, ' ');
+ }
+ }
+ if (info->print_length < 0 || k < info->print_length) {
+ ++local_length;
+ info->length = 0;
+ length += LispDoWriteObject(stream,
+ CAR(ary), info, 0);
+ }
+ else
+ length += LispWriteStr(stream, "...", 3);
+ for (k = rank - 1; k > i; k--)
+ length += LispWriteChar(stream, ')');
+ if (loop[0] < dims[0])
+ length += LispWriteChar(stream, ' ');
+ ary = CDR(ary);
+ }
+ else {
+ ++local_length;
+ length += LispWriteStr(stream, "...)", 4);
+ for (; local_length < dims[0] - 1; local_length++)
+ length += LispWriteStr(stream, " ...)", 5);
+ if (local_length <= dims[0])
+ length += LispWriteStr(stream, " ...", 4);
+ break;
+ }
+ }
+ LispFree(dims);
+ LispFree(loop);
+ }
+ info->length = print_length;
+ }
+ length += LispWriteChar(stream, ')');
+ }
+ else
+ length += LispWriteChar(stream, '#');
+ info->level = print_level;
+ DECDEPTH();
+
+ return (length);
+}
+
+static int
+LispWriteStruct(LispObj *stream, LispObj *object, write_info *info)
+{
+ int length;
+ long circle;
+ LispObj *symbol;
+ LispObj *def = object->data.struc.def;
+ LispObj *field = object->data.struc.fields;
+
+ if (info->circles && (circle = LispCheckCircle(object, info)) >= 0 &&
+ LispPrintCircle(stream, object, circle, &length, info) == 0)
+ return (length);
+
+ INCDEPTH();
+ length = LispWriteStr(stream, "#S(", 3);
+ symbol = SYMBOLP(CAR(def)) ? CAR(def) : CAAR(def);
+ length += LispWriteAtom(stream, symbol, info);
+ def = CDR(def);
+ for (; def != NIL; def = CDR(def), field = CDR(field)) {
+ length += LispWriteChar(stream, ' ');
+ symbol = SYMBOLP(CAR(def)) ? CAR(def) : CAAR(def);
+ length += LispWriteAtom(stream, symbol, info);
+ length += LispWriteChar(stream, ' ');
+ length += LispDoWriteObject(stream, CAR(field), info, 1);
+ }
+ length += LispWriteChar(stream, ')');
+ DECDEPTH();
+
+ return (length);
+}
+
+int
+LispFormatInteger(LispObj *stream, LispObj *object, int radix,
+ int atsign, int collon, int mincol,
+ int padchar, int commachar, int commainterval)
+{
+ char stk[128], *str = stk;
+ int i, length, sign, intervals;
+
+ if (LONGINTP(object))
+ format_integer(stk, LONGINT_VALUE(object), radix);
+ else {
+ if (mpi_getsize(object->data.mp.integer, radix) >= sizeof(stk))
+ str = mpi_getstr(NULL, object->data.mp.integer, radix);
+ else
+ mpi_getstr(str, object->data.mp.integer, radix);
+ }
+
+ sign = *str == '-';
+ length = strlen(str);
+
+ /* if collon, update length for the number of commachars to be printed */
+ if (collon && commainterval > 0 && commachar) {
+ intervals = length / commainterval;
+ length += intervals;
+ }
+ else
+ intervals = 0;
+
+ /* if sign must be printed, and number is positive */
+ if (atsign && !sign)
+ ++length;
+
+ /* if need padding */
+ if (padchar && mincol > length)
+ LispWriteChars(stream, padchar, mincol - length);
+
+ /* if need to print number sign */
+ if (sign || atsign)
+ LispWriteChar(stream, sign ? '-' : '+');
+
+ /* if need to print commas to separate groups of numbers */
+ if (intervals) {
+ int j;
+ char *ptr;
+
+ i = (length - atsign) - intervals;
+ j = i % commainterval;
+ /* make the loop below easier */
+ if (j == 0)
+ j = commainterval;
+ i -= j;
+ ptr = str + sign;
+ for (; j > 0; j--, ptr++)
+ LispWriteChar(stream, *ptr);
+ for (; i > 0; i -= commainterval) {
+ LispWriteChar(stream, commachar);
+ for (j = 0; j < commainterval; j++, ptr++)
+ LispWriteChar(stream, *ptr);
+ }
+ }
+ /* else, just print the string */
+ else
+ LispWriteStr(stream, str + sign, length - sign);
+
+ /* if number required more than sizeof(stk) bytes */
+ if (str != stk)
+ LispFree(str);
+
+ return (length);
+}
+
+int
+LispFormatRomanInteger(LispObj *stream, long value, int new_roman)
+{
+ char stk[32];
+ int length;
+
+ length = 0;
+ while (value > 1000) {
+ stk[length++] = 'M';
+ value -= 1000;
+ }
+ if (new_roman) {
+ if (value >= 900) {
+ strcpy(stk + length, "CM");
+ length += 2,
+ value -= 900;
+ }
+ else if (value < 500 && value >= 400) {
+ strcpy(stk + length, "CD");
+ length += 2;
+ value -= 400;
+ }
+ }
+ if (value >= 500) {
+ stk[length++] = 'D';
+ value -= 500;
+ }
+ while (value >= 100) {
+ stk[length++] = 'C';
+ value -= 100;
+ }
+ if (new_roman) {
+ if (value >= 90) {
+ strcpy(stk + length, "XC");
+ length += 2,
+ value -= 90;
+ }
+ else if (value < 50 && value >= 40) {
+ strcpy(stk + length, "XL");
+ length += 2;
+ value -= 40;
+ }
+ }
+ if (value >= 50) {
+ stk[length++] = 'L';
+ value -= 50;
+ }
+ while (value >= 10) {
+ stk[length++] = 'X';
+ value -= 10;
+ }
+ if (new_roman) {
+ if (value == 9) {
+ strcpy(stk + length, "IX");
+ length += 2,
+ value -= 9;
+ }
+ else if (value == 4) {
+ strcpy(stk + length, "IV");
+ length += 2;
+ value -= 4;
+ }
+ }
+ if (value >= 5) {
+ stk[length++] = 'V';
+ value -= 5;
+ }
+ while (value) {
+ stk[length++] = 'I';
+ --value;
+ }
+
+ stk[length] = '\0';
+
+ return (LispWriteStr(stream, stk, length));
+}
+
+int
+LispFormatEnglishInteger(LispObj *stream, long number, int ordinal)
+{
+ static char *ds[] = {
+ "", "one", "two", "three", "four",
+ "five", "six", "seven", "eight", "nine",
+ "ten", "eleven", "twelve", "thirteen", "fourteen",
+ "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
+ };
+ static char *dsth[] = {
+ "", "first", "second", "third", "fourth",
+ "fifth", "sixth", "seventh", "eighth", "ninth",
+ "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth",
+ "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"
+ };
+ static char *hs[] = {
+ "", "", "twenty", "thirty", "forty",
+ "fifty", "sixty", "seventy", "eighty", "ninety"
+ };
+ static char *hsth[] = {
+ "", "", "twentieth", "thirtieth", "fortieth",
+ "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth"
+ };
+ static char *ts[] = {
+ "", "thousand", "million"
+ };
+ static char *tsth[] = {
+ "", "thousandth", "millionth"
+ };
+ char stk[256];
+ int length, sign;
+
+ sign = number < 0;
+ if (sign)
+ number = -number;
+ length = 0;
+
+#define SIGNLEN 6 /* strlen("minus ") */
+ if (sign) {
+ strcpy(stk, "minus ");
+ length += SIGNLEN;
+ }
+ else if (number == 0) {
+ if (ordinal) {
+ strcpy(stk, "zeroth");
+ length += 6; /* strlen("zeroth") */
+ }
+ else {
+ strcpy(stk, "zero");
+ length += 4; /* strlen("zero") */
+ }
+ }
+ for (;;) {
+ int count, temp;
+ char *t, *h, *d;
+ long value = number;
+
+ for (count = 0; value >= 1000; value /= 1000, count++)
+ ;
+
+ t = ds[value / 100];
+ if (ordinal && !count && (value % 10) == 0)
+ h = hsth[(value % 100) / 10];
+ else
+ h = hs[(value % 100) / 10];
+
+ if (ordinal && !count)
+ d = *h ? dsth[value % 10] : dsth[value % 20];
+ else
+ d = *h ? ds[value % 10] : ds[value % 20];
+
+ if (((!sign && length) || length > SIGNLEN) && (*t || *h || *d)) {
+ if (!ordinal || count || *h || *t) {
+ strcpy(stk + length, ", ");
+ length += 2;
+ }
+ else {
+ strcpy(stk + length, " ");
+ ++length;
+ }
+ }
+
+ if (*t) {
+ if (ordinal && !count && (value % 100) == 0)
+ temp = sprintf(stk + length, "%s hundredth", t);
+ else
+ temp = sprintf(stk + length, "%s hundred", t);
+ length += temp;
+ }
+
+ if (*h) {
+ if (*t) {
+ if (ordinal && !count) {
+ strcpy(stk + length, " ");
+ ++length;
+ }
+ else {
+ strcpy(stk + length, " and ");
+ length += 5; /* strlen(" and ") */
+ }
+ }
+ strcpy(stk + length, h);
+ length += strlen(h);
+ }
+
+ if (*d) {
+ if (*h) {
+ strcpy(stk + length, "-");
+ ++length;
+ }
+ else if (*t) {
+ if (ordinal && !count) {
+ strcpy(stk + length, " ");
+ ++length;
+ }
+ else {
+ strcpy(stk + length, " and ");
+ length += 5; /* strlen(" and ") */
+ }
+ }
+ strcpy(stk + length, d);
+ length += strlen(d);
+ }
+
+ if (!count)
+ break;
+ else
+ temp = count;
+
+ if (count > 1) {
+ value *= 1000;
+ while (--count)
+ value *= 1000;
+ number -= value;
+ }
+ else
+ number %= 1000;
+
+ if (ordinal && number == 0 && !*t && !*h)
+ temp = sprintf(stk + length, " %s", tsth[temp]);
+ else
+ temp = sprintf(stk + length, " %s", ts[temp]);
+ length += temp;
+
+ if (!number)
+ break;
+ }
+
+ return (LispWriteStr(stream, stk, length));
+}
+
+int
+LispFormatCharacter(LispObj *stream, LispObj *object,
+ int atsign, int collon)
+{
+ int length = 0;
+ int ch = SCHAR_VALUE(object);
+
+ if (atsign && !collon)
+ length += LispWriteStr(stream, "#\\", 2);
+ if ((atsign || collon) && (ch <= ' ' || ch == 0177)) {
+ char *name = LispChars[ch].names[0];
+
+ length += LispWriteStr(stream, name, strlen(name));
+ }
+ else
+ length += LispWriteChar(stream, ch);
+
+ return (length);
+}
+
+/* returns 1 if string size must grow, done inplace */
+static int
+float_string_inc(char *buffer, int offset)
+{
+ int i;
+
+ for (i = offset; i >= 0; i--) {
+ if (buffer[i] == '9')
+ buffer[i] = '0';
+ else if (buffer[i] != '.') {
+ ++buffer[i];
+ break;
+ }
+ }
+ if (i < 0) {
+ int length = strlen(buffer);
+
+ /* string size must change */
+ memmove(buffer + 1, buffer, length + 1);
+ buffer[0] = '1';
+
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+LispFormatFixedFloat(LispObj *stream, LispObj *object,
+ int atsign, int w, int *pd, int k, int overflowchar,
+ int padchar)
+{
+ char buffer[512], stk[64];
+ int sign, exponent, length, offset, d = pd ? *pd : FLOAT_PREC, again;
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0) {
+ exponent = k = 0;
+ strcpy(stk, "+0");
+ }
+ else
+ /* calculate format parameters, adjusting scale factor */
+ parse_double(stk, &exponent, value, d + 1 + k);
+
+ /* make sure k won't cause overflow */
+ if (k > 128)
+ k = 128;
+ else if (k < -128)
+ k = -128;
+
+ /* make sure d won't cause overflow */
+ if (d > 128)
+ d = 128;
+ else if (d < -128)
+ d = -128;
+
+ /* adjust scale factor, exponent is used as an index in stk */
+ exponent += k + 1;
+
+ /* how many bytes in float representation */
+ length = strlen(stk) - 1;
+
+ /* need to print a sign? */
+ sign = atsign || (stk[0] == '-');
+
+ /* format number, cannot overflow, as control variables were checked */
+ offset = 0;
+ if (sign)
+ buffer[offset++] = stk[0];
+ if (exponent > 0) {
+ if (exponent > length) {
+ memcpy(buffer + offset, stk + 1, length);
+ memset(buffer + offset + length, '0', exponent - length);
+ }
+ else
+ memcpy(buffer + offset, stk + 1, exponent);
+ offset += exponent;
+ buffer[offset++] = '.';
+ if (length > exponent) {
+ memcpy(buffer + offset, stk + 1 + exponent, length - exponent);
+ offset += length - exponent;
+ }
+ else
+ buffer[offset++] = '0';
+ }
+ else {
+ buffer[offset++] = '0';
+ buffer[offset++] = '.';
+ while (exponent < 0) {
+ buffer[offset++] = '0';
+ exponent++;
+ }
+ memcpy(buffer + offset, stk + 1, length);
+ offset += length;
+ }
+ buffer[offset] = '\0';
+
+ again = 0;
+fixed_float_check_again:
+ /* make sure only d digits are printed after decimal point */
+ if (d > 0) {
+ char *dptr = strchr(buffer, '.');
+
+ length = strlen(dptr) - 1;
+ /* check if need to remove excess digits */
+ if (length > d) {
+ int digit;
+
+ offset = (dptr - buffer) + 1 + d;
+ digit = buffer[offset];
+
+ /* remove extra digits */
+ buffer[offset] = '\0';
+
+ /* check if need to round */
+ if (!again && offset > 1 && isdigit(digit) && digit >= '5' &&
+ isdigit(buffer[offset - 1]) &&
+ float_string_inc(buffer, offset - 1))
+ ++offset;
+ }
+ /* check if need to add extra zero digits to fill space */
+ else if (length < d) {
+ offset += d - length;
+ for (++length; length <= d; length++)
+ dptr[length] = '0';
+ dptr[length] = '\0';
+ }
+ }
+ else {
+ /* no digits after decimal point */
+ int digit, inc = 0;
+ char *dptr = strchr(buffer, '.') + 1;
+
+ digit = *dptr;
+ if (!again && digit >= '5' && dptr >= buffer + 2 && isdigit(dptr[-2]))
+ inc = float_string_inc(buffer, dptr - buffer - 2);
+
+ offset = (dptr - buffer) + inc;
+ buffer[offset] = '\0';
+ }
+
+ /* if d was not specified, remove any extra zeros */
+ if (pd == NULL) {
+ while (offset > 2 && buffer[offset - 2] != '.' &&
+ buffer[offset - 1] == '0')
+ --offset;
+ buffer[offset] = '\0';
+ }
+
+ if (w > 0 && offset > w) {
+ /* first check if can remove extra fractional digits */
+ if (pd == NULL) {
+ char *ptr = strchr(buffer, '.') + 1;
+
+ if (ptr - buffer < w) {
+ d = w - (ptr - buffer);
+ goto fixed_float_check_again;
+ }
+ }
+
+ /* remove leading "zero" to save space */
+ if ((!sign && buffer[0] == '0') || (sign && buffer[1] == '0')) {
+ /* ending nul also copied */
+ memmove(buffer + sign, buffer + sign + 1, offset);
+ --offset;
+ }
+ /* remove leading '+' to "save" space */
+ if (offset > w && buffer[0] == '+') {
+ /* ending nul also copied */
+ memmove(buffer, buffer + 1, offset);
+ --offset;
+ }
+ }
+
+ /* if cannot represent number in given width */
+ if (overflowchar && offset > w) {
+ again = 1;
+ goto fixed_float_overflow;
+ }
+
+ length = 0;
+ /* print padding if required */
+ if (w > offset)
+ length += LispWriteChars(stream, padchar, w - offset);
+
+ /* print float number representation */
+ return (LispWriteStr(stream, buffer, offset) + length);
+
+fixed_float_overflow:
+ return (LispWriteChars(stream, overflowchar, w));
+}
+
+int
+LispFormatExponentialFloat(LispObj *stream, LispObj *object,
+ int atsign, int w, int *pd, int e, int k,
+ int overflowchar, int padchar, int exponentchar)
+{
+ return (LispDoFormatExponentialFloat(stream, object, atsign, w,
+ pd, e, k, overflowchar, padchar,
+ exponentchar, 1));
+}
+
+int
+LispDoFormatExponentialFloat(LispObj *stream, LispObj *object,
+ int atsign, int w, int *pd, int e, int k,
+ int overflowchar, int padchar, int exponentchar,
+ int format)
+{
+ char buffer[512], stk[64];
+ int sign, exponent, length, offset, d = pd ? *pd : FLOAT_PREC;
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0) {
+ exponent = 0;
+ k = 1;
+ strcpy(stk, "+0");
+ }
+ else
+ /* calculate format parameters, adjusting scale factor */
+ parse_double(stk, &exponent, value, d + k - 1);
+
+ /* set e to a value that won't overflow */
+ if (e > 16)
+ e = 16;
+
+ /* set k to a value that won't overflow */
+ if (k > 128)
+ k = 128;
+ else if (k < -128)
+ k = -128;
+
+ /* set d to a value that won't overflow */
+ if (d > 128)
+ d = 128;
+ else if (d < -128)
+ d = -128;
+
+ /* how many bytes in float representation */
+ length = strlen(stk) - 1;
+
+ /* need to print a sign? */
+ sign = atsign || (stk[0] == '-');
+
+ /* adjust number of digits after decimal point */
+ if (k > 0)
+ d -= k - 1;
+
+ /* adjust exponent, based on scale factor */
+ exponent -= k - 1;
+
+ /* format number, cannot overflow, as control variables were checked */
+ offset = 0;
+ if (sign)
+ buffer[offset++] = stk[0];
+ if (k > 0) {
+ if (k > length) {
+ memcpy(buffer + offset, stk + 1, length);
+ offset += length;
+ }
+ else {
+ memcpy(buffer + offset, stk + 1, k);
+ offset += k;
+ }
+ buffer[offset++] = '.';
+ if (length > k) {
+ memcpy(buffer + offset, stk + 1 + k, length - k);
+ offset += length - k;
+ }
+ else
+ buffer[offset++] = '0';
+ }
+ else {
+ int tmp = k;
+
+ buffer[offset++] = '0';
+ buffer[offset++] = '.';
+ while (tmp < 0) {
+ buffer[offset++] = '0';
+ tmp++;
+ }
+ memcpy(buffer + offset, stk + 1, length);
+ offset += length;
+ }
+
+ /* if format, then always add a sign to exponent */
+ buffer[offset++] = exponentchar;
+ if (format || exponent < 0)
+ buffer[offset++] = exponent < 0 ? '-' : '+';
+
+ /* XXX destroy stk contents */
+ sprintf(stk, "%%0%dd", e);
+ /* format scale factor*/
+ length = sprintf(buffer + offset, stk,
+ exponent < 0 ? -exponent : exponent);
+ /* check for overflow in exponent */
+ if (length > e && overflowchar)
+ goto exponential_float_overflow;
+ offset += length;
+
+ /* make sure only d digits are printed after decimal point */
+ if (d > 0) {
+ int currd;
+ char *dptr = strchr(buffer, '.'),
+ *eptr = strchr(dptr, exponentchar);
+
+ currd = eptr - dptr - 1;
+ length = strlen(eptr);
+
+ /* check if need to remove excess digits */
+ if (currd > d) {
+ int digit, dpos;
+
+ dpos = offset = (dptr - buffer) + 1 + d;
+ digit = buffer[offset];
+
+ memmove(buffer + offset, eptr, length + 1);
+ /* also copy ending nul character */
+
+ /* adjust offset to length of total string */
+ offset += length;
+
+ /* check if need to round */
+ if (dpos > 1 && isdigit(digit) && digit >= '5' &&
+ isdigit(buffer[dpos - 1]) &&
+ float_string_inc(buffer, dpos - 1))
+ ++offset;
+ }
+ /* check if need to add extra zero digits to fill space */
+ else if (pd && currd < d) {
+ memmove(eptr + d - currd, eptr, length + 1);
+ /* also copy ending nul character */
+
+ offset += d - currd;
+ for (++currd; currd <= d; currd++)
+ dptr[currd] = '0';
+ }
+ /* check if need to remove zeros */
+ else if (pd == NULL) {
+ int zeros = 1;
+
+ while (eptr[-zeros] == '0')
+ ++zeros;
+ if (eptr[-zeros] == '.')
+ --zeros;
+ if (zeros > 1) {
+ memmove(eptr - zeros + 1, eptr, length + 1);
+ offset -= zeros - 1;
+ }
+ }
+ }
+ else {
+ /* no digits after decimal point */
+ int digit, inc = 0;
+ char *dptr = strchr(buffer, '.'),
+ *eptr = strchr(dptr, exponentchar);
+
+ digit = dptr[1];
+
+ offset = (dptr - buffer) + 1;
+ length = strlen(eptr);
+ memmove(buffer + offset, eptr, length + 1);
+ /* also copy ending nul character */
+
+ if (digit >= '5' && dptr >= buffer + 2 &&
+ isdigit(dptr[-2]))
+ inc = float_string_inc(buffer, dptr - buffer - 2);
+
+ /* adjust offset to length of total string */
+ offset += length + inc;
+ }
+
+ if (w > 0 && offset > w) {
+ /* remove leading "zero" to save space */
+ if ((!sign && buffer[0] == '0') || (sign && buffer[1] == '0')) {
+ /* ending nul also copied */
+ memmove(buffer + sign, buffer + sign + 1, offset);
+ --offset;
+ }
+ /* remove leading '+' to "save" space */
+ if (offset > w && buffer[0] == '+') {
+ /* ending nul also copied */
+ memmove(buffer, buffer + 1, offset);
+ --offset;
+ }
+ }
+
+ /* if cannot represent number in given width */
+ if (overflowchar && offset > w)
+ goto exponential_float_overflow;
+
+ length = 0;
+ /* print padding if required */
+ if (w > offset)
+ length += LispWriteChars(stream, padchar, w - offset);
+
+ /* print float number representation */
+ return (LispWriteStr(stream, buffer, offset) + length);
+
+exponential_float_overflow:
+ return (LispWriteChars(stream, overflowchar, w));
+}
+
+int
+LispFormatGeneralFloat(LispObj *stream, LispObj *object,
+ int atsign, int w, int *pd, int e, int k,
+ int overflowchar, int padchar, int exponentchar)
+{
+ char stk[64];
+ int length, exponent, n, dd, ee, ww, d = pd ? *pd : FLOAT_PREC;
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0) {
+ exponent = 0;
+ n = 0;
+ d = 1;
+ strcpy(stk, "+0");
+ }
+ else {
+ /* calculate format parameters, adjusting scale factor */
+ parse_double(stk, &exponent, value, d + k - 1);
+ n = exponent + 1;
+ }
+
+ /* Let ee equal e+2, or 4 if e is omitted. */
+ if (e)
+ ee = e + 2;
+ else
+ ee = 4;
+
+ /* Let ww equal w-ee, or nil if w is omitted. */
+ if (w)
+ ww = w - ee;
+ else
+ ww = 0;
+
+ dd = d - n;
+ if (d >= dd && dd >= 0) {
+ length = LispFormatFixedFloat(stream, object, atsign, ww,
+ &dd, 0, overflowchar, padchar);
+
+ /* ~ee@T */
+ length += LispWriteChars(stream, padchar, ee);
+ }
+ else
+ length = LispFormatExponentialFloat(stream, object, atsign,
+ w, pd, e, k, overflowchar,
+ padchar, exponentchar);
+
+ return (length);
+}
+
+int
+LispFormatDollarFloat(LispObj *stream, LispObj *object,
+ int atsign, int collon, int d, int n, int w, int padchar)
+{
+ char buffer[512], stk[64];
+ int sign, exponent, length, offset;
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0) {
+ exponent = 0;
+ strcpy(stk, "+0");
+ }
+ else
+ /* calculate format parameters, adjusting scale factor */
+ parse_double(stk, &exponent, value, d == 0 ? FLOAT_PREC : d + 1);
+
+ /* set d to a "sane" value */
+ if (d > 128)
+ d = 128;
+
+ /* set n to a "sane" value */
+ if (n > 128)
+ n = 128;
+
+ /* use exponent as index in stk */
+ ++exponent;
+
+ /* don't put sign in buffer,
+ * if collon specified, must go before padding */
+ sign = atsign || (stk[0] == '-');
+
+ offset = 0;
+
+ /* pad with zeros if required */
+ if (exponent > 0)
+ n -= exponent;
+ while (n > 0) {
+ buffer[offset++] = '0';
+ n--;
+ }
+
+ /* how many bytes in float representation */
+ length = strlen(stk) - 1;
+
+ if (exponent > 0) {
+ if (exponent > length) {
+ memcpy(buffer + offset, stk + 1, length);
+ memset(buffer + offset + length, '0', exponent - length);
+ }
+ else
+ memcpy(buffer + offset, stk + 1, exponent);
+ offset += exponent;
+ buffer[offset++] = '.';
+ if (length > exponent) {
+ memcpy(buffer + offset, stk + 1 + exponent, length - exponent);
+ offset += length - exponent;
+ }
+ else
+ buffer[offset++] = '0';
+ }
+ else {
+ if (n > 0)
+ buffer[offset++] = '0';
+ buffer[offset++] = '.';
+ while (exponent < 0) {
+ buffer[offset++] = '0';
+ exponent++;
+ }
+ memcpy(buffer + offset, stk + 1, length);
+ offset += length;
+ }
+ buffer[offset] = '\0';
+
+ /* make sure only d digits are printed after decimal point */
+ if (d > 0) {
+ char *dptr = strchr(buffer, '.');
+
+ length = strlen(dptr) - 1;
+ /* check if need to remove excess digits */
+ if (length > d) {
+ int digit;
+
+ offset = (dptr - buffer) + 1 + d;
+ digit = buffer[offset];
+
+ /* remove extra digits */
+ buffer[offset] = '\0';
+
+ /* check if need to round */
+ if (offset > 1 && isdigit(digit) && digit >= '5' &&
+ isdigit(buffer[offset - 1]) &&
+ float_string_inc(buffer, offset - 1))
+ ++offset;
+ }
+ /* check if need to add extra zero digits to fill space */
+ else if (length < d) {
+ offset += d - length;
+ for (++length; length <= d; length++)
+ dptr[length] = '0';
+ dptr[length] = '\0';
+ }
+ }
+ else {
+ /* no digits after decimal point */
+ int digit, inc = 0;
+ char *dptr = strchr(buffer, '.') + 1;
+
+ digit = *dptr;
+ if (digit >= '5' && dptr >= buffer + 2 && isdigit(dptr[-2]))
+ inc = float_string_inc(buffer, dptr - buffer - 2);
+
+ offset = (dptr - buffer) + inc;
+ buffer[offset] = '\0';
+ }
+
+ length = 0;
+ if (sign) {
+ ++offset;
+ if (atsign && collon)
+ length += LispWriteChar(stream, value >= 0.0 ? '+' : '-');
+ }
+
+ /* print padding if required */
+ if (w > offset)
+ length += LispWriteChars(stream, padchar, w - offset);
+
+ if (atsign && !collon)
+ length += LispWriteChar(stream, value >= 0.0 ? '+' : '-');
+
+ /* print float number representation */
+ return (LispWriteStr(stream, buffer, offset) + length);
+}
diff --git a/lisp/write.h b/lisp/write.h
new file mode 100644
index 0000000..994a374
--- /dev/null
+++ b/lisp/write.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/write.h,v 1.9 2002/12/04 05:27:59 paulo Exp $ */
+
+#ifndef Lisp_write_h
+#define Lisp_write_h
+
+#include "io.h"
+
+/*
+ * Prototypes
+ */
+void LispWriteInit(void);
+
+LispObj *Lisp_FreshLine(LispBuiltin*);
+LispObj *Lisp_Prin1(LispBuiltin*);
+LispObj *Lisp_Princ(LispBuiltin*);
+LispObj *Lisp_Print(LispBuiltin*);
+LispObj *Lisp_Terpri(LispBuiltin*);
+LispObj *Lisp_Write(LispBuiltin*);
+LispObj *Lisp_WriteChar(LispBuiltin*);
+LispObj *Lisp_WriteLine(LispBuiltin*);
+LispObj *Lisp_WriteString(LispBuiltin*);
+
+int LispGetColumn(LispObj*);
+
+int LispWriteChar(LispObj*, int);
+int LispWriteChars(LispObj*, int, int);
+int LispWriteStr(LispObj*, char*, long);
+
+ /* write any lisp object to stream */
+int LispWriteObject(LispObj*, LispObj*);
+
+/* formatted output */
+ /* object must be an integer */
+int LispFormatInteger(LispObj*, LispObj*, int, int, int, int, int, int, int);
+ /* must be in range 1 to 3999 for new roman, 1 to 4999 for old roman */
+int LispFormatRomanInteger(LispObj*, long, int);
+ /* must be in range -9999999 to 9999999 */
+int LispFormatEnglishInteger(LispObj*, long, int);
+ /* object must be a character */
+int LispFormatCharacter(LispObj*, LispObj*, int, int);
+ /* object must be a float */
+int LispFormatFixedFloat(LispObj*, LispObj*, int, int, int*, int, int, int);
+ /* object must be a float */
+int LispFormatExponentialFloat(LispObj*, LispObj*,
+ int, int, int*, int, int, int, int, int);
+ /* object must be a float */
+int LispFormatGeneralFloat(LispObj*, LispObj*, int,
+ int, int*, int, int, int, int, int);
+int LispFormatDollarFloat(LispObj*, LispObj*, int, int, int, int, int, int);
+
+#endif /* Lisp_write_h */
diff --git a/lisp/xedit.c b/lisp/xedit.c
new file mode 100644
index 0000000..9ef7f8f
--- /dev/null
+++ b/lisp/xedit.c
@@ -0,0 +1,1636 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/xedit.c,v 1.24 2003/01/13 03:57:59 paulo Exp $ */
+
+#include "../xedit.h"
+#include <X11/Xaw/TextSrcP.h> /* Needs some private definitions */
+#include <X11/Xaw/TextSinkP.h> /* Also needs private definitions... */
+#include <X11/Xmu/Xmu.h>
+#define XEDIT_LISP_PRIVATE
+#include "xedit.h"
+#include <signal.h>
+
+/* Initialize to enter lisp */
+#define LISP_SETUP() \
+ int lisp__running = lisp__data.running
+
+/* XXX Maybe should use ualarm or better, setitimer, but one
+ * second seens good enough to check for interrupts */
+
+#define ENABLE_SIGALRM() \
+ old_sigalrm = signal(SIGALRM, SigalrmHandler); \
+ alarm(1)
+
+#define DISABLE_SIGALRM() \
+ alarm(0); \
+ signal(SIGALRM, old_sigalrm)
+
+/* Enter lisp */
+#define LISP_ENTER() \
+ if (!lisp__running) { \
+ lisp__data.running = 1; \
+ XFlush(XtDisplay(textwindow)); \
+ ENABLE_SIGALRM(); \
+ if (sigsetjmp(lisp__data.jmp, 1) != 0) { \
+ DISABLE_SIGALRM(); \
+ lisp__data.running = 0; \
+ return; \
+ } \
+ }
+
+/* Leave lisp */
+#define LISP_LEAVE() \
+ if (!lisp__running) { \
+ DISABLE_SIGALRM(); \
+ LispTopLevel(); \
+ lisp__data.running = 0; \
+ }
+
+/*
+ * Types
+ */
+typedef struct {
+ XawTextPosition left, right;
+ XrmQuark property;
+} EntityInfo;
+
+/*
+ * Prototypes
+ */
+static Bool ControlGPredicate(Display*, XEvent*, XPointer);
+static ssize_t WriteToStdout(int, const void*, size_t);
+static ssize_t WriteToStderr(int, const void*, size_t);
+static ssize_t WrapWrite(Widget, const void*, size_t);
+static void XeditUpdateModeInfos(void);
+static void XeditPrint(Widget, LispObj*, int);
+static void XeditInteractiveCallback(Widget, XtPointer, XtPointer);
+static void XeditIndentationCallback(Widget, XtPointer, XtPointer);
+static LispObj *XeditCharAt(LispBuiltin*, int);
+static LispObj *XeditSearch(LispBuiltin*, XawTextScanDirection);
+
+/*
+ * Initialization
+ */
+#ifdef SIGNALRETURNSINT
+static int (*old_sigalrm)(int);
+#else
+static void (*old_sigalrm)(int);
+#endif
+
+EditModeInfo *mode_infos;
+Cardinal num_mode_infos;
+
+static LispObj *Oauto_modes, *Oauto_mode, *Osyntax_highlight, *Osyntable_indent;
+
+/* Just to make calling interactive reparse easier */
+static LispObj interactive_arguments[4];
+
+static LispObj *justify_modes[4];
+static LispObj *wrap_modes[3];
+static LispObj *scan_types[5];
+static LispObj *scan_directions[2];
+static LispObj execute_stream;
+static LispString execute_string;
+static LispObj result_stream;
+static LispString result_string;
+static XawTextPropertyList **property_lists;
+static Cardinal num_property_lists;
+
+/* Some hacks to (at lest try to) avoid problems reentering Xlib while
+ * testing for user interrupts */
+static volatile int disable_timeout, request_timeout;
+
+extern int pagesize;
+
+static LispBuiltin xeditbuiltins[] = {
+ {LispFunction, Xedit_AddEntity, "add-entity offset length identifier"},
+ {LispFunction, Xedit_AutoFill, "auto-fill &optional value"},
+ {LispFunction, Xedit_Background, "background &optional color"},
+ {LispFunction, Xedit_CharAfter, "char-after &optional offset"},
+ {LispFunction, Xedit_CharBefore, "char-before &optional offset"},
+ {LispFunction, Xedit_ClearEntities, "clear-entities left right"},
+ {LispFunction, Xedit_ConvertPropertyList, "convert-property-list name definition"},
+ {LispFunction, Xedit_Font, "font &optional font"},
+ {LispFunction, Xedit_Foreground, "foreground &optional color"},
+ {LispFunction, Xedit_GotoChar, "goto-char offset"},
+ {LispFunction, Xedit_HorizontalScrollbar, "horizontal-scrollbar &optional state"},
+ {LispFunction, Xedit_Insert, "insert text"},
+ {LispFunction, Xedit_Justification, "justification &optional value"},
+ {LispFunction, Xedit_LeftColumn, "left-column &optional left"},
+ {LispFunction, Xedit_Point, "point"},
+ {LispFunction, Xedit_PointMax, "point-max"},
+ {LispFunction, Xedit_PointMin, "point-min"},
+ {LispFunction, Xedit_PropertyList, "property-list &optional value"},
+ {LispFunction, Xedit_ReadText, "read-text offset length"},
+ {LispFunction, Xedit_ReplaceText, "replace-text left right text"},
+ {LispFunction, Xedit_RightColumn, "right-column &optional right"},
+ {LispFunction, Xedit_Scan, "scan offset type direction &key count include"},
+ {LispFunction, Xedit_SearchBackward, "search-backward string &optional offset ignore-case"},
+ {LispFunction, Xedit_SearchForward, "search-forward string &optional offset ignore-case"},
+ {LispFunction, Xedit_VerticalScrollbar, "vertical-scrollbar &optional state"},
+ {LispFunction, Xedit_WrapMode, "wrap-mode &optional value"},
+
+ /* This should be available from elsewhere at some time... */
+ {LispFunction, Xedit_XrmStringToQuark, "xrm-string-to-quark string"},
+};
+
+/*
+ * Implementation
+ */
+/*ARGUSED*/
+static Bool
+ControlGPredicate(Display *display, XEvent *event, XPointer arguments)
+{
+ char buffer[2];
+
+ return ((event->type == KeyPress || event->type == KeyRelease) &&
+ (event->xkey.state & ControlMask) &&
+ XLookupString(&(event->xkey), buffer, sizeof(buffer), NULL, NULL) &&
+ buffer[0] == '\a');
+}
+
+/*ARGSUSED*/
+static
+#ifdef SIGNALRETURNSINT
+int
+#else
+void
+#endif
+SigalrmHandler(int signum)
+{
+ XEvent event;
+
+ if (disable_timeout) {
+ request_timeout = 1;
+ return;
+ }
+
+ /* Check if user pressed C-g */
+ if (XCheckIfEvent(XtDisplay(textwindow), &event, ControlGPredicate, NULL)) {
+ XPutBackEvent(XtDisplay(textwindow), &event);
+ alarm(0);
+ /* Tell a signal was received, print message for SIGINT */
+ LispSignal(SIGINT);
+ }
+ else
+ alarm(1);
+#ifdef SIGNALRETURNSINT
+ return (0);
+#endif
+}
+
+static ssize_t
+WrapWrite(Widget output, const void *buffer, size_t nbytes)
+{
+ XawTextBlock block;
+ XawTextPosition position;
+
+ disable_timeout = 1;
+ position = XawTextGetInsertionPoint(output);
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ block.length = nbytes;
+ block.ptr = (String)buffer;
+ XawTextReplace(output, position, position, &block);
+ XawTextSetInsertionPoint(output, position + block.length);
+ disable_timeout = 0;
+
+ if (request_timeout) {
+ XFlush(XtDisplay(output));
+ request_timeout = 0;
+ SigalrmHandler(SIGALRM);
+ }
+
+ return ((ssize_t)nbytes);
+}
+
+static ssize_t
+WriteToStdout(int fd, const void *buffer, size_t nbytes)
+{
+ return (WrapWrite(textwindow, buffer, nbytes));
+}
+
+static ssize_t
+WriteToStderr(int fd, const void *buffer, size_t nbytes)
+{
+ return (WrapWrite(messwidget, buffer, nbytes));
+}
+
+void
+LispXeditInitialize(void)
+{
+ int i;
+ char *string;
+ LispObj *xedit, *list, *savepackage;
+
+ LispSetFileWrite(Stdout, WriteToStdout);
+ LispSetFileWrite(Stderr, WriteToStderr);
+
+ justify_modes[0] = KEYWORD("LEFT");
+ justify_modes[1] = KEYWORD("RIGHT");
+ justify_modes[2] = KEYWORD("CENTER");
+ justify_modes[3] = KEYWORD("FULL");
+
+ wrap_modes[0] = KEYWORD("NEVER");
+ wrap_modes[1] = KEYWORD("LINE");
+ wrap_modes[2] = KEYWORD("WORD");
+
+ scan_types[0] = KEYWORD("POSITIONS");
+ scan_types[1] = KEYWORD("WHITE-SPACE");
+ scan_types[2] = KEYWORD("EOL");
+ scan_types[3] = KEYWORD("PARAGRAPH");
+ scan_types[4] = KEYWORD("ALL");
+ scan_types[5] = KEYWORD("ALPHA-NUMERIC");
+
+ scan_directions[0] = justify_modes[0];
+ scan_directions[1] = justify_modes[1];
+
+ /* Remember value of current package */
+ savepackage = PACKAGE;
+
+ /* Create the XEDIT package */
+ xedit = LispNewPackage(STRING("XEDIT"), NIL);
+
+ /* Update list of packages */
+ PACK = CONS(xedit, PACK);
+
+ /* Temporarily switch to the XEDIT package */
+ lisp__data.pack = lisp__data.savepack = xedit->data.package.package;
+ PACKAGE = xedit;
+
+ /* Add XEDIT builtin functions */
+ for (i = 0; i < sizeof(xeditbuiltins) / sizeof(xeditbuiltins[0]); i++)
+ LispAddBuiltinFunction(&xeditbuiltins[i]);
+
+ /* Create these objects in the xedit package */
+ Oauto_modes = STATIC_ATOM("*AUTO-MODES*");
+ Oauto_mode = STATIC_ATOM("AUTO-MODE");
+ Osyntax_highlight = STATIC_ATOM("SYNTAX-HIGHLIGHT");
+ Osyntable_indent = STATIC_ATOM("SYNTABLE-INDENT");
+
+ /* Import symbols from the LISP and EXT packages */
+ for (list = PACK; CONSP(list); list = CDR(list)) {
+ string = THESTR(CAR(list)->data.package.name);
+ if (strcmp(string, "LISP") == 0 || strcmp(string, "EXT") == 0)
+ LispUsePackage(CAR(list));
+ }
+
+ /* Restore previous package */
+ lisp__data.pack = savepackage->data.package.package;
+ PACKAGE = savepackage;
+
+ /* Initialize helper static objects used when executing expressions */
+ execute_stream.type = LispStream_t;
+ execute_stream.data.stream.source.string = &execute_string;
+ execute_stream.data.stream.pathname = NIL;
+ execute_stream.data.stream.type = LispStreamString;
+ execute_stream.data.stream.readable = 1;
+ execute_stream.data.stream.writable = 0;
+ execute_string.output = 0;
+ result_stream.type = LispStream_t;
+ result_stream.data.stream.source.string = &result_string;
+ result_stream.data.stream.pathname = NIL;
+ result_stream.data.stream.type = LispStreamString;
+ result_stream.data.stream.readable = 0;
+ result_stream.data.stream.writable = 1;
+ result_string.string = XtMalloc(pagesize);
+ result_string.space = pagesize;
+
+ /* Initialize interactive edition function arguments */
+ /* first argument is syntax table */
+ interactive_arguments[0].type = LispCons_t;
+ interactive_arguments[0].data.cons.cdr = &interactive_arguments[1];
+ /* second argument is where to start reparsing */
+ interactive_arguments[1].type = LispCons_t;
+ interactive_arguments[1].data.cons.cdr = &interactive_arguments[2];
+ /* third argument is where to stop reparsing */
+ interactive_arguments[2].type = LispCons_t;
+ interactive_arguments[2].data.cons.cdr = &interactive_arguments[3];
+ /* fourth argument is interactive flag */
+ interactive_arguments[3].type = LispCons_t;
+ interactive_arguments[3].data.cons.car = T;
+ interactive_arguments[3].data.cons.cdr = NIL;
+
+ /* Load extra functions and data type definitions */
+ EXECUTE("(require \"xedit\")");
+
+
+ /*
+ * This assumes that the *auto-modes* variable is a list where every
+ * item has the format:
+ * (regexp string-desc load-file-desc . symbol-name)
+ * Minimal error checking is done.
+ */
+
+ if (Oauto_modes->data.atom->a_object) {
+ LispObj *desc, *modes = Oauto_modes->data.atom->property->value;
+
+ for (; CONSP(modes); modes = CDR(modes)) {
+ list = CAR(modes);
+
+ desc = NIL;
+ for (i = 0; i < 3 && CONSP(list); i++, list = CDR(list)) {
+ if (i == 1)
+ desc = CAR(list);
+ }
+ if (i == 3 && STRINGP(desc)) {
+ mode_infos = (EditModeInfo*)
+ XtRealloc((XtPointer)mode_infos, sizeof(EditModeInfo) *
+ (num_mode_infos + 1));
+ mode_infos[num_mode_infos].desc = XtNewString(THESTR(desc));
+ mode_infos[num_mode_infos].symbol = list;
+ mode_infos[num_mode_infos].syntax = NULL;
+ ++num_mode_infos;
+ }
+ }
+ }
+}
+
+static void
+XeditUpdateModeInfos(void)
+{
+ int i;
+
+ for (i = 0; i < num_mode_infos; i++) {
+ if (mode_infos[i].symbol &&
+ mode_infos[i].syntax == NULL &&
+ XSYMBOLP(mode_infos[i].symbol) &&
+ mode_infos[i].symbol->data.atom->a_object)
+ mode_infos[i].syntax =
+ mode_infos[i].symbol->data.atom->property->value;
+ }
+}
+
+void
+XeditLispExecute(Widget output, XawTextPosition left, XawTextPosition right)
+{
+ GC_ENTER();
+ LISP_SETUP();
+ int alloced, return_count;
+ XawTextBlock block;
+ XawTextPosition position;
+ char *string, *ptr;
+ LispObj *result, *code, *_cod, *returns;
+
+ LISP_ENTER();
+
+ position = left;
+ XawTextSourceRead(XawTextGetSource(textwindow), left, &block, right - left);
+ if (block.length < right - left) {
+ alloced = 1;
+ string = ptr = LispMalloc(right - left);
+ memcpy(ptr, block.ptr, block.length);
+ position = left + block.length;
+ ptr += block.length;
+ for (; position < right;) {
+ XawTextSourceRead(XawTextGetSource(textwindow),
+ position, &block, right - position);
+ memcpy(ptr, block.ptr, block.length);
+ position += block.length;
+ ptr += block.length;
+ }
+ }
+ else {
+ alloced = 0;
+ string = block.ptr;
+ }
+
+ execute_string.string = string;
+ execute_string.length = right - left;
+ execute_string.input = 0;
+ LispPushInput(&execute_stream);
+ _cod = COD;
+ result = NIL;
+ if ((code = LispRead()) != NULL)
+ result = EVAL(code);
+ COD = _cod;
+ LispPopInput(&execute_stream);
+
+ returns = NIL;
+ if (RETURN_COUNT > 0) {
+ GC_PROTECT(result);
+ returns = _cod = CONS(RETURN(0), NIL);
+ GC_PROTECT(returns);
+ for (return_count = 1; return_count < RETURN_COUNT; return_count++) {
+ RPLACD(_cod, CONS(RETURN(return_count), NIL));
+ _cod = CDR(_cod);
+ }
+ }
+ LispFflush(Stdout);
+ LispUpdateResults(code, result);
+ if (RETURN_COUNT >= 0) {
+ XeditPrint(output, result, 1);
+ for (; CONSP(returns); returns = CDR(returns))
+ XeditPrint(output, CAR(returns), 0);
+ }
+
+ if (alloced)
+ LispFree(string);
+ GC_LEAVE();
+
+ LISP_LEAVE();
+}
+
+static void
+XeditPrint(Widget output, LispObj *object, int newline)
+{
+ XawTextBlock block;
+ XawTextPosition position;
+
+ result_string.length = result_string.output = 0;
+ if (newline) {
+ position = XawTextGetInsertionPoint(output);
+ if (position != XawTextSourceScan(XawTextGetSource(output),
+ position, XawstEOL,
+ XawsdLeft, 1, False))
+ LispSputc(&result_string, '\n');
+ }
+ LispWriteObject(&result_stream, object);
+ LispSputc(&result_string, '\n');
+
+ position = XawTextGetInsertionPoint(output);
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ block.length = result_string.length;
+ block.ptr = result_string.string;
+ XawTextReplace(output, position, position, &block);
+ XawTextSetInsertionPoint(output, position + block.length);
+}
+
+/*
+ * This function is defined here to avoid exporting all the lisp interfaces
+ * to the core xedit code.
+ */
+void
+XeditLispSetEditMode(xedit_flist_item *item, LispObj *symbol)
+{
+ GC_ENTER();
+ LISP_SETUP();
+ LispObj *syntax, *name;
+
+ item->xldata = (XeditLispData*)XtCalloc(1, sizeof(XeditLispData));
+
+ LISP_ENTER();
+
+ /* Create an object that represents the buffer filename.
+ * Note that the entire path is passed to the auto-mode
+ * function, so that directory names may be also be used
+ * when determining a file type. */
+ name = STRING(item->filename);
+ GC_PROTECT(name);
+
+ /* Call the AUTO-MODE function to check if there is a
+ * syntax definition for the file being loaded */
+ if (symbol == NULL)
+ syntax = APPLY1(Oauto_mode, name);
+ else
+ syntax = APPLY2(Oauto_mode, name, symbol);
+
+ /* Don't need the name object anymore */
+ GC_LEAVE();
+
+ if (syntax != NIL) {
+ Arg arg[1];
+ LispObj arguments;
+ XawTextPropertyList *property_list;
+
+ item->xldata->syntax = syntax;
+
+ /* Apply the syntax highlight to the current buffer */
+ arguments.type = LispCons_t;
+ arguments.data.cons.car = syntax;
+ arguments.data.cons.cdr = NIL;
+ LispFuncall(Osyntax_highlight, &arguments, 1);
+
+ /* The previous call added the property list to the widget,
+ * remember it when switching sources. */
+ XtSetArg(arg[0], XawNtextProperties, &property_list);
+ XtGetValues(XawTextGetSink(textwindow), arg, 1);
+ item->properties = property_list;
+
+ /* Add callback for interactive changes */
+ XtAddCallback(item->source, XtNpropertyCallback,
+ XeditInteractiveCallback, item->xldata);
+
+ /* Update information as a new file may have been loaded */
+ XeditUpdateModeInfos();
+ }
+ else
+ item->properties = NULL;
+
+ LISP_LEAVE();
+}
+
+void
+XeditLispUnsetEditMode(xedit_flist_item *item)
+{
+ if (item->xldata) {
+ XtRemoveCallback(item->source, XtNpropertyCallback,
+ XeditInteractiveCallback, item->xldata);
+ XtFree((XtPointer)item->xldata);
+ item->xldata = NULL;
+ }
+}
+
+#define MAX_INFOS 32
+/*
+ * This callback tries to do it's best in generating correct output while
+ * also doing minimal work/redrawing of the screen. It probably will fail
+ * for some syntax-definitions, or will just not properly repaint the
+ * screen. In the later case, just press Ctrl+L.
+ * There isn't yet any command to force reparsing of some regions, and if
+ * the parser becomes confused, you may need to go to a line, press a space
+ * and undo, just to force it to reparse the line, and possibly some extra
+ * lines until the parser thinks the display is in sync.
+ * Sometimes it will repaint a lot more of text than what is being requested
+ * by this callback, this should be fixed at some time, as for certain cases
+ * it is also required some redesign in the Xaw interface.
+ */
+static void
+XeditInteractiveCallback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ LISP_SETUP();
+ XeditLispData *data = (XeditLispData*)client_data;
+ LispObj *syntax = data->syntax;
+ XawTextPropertyInfo *info = (XawTextPropertyInfo*)call_data;
+ LispObj *result, *syntable;
+ XawTextAnchor *anchor;
+ XawTextEntity *entity;
+ XawTextPosition first, last, left, right, begin, next, tmp, position;
+ int i, j, indent;
+ TextSrcObject src = (TextSrcObject)w;
+ EntityInfo oinfo[MAX_INFOS], ninfo[MAX_INFOS];
+ XrmQuark props[MAX_INFOS];
+ int num_oinfo, num_ninfo, num_props;
+ XmuScanline *clip, *oclip, *nclip;
+ XmuSegment segment, *seg;
+
+ if (data->disable_highlight)
+ return;
+
+ LISP_ENTER();
+
+ first = XawTextSourceScan(w, 0, XawstAll, XawsdLeft, 1, True);
+ last = XawTextSourceScan(w, 0, XawstAll, XawsdRight, 1, True);
+
+ left = info->left;
+ right = left + info->block->length;
+
+ /* For now, only call the indent hook if a single character was typed */
+ indent = (info->right == left) && (right == left + 1);
+
+ /* Always reparse full lines */
+ left = begin = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 1, False);
+ right = next = XawTextSourceScan(w, right, XawstEOL, XawsdRight, 1, False);
+
+
+ /* Check properties in the modified text. If a complex nested syntax
+ * table was parsed, the newline has it's default property, so, while
+ * the newline has a property, backup a line to make sure everything is
+ * properly parsed.
+ * Maybe should limit the number of backuped lines, but if the parsing
+ * becomes noticeable slow, better to rethink the syntax definition. */
+ while (left > first) {
+ position = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 1, True);
+ if (XawTextSourceAnchorAndEntity(w, position, &anchor, &entity))
+ left = XawTextSourceScan(w, left, XawstEOL, XawsdLeft, 2, False);
+ else
+ break;
+ }
+
+ /* While the newline after the right position has a "hidden" property,
+ * keep incrementing a line to be reparsed. */
+ while (right < last) {
+ if (XawTextSourceAnchorAndEntity(w, right, &anchor, &entity))
+ right = XawTextSourceScan(w, right, XawstEOL, XawsdRight, 2, False);
+ else
+ break;
+ }
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define STORE_STATE(count, info, from, to) \
+ (count) = 0; \
+ if ((anchor = XawTextSourceFindAnchor(w, (from))) != NULL) { \
+ entity = anchor->entities; \
+ /* Find first entity in the region to parse */ \
+ while (entity && \
+ anchor->position + entity->offset + entity->length <= \
+ (from)) \
+ entity = entity->next; \
+ /* Loop storing information */ \
+ while (entity && \
+ (position = anchor->position + entity->offset) < (to)) { \
+ (info)[(count)].left = MAX(position, (from)); \
+ position += entity->length; \
+ (info)[(count)].right = MIN(position, (to)); \
+ (info)[(count)].property = entity->property; \
+ /* If the changes are so complex, user need press Ctrl+L */ \
+ if (++(count) >= MAX_INFOS) \
+ break; \
+ if ((entity = entity->next) == NULL && \
+ (anchor = XawTextSourceNextAnchor(w, anchor)) != NULL) \
+ entity = anchor->entities; \
+ } \
+ }
+
+ /* Remember old state */
+ STORE_STATE(num_oinfo, oinfo, begin, right);
+
+ /* Reparse the lines in the modified/edited range of text */
+ interactive_arguments[0].data.cons.car = syntax;
+ interactive_arguments[1].data.cons.car = FIXNUM(left);
+ interactive_arguments[2].data.cons.car = FIXNUM(right);
+ result = APPLY(Osyntax_highlight, &interactive_arguments[0]);
+ /* Indent table is the second return value */
+ if (RETURN_COUNT)
+ syntable = RETURN(0);
+ else
+ syntable = NIL;
+
+ /* This normally is the same value as right, but the parser may have
+ * continued when the syntax table stack did not finish. */
+ if (FIXNUMP(result))
+ right = FIXNUM_VALUE(result);
+
+ LISP_LEAVE();
+
+ /* Check what have changed */
+ STORE_STATE(num_ninfo, ninfo, begin, right);
+
+ /* Initialize to redraw everything. */
+ clip = XmuNewScanline(0, begin, right);
+
+#define CLIP_MASK(mask, from, to) \
+ if ((from) < (to)) { \
+ segment.x1 = (from); \
+ segment.x2 = (to); \
+ XmuScanlineOrSegment((mask), &segment); \
+ }
+
+ oclip = XmuNewScanline(0, 0, 0);
+ nclip = XmuNewScanline(0, 0, 0);
+
+#define CLIP_DEFAULT(mask, from, info, num_info) \
+ for (tmp = (from), i = 0; i < (num_info); i++) { \
+ CLIP_MASK((mask), tmp, (info)[i].left); \
+ tmp = (info)[i].right; \
+ }
+
+ /* First generate masks of regions with the default property */
+ CLIP_DEFAULT(oclip, begin, oinfo, num_oinfo);
+ CLIP_DEFAULT(nclip, begin, ninfo, num_ninfo);
+
+ /* Store unchanged region in oclip */
+ XmuScanlineAnd(oclip, nclip);
+
+ /* Don't need to redraw the region in oclip */
+ XmuScanlineXor(clip, oclip);
+
+#define LIST_PROPERTIES(prop, num_prop, info, num_info) \
+ (num_prop) = 0; \
+ for (i = 0; i < (num_info); i++) { \
+ for (j = 0; j < (num_prop); j++) \
+ if ((prop)[j] == (info)[i].property) \
+ break; \
+ if (j == (num_prop)) \
+ (prop)[(num_prop)++] = (info)[i].property; \
+ }
+
+ /* Prepare to generate masks of regions of text with defined properties */
+ LIST_PROPERTIES(props, num_props, oinfo, num_oinfo);
+
+#define CLIP_PROPERTY(mask, prop, info, num_info) \
+ for (j = 0; j < (num_info); j++) { \
+ if ((info)[j].property == (prop)) { \
+ CLIP_MASK((mask), (info)[j].left, (info)[j].right); \
+ } \
+ }
+
+ /* Only care about the old properties, new ones need to be redrawn */
+ for (i = 0; i < num_props; i++) {
+ XrmQuark property = props[i];
+
+ /* Reset oclip and nclip */
+ XmuScanlineXor(oclip, oclip);
+ XmuScanlineXor(nclip, nclip);
+
+ /* Generate masks */
+ CLIP_PROPERTY(oclip, property, oinfo, num_oinfo);
+ CLIP_PROPERTY(nclip, property, ninfo, num_ninfo);
+
+ /* Store unchanged region in oclip */
+ XmuScanlineAnd(oclip, nclip);
+
+ /* Don't need to redraw the region in oclip */
+ XmuScanlineXor(clip, oclip);
+ XmuOptimizeScanline(clip);
+ }
+
+ XmuDestroyScanline(oclip);
+ XmuDestroyScanline(nclip);
+
+ /* Tell Xaw that need update some regions */
+ for (seg = clip->segment; seg; seg = seg->next) {
+ for (i = 0; i < src->textSrc.num_text; i++)
+ /* This really should have an exported interface... */
+ _XawTextNeedsUpdating((TextWidget)(src->textSrc.text[i]),
+ seg->x1, seg->x2 + (seg->x2 > next));
+ }
+ XmuDestroyScanline(clip);
+
+ data->syntable = syntable;
+ /* XXX check lisp__running to know if at the toplevel parsing state */
+ if (indent && syntable != NIL && !lisp__running &&
+ /* Doing an undo, probably will need an exported interface for this
+ * case. Should not change the text now. */
+ (!src->textSrc.enable_undo || !src->textSrc.undo_state))
+ XtAddCallback(textwindow, XtNpositionCallback,
+ XeditIndentationCallback, data);
+}
+
+/*
+ * This callback is called if the syntax table where the cursor is located
+ * defines an indentation function.
+ */
+static void
+XeditIndentationCallback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ LISP_SETUP();
+ LispObj *indentp;
+ XeditLispData *data = (XeditLispData*)client_data;
+
+ data->disable_highlight = True;
+ XtRemoveCallback(w, XtNpositionCallback, XeditIndentationCallback, data);
+
+ LISP_ENTER();
+
+ /* Get pointer to indentation function */
+ indentp = APPLY1(Osyntable_indent, data->syntable);
+
+ /* Execute indentation function */
+ if (indentp != NIL)
+ APPLY2(indentp, data->syntax, data->syntable);
+
+ data->disable_highlight = False;
+
+ LISP_LEAVE();
+}
+
+/************************************************************************
+ * Builtin functions
+ ************************************************************************/
+LispObj *
+Xedit_AddEntity(LispBuiltin *builtin)
+/*
+ add-entity offset length identifier
+ */
+{
+ LispObj *offset, *length, *identifier;
+
+ identifier = ARGUMENT(2);
+ length = ARGUMENT(1);
+ offset = ARGUMENT(0);
+
+ CHECK_INDEX(offset);
+ CHECK_INDEX(length);
+ CHECK_LONGINT(identifier);
+
+ return (XawTextSourceAddEntity(XawTextGetSource(textwindow), 0, 0, NULL,
+ FIXNUM_VALUE(offset), FIXNUM_VALUE(length),
+ LONGINT_VALUE(identifier)) ? T : NIL);
+}
+
+LispObj *
+Xedit_AutoFill(LispBuiltin *builtin)
+/*
+ auto-fill &optional value
+ */
+{
+ Arg arg[1];
+ Boolean state;
+
+ LispObj *value;
+
+ value = ARGUMENT(0);
+
+ if (value != UNSPEC) {
+ XtSetArg(arg[0], XtNautoFill, value == NIL ? False : True);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ XtSetArg(arg[0], XtNautoFill, &state);
+ XtGetValues(textwindow, arg, 1);
+ value = state ? T : NIL;
+ }
+
+ return (value);
+}
+
+LispObj *
+Xedit_Background(LispBuiltin *builtin)
+/*
+ background &optional color
+ */
+{
+ Pixel pixel;
+ Arg arg[1];
+ XrmValue from, to;
+
+ LispObj *color;
+
+ color = ARGUMENT(0);
+
+ if (color != UNSPEC) {
+ CHECK_STRING(color);
+
+ from.size = STRLEN(color);
+ from.addr = (XtPointer)THESTR(color);
+ to.size = sizeof(Pixel);
+ to.addr = (XtPointer)&pixel;
+
+ if (!XtConvertAndStore(XawTextGetSink(textwindow),
+ XtRString, &from, XtRPixel, &to))
+ LispDestroy("cannot convert %s to Pixel", STROBJ(color));
+
+ XtSetArg(arg[0], XtNbackground, pixel);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ from.size = sizeof(Pixel);
+ from.addr = (XtPointer)&pixel;
+ to.size = 0;
+ to.addr = NULL;
+
+ XtSetArg(arg[0], XtNbackground, &pixel);
+ XtGetValues(XawTextGetSink(textwindow), arg, 1);
+ /* This cannot fail */
+ XtConvertAndStore(textwindow, XtRPixel, &from, XtRString, &to);
+
+ color = STRING(to.addr);
+ }
+
+ return (color);
+}
+
+static LispObj *
+XeditCharAt(LispBuiltin *builtin, int before)
+{
+ Widget source = XawTextGetSource(textwindow);
+ XawTextPosition first, point, last;
+ XawTextBlock block;
+
+ LispObj *offset;
+
+ offset = ARGUMENT(0);
+ if (offset != UNSPEC) {
+ CHECK_INDEX(offset);
+ }
+
+ first = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True);
+ if (FIXNUMP(offset))
+ point = FIXNUM_VALUE(offset);
+ else
+ point = XawTextGetInsertionPoint(textwindow);
+ if (before && point > first) {
+ XawTextPosition position =
+ XawTextSourceScan(source, point, XawstPositions, XawsdLeft, 1, True);
+
+ if (position < point)
+ point = position;
+ else
+ return (NIL);
+ }
+ last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True);
+
+ if (point < first || point > last)
+ return (NIL);
+
+ XawTextSourceRead(source, point, &block, 1);
+
+ return (block.length ? SCHAR(*(unsigned char*)block.ptr) : NIL);
+}
+
+LispObj *
+Xedit_CharAfter(LispBuiltin *builtin)
+/*
+ char-after &optional offset
+ */
+{
+ return (XeditCharAt(builtin, 0));
+}
+
+LispObj *
+Xedit_CharBefore(LispBuiltin *builtin)
+/*
+ char-before &optional offset
+ */
+{
+ return (XeditCharAt(builtin, 1));
+}
+
+LispObj *
+Xedit_ClearEntities(LispBuiltin *builtin)
+/*
+ clear-entities left right
+ */
+{
+ LispObj *left, *right;
+
+ right = ARGUMENT(1);
+ left = ARGUMENT(0);
+
+ CHECK_INDEX(left);
+ CHECK_INDEX(right);
+
+ XawTextSourceClearEntities(XawTextGetSource(textwindow),
+ FIXNUM_VALUE(left), FIXNUM_VALUE(right));
+
+ return (T);
+}
+
+LispObj *
+Xedit_ConvertPropertyList(LispBuiltin *builtin)
+/*
+ convert-property-list name definition
+ */
+{
+ LispObj *result;
+ XawTextPropertyList *property_list;
+
+ LispObj *name, *definition;
+
+ definition = ARGUMENT(1);
+ name = ARGUMENT(0);
+
+ CHECK_STRING(name);
+ CHECK_STRING(definition);
+
+ result = NIL;
+ property_list = XawTextSinkConvertPropertyList(THESTR(name),
+ THESTR(definition),
+ topwindow->core.screen,
+ topwindow->core.colormap,
+ topwindow->core.depth);
+
+ if (property_list) {
+ Cardinal i;
+
+ for (i = 0; i < num_property_lists; i++)
+ /* Check if a new property list was created */
+ if (property_lists[i]->identifier == property_list->identifier)
+ break;
+
+ /* Remember this pointer when asked back for it */
+ if (i == num_property_lists) {
+ property_lists = (XawTextPropertyList**)
+ XtRealloc((XtPointer)property_lists,
+ sizeof(XawTextPropertyList) *
+ (num_property_lists + 1));
+ property_lists[num_property_lists++] = property_list;
+ }
+ result = INTEGER(property_list->identifier);
+ }
+
+ return (result);
+}
+
+LispObj *
+Xedit_Font(LispBuiltin *builtin)
+/*
+ font &optional font
+ */
+{
+ XFontStruct *font_struct;
+ Arg arg[1];
+ XrmValue from, to;
+
+ LispObj *font;
+
+ font = ARGUMENT(0);
+
+ if (font != UNSPEC) {
+ CHECK_STRING(font);
+
+ from.size = STRLEN(font);
+ from.addr = (XtPointer)THESTR(font);
+ to.size = sizeof(XFontStruct*);
+ to.addr = (XtPointer)&font_struct;
+
+ if (!XtConvertAndStore(textwindow, XtRString, &from, XtRFontStruct, &to))
+ LispDestroy("cannot convert %s to FontStruct", STROBJ(font));
+
+ XtSetArg(arg[0], XtNfont, font_struct);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ from.size = sizeof(XFontStruct*);
+ from.addr = (XtPointer)&font_struct;
+ to.size = 0;
+ to.addr = NULL;
+
+ XtSetArg(arg[0], XtNfont, &font_struct);
+ XtGetValues(XawTextGetSink(textwindow), arg, 1);
+ /* This cannot fail */
+ XtConvertAndStore(textwindow, XtRFontStruct, &from, XtRString, &to);
+
+ font = STRING(to.addr);
+ }
+
+ return (font);
+}
+
+LispObj *
+Xedit_Foreground(LispBuiltin *builtin)
+/*
+ foreground &optional color
+ */
+{
+ Pixel pixel;
+ Arg arg[1];
+ XrmValue from, to;
+
+ LispObj *color;
+
+ color = ARGUMENT(0);
+
+ if (color != UNSPEC) {
+ CHECK_STRING(color);
+
+ from.size = STRLEN(color);
+ from.addr = (XtPointer)THESTR(color);
+ to.size = sizeof(Pixel);
+ to.addr = (XtPointer)&pixel;
+
+ if (!XtConvertAndStore(XawTextGetSink(textwindow),
+ XtRString, &from, XtRPixel, &to))
+ LispDestroy("cannot convert %s to Pixel", STROBJ(color));
+
+ XtSetArg(arg[0], XtNforeground, pixel);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ from.size = sizeof(Pixel);
+ from.addr = (XtPointer)&pixel;
+ to.size = 0;
+ to.addr = NULL;
+
+ XtSetArg(arg[0], XtNforeground, &pixel);
+ XtGetValues(XawTextGetSink(textwindow), arg, 1);
+ /* This cannot fail */
+ XtConvertAndStore(textwindow, XtRPixel, &from, XtRString, &to);
+
+ color = STRING(to.addr);
+ }
+
+ return (color);
+}
+
+LispObj *
+Xedit_GotoChar(LispBuiltin *builtin)
+/*
+ goto-char offset
+ */
+{
+ LispObj *offset;
+ XawTextPosition point;
+
+ offset = ARGUMENT(0);
+
+ CHECK_INDEX(offset);
+ XawTextSetInsertionPoint(textwindow, FIXNUM_VALUE(offset));
+ point = XawTextGetInsertionPoint(textwindow);
+ if (point != FIXNUM_VALUE(offset))
+ offset = FIXNUM(point);
+
+ return (offset);
+}
+
+LispObj *
+Xedit_HorizontalScrollbar(LispBuiltin *builtin)
+/*
+ horizontal-scrollbar &optional state
+ */
+{
+ Arg arg[1];
+ XawTextScrollMode scroll;
+
+ LispObj *state;
+
+ state = ARGUMENT(0);
+
+ if (state != UNSPEC) {
+ scroll = state == NIL ? XawtextScrollNever : XawtextScrollAlways;
+ XtSetArg(arg[0], XtNscrollHorizontal, scroll);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ XtSetArg(arg[0], XtNscrollHorizontal, &scroll);
+ XtGetValues(textwindow, arg, 1);
+ state = scroll == XawtextScrollAlways ? T : NIL;
+ }
+
+ return (state);
+}
+
+LispObj *
+Xedit_Insert(LispBuiltin *builtin)
+/*
+ insert text
+ */
+{
+ XawTextPosition point = XawTextGetInsertionPoint(textwindow);
+ XawTextBlock block;
+
+ LispObj *text;
+
+ text = ARGUMENT(0);
+
+ CHECK_STRING(text);
+
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ block.length = STRLEN(text);
+ block.ptr = THESTR(text);
+ XawTextReplace(textwindow, point, point, &block);
+ XawTextSetInsertionPoint(textwindow, point + block.length);
+
+ return (text);
+}
+
+LispObj *
+Xedit_Justification(LispBuiltin *builtin)
+/*
+ justification &optional value
+ */
+{
+ int i;
+ Arg arg[1];
+ XawTextJustifyMode justify;
+
+ LispObj *value;
+
+ value = ARGUMENT(0);
+
+ if (value != UNSPEC) {
+ for (i = 0; i < 4; i++)
+ if (value == justify_modes[i])
+ break;
+ if (i >= 4)
+ LispDestroy("%s: argument must be "
+ ":LEFT, :RIGHT, :CENTER, or :FULL, not %s",
+ STRFUN(builtin), STROBJ(value));
+ XtSetArg(arg[0], XtNjustifyMode, (XawTextJustifyMode)i);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ XtSetArg(arg[0], XtNjustifyMode, &justify);
+ XtGetValues(textwindow, arg, 1);
+ i = (int)justify;
+ if (i <= 0 || i >= 4)
+ i = 0;
+ value = justify_modes[i];
+ }
+
+ return (value);
+}
+
+LispObj *
+Xedit_LeftColumn(LispBuiltin *builtin)
+/*
+ left-column &optional left
+ */
+{
+ short left;
+ Arg arg[1];
+
+ LispObj *oleft;
+
+ oleft = ARGUMENT(0);
+
+ if (oleft != UNSPEC) {
+ CHECK_INDEX(oleft);
+ if (FIXNUM_VALUE(oleft) >= 32767)
+ left = 32767;
+ else
+ left = FIXNUM_VALUE(oleft);
+
+ XtSetArg(arg[0], XtNleftColumn, left);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ XtSetArg(arg[0], XtNleftColumn, &left);
+ XtGetValues(textwindow, arg, 1);
+
+ oleft = FIXNUM((long)left);
+ }
+
+ return (oleft);
+}
+
+LispObj *
+Xedit_Point(LispBuiltin *builtin)
+/*
+ point
+ */
+{
+ return (FIXNUM(XawTextGetInsertionPoint(textwindow)));
+}
+
+LispObj *
+Xedit_PointMax(LispBuiltin *builtin)
+/*
+ point-max
+ */
+{
+ return (FIXNUM(XawTextSourceScan(XawTextGetSource(textwindow), 0,
+ XawstAll, XawsdRight, 1, True)));
+}
+
+LispObj *
+Xedit_PointMin(LispBuiltin *builtin)
+/*
+ point-min
+ */
+{
+ return (FIXNUM(XawTextSourceScan(XawTextGetSource(textwindow), 0,
+ XawstAll, XawsdLeft, 1, True)));
+}
+
+LispObj *
+Xedit_PropertyList(LispBuiltin *builtin)
+/*
+ property-list &optional value
+ */
+{
+ Arg arg[1];
+ XawTextPropertyList *property_list;
+
+ LispObj *value;
+
+ value = ARGUMENT(0);
+
+ if (value != UNSPEC) {
+ Cardinal i;
+ XrmQuark quark;
+
+ CHECK_LONGINT(value);
+ property_list = NULL;
+ quark = LONGINT_VALUE(value);
+ for (i = 0; i < num_property_lists; i++)
+ if (property_lists[i]->identifier == quark) {
+ property_list = property_lists[i];
+ break;
+ }
+
+ if (property_list) {
+ XtSetArg(arg[0], XawNtextProperties, property_list);
+ XtSetValues(XawTextGetSink(textwindow), arg, 1);
+ }
+ else
+ /* Maybe should generate an error here */
+ value = NIL;
+ }
+ else {
+ XtSetArg(arg[0], XawNtextProperties, &property_list);
+ XtGetValues(XawTextGetSink(textwindow), arg, 1);
+ if (property_list)
+ value = INTEGER(property_list->identifier);
+ }
+
+ return (value);
+}
+
+LispObj *
+Xedit_ReadText(LispBuiltin *builtin)
+/*
+ read-text offset length
+ */
+{
+ XawTextPosition last = XawTextSourceScan(XawTextGetSource(textwindow), 0,
+ XawstAll, XawsdRight, 1, True);
+ XawTextPosition from, to, len;
+ XawTextBlock block;
+ char *string, *ptr;
+
+ LispObj *offset, *length;
+
+ length = ARGUMENT(1);
+ offset = ARGUMENT(0);
+
+ CHECK_INDEX(offset);
+ CHECK_INDEX(length);
+
+ from = FIXNUM_VALUE(offset);
+ to = from + FIXNUM_VALUE(length);
+ if (from > last)
+ from = last;
+ if (to > last)
+ to = last;
+
+ if (from == to)
+ return (STRING(""));
+
+ len = to - from;
+ string = LispMalloc(len);
+
+ for (ptr = string; from < to;) {
+ XawTextSourceRead(XawTextGetSource(textwindow), from, &block, to - from);
+ memcpy(ptr, block.ptr, block.length);
+ ptr += block.length;
+ from += block.length;
+ }
+
+ return (LSTRING2(string, len));
+}
+
+LispObj *
+Xedit_ReplaceText(LispBuiltin *builtin)
+/*
+ replace-text left right text
+ */
+{
+ XawTextPosition last = XawTextSourceScan(XawTextGetSource(textwindow), 0,
+ XawstAll, XawsdRight, 1, True);
+ XawTextPosition left, right;
+ XawTextBlock block;
+
+ LispObj *oleft, *oright, *text;
+
+ text = ARGUMENT(2);
+ oright = ARGUMENT(1);
+ oleft = ARGUMENT(0);
+
+ CHECK_INDEX(oleft);
+ CHECK_INDEX(oright);
+ CHECK_STRING(text);
+
+ left = FIXNUM_VALUE(oleft);
+ right = FIXNUM_VALUE(oright);
+ if (left > last)
+ left = last;
+ if (left > right)
+ right = left;
+ else if (right > last)
+ right = last;
+
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ block.length = STRLEN(text);
+ block.ptr = THESTR(text);
+ XawTextReplace(textwindow, left, right, &block);
+
+ return (text);
+}
+
+LispObj *
+Xedit_RightColumn(LispBuiltin *builtin)
+/*
+ right-column &optional right
+ */
+{
+ short right;
+ Arg arg[1];
+
+ LispObj *oright;
+
+ oright = ARGUMENT(0);
+
+ if (oright != UNSPEC) {
+ CHECK_INDEX(oright);
+ if (FIXNUM_VALUE(oright) >= 32767)
+ right = 32767;
+ else
+ right = FIXNUM_VALUE(oright);
+
+ XtSetArg(arg[0], XtNrightColumn, right);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ XtSetArg(arg[0], XtNrightColumn, &right);
+ XtGetValues(textwindow, arg, 1);
+
+ oright = FIXNUM(right);
+ }
+
+ return (oright);
+}
+
+LispObj *
+Xedit_Scan(LispBuiltin *builtin)
+/*
+ scan offset type direction &key count include
+ */
+{
+ int i;
+ XawTextPosition offset;
+ XawTextScanType type;
+ XawTextScanDirection direction;
+ int count;
+
+ LispObj *ooffset, *otype, *odirection, *ocount, *include;
+
+ include = ARGUMENT(4);
+ if (include == UNSPEC)
+ include = NIL;
+ ocount = ARGUMENT(3);
+ odirection = ARGUMENT(2);
+ otype = ARGUMENT(1);
+ ooffset = ARGUMENT(0);
+
+ CHECK_INDEX(ooffset);
+ offset = FIXNUM_VALUE(ooffset);
+
+ for (i = 0; i < 2; i++)
+ if (odirection == scan_directions[i])
+ break;
+ if (i >= 2)
+ LispDestroy("%s: direction must be "
+ ":LEFT or :RIGHT, not %s",
+ STRFUN(builtin), STROBJ(odirection));
+ direction = (XawTextScanDirection)i;
+
+ for (i = 0; i < 5; i++)
+ if (otype == scan_types[i])
+ break;
+ if (i >= 5)
+ LispDestroy("%s: direction must be "
+ ":POSITIONS, :WHITE-SPACE, :EOL, "
+ ":PARAGRAPH, :ALL, or :ALPHA-NUMERIC, not %s",
+ STRFUN(builtin), STROBJ(otype));
+ type = (XawTextScanType)i;
+
+ if (ocount == UNSPEC)
+ count = 1;
+ else {
+ CHECK_INDEX(ocount);
+ count = FIXNUM_VALUE(ocount);
+ }
+
+ offset = XawTextSourceScan(XawTextGetSource(textwindow),
+ offset, type, direction, count,
+ include != NIL);
+
+ return (FIXNUM(offset));
+}
+
+static LispObj *
+XeditSearch(LispBuiltin *builtin, XawTextScanDirection direction)
+{
+ XawTextBlock block;
+ XawTextPosition position;
+
+ LispObj *string, *offset, *ignore_case;
+
+ ignore_case = ARGUMENT(2);
+ offset = ARGUMENT(1);
+ string = ARGUMENT(0);
+
+ CHECK_STRING(string);
+ if (offset != UNSPEC) {
+ CHECK_INDEX(offset);
+ position = FIXNUM_VALUE(offset);
+ }
+ else
+ position = XawTextGetInsertionPoint(textwindow);
+
+ block.firstPos = (ignore_case != UNSPEC && ignore_case != NIL) ? 1 : 0;
+ block.format = FMT8BIT;
+ block.length = STRLEN(string);
+ block.ptr = THESTR(string);
+ position = XawTextSourceSearch(XawTextGetSource(textwindow),
+ position, direction, &block);
+
+ return (position != XawTextSearchError ? FIXNUM(position) : NIL);
+}
+
+
+LispObj *
+Xedit_SearchBackward(LispBuiltin *builtin)
+/*
+ search-backward string &optional offset ignore-case
+ */
+{
+ return (XeditSearch(builtin, XawsdLeft));
+}
+
+LispObj *
+Xedit_SearchForward(LispBuiltin *builtin)
+/*
+ search-forward string &optional offset ignore-case
+ */
+{
+ return (XeditSearch(builtin, XawsdRight));
+}
+
+LispObj *
+Xedit_VerticalScrollbar(LispBuiltin *builtin)
+/*
+ vertical-scrollbar &optional state
+ */
+{
+ Arg arg[1];
+ XawTextScrollMode scroll;
+
+ LispObj *state;
+
+ state = ARGUMENT(0);
+
+ if (state != UNSPEC) {
+ scroll = state == NIL ? XawtextScrollNever : XawtextScrollAlways;
+ XtSetArg(arg[0], XtNscrollVertical, scroll);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ XtSetArg(arg[0], XtNscrollVertical, &scroll);
+ XtGetValues(textwindow, arg, 1);
+ state = scroll == XawtextScrollAlways ? T : NIL;
+ }
+
+ return (state);
+}
+
+LispObj *
+Xedit_WrapMode(LispBuiltin *builtin)
+/*
+ wrap-mode &optional value
+ */
+{
+ int i;
+ Arg arg[1];
+ XawTextWrapMode wrap;
+
+ LispObj *value;
+
+ value = ARGUMENT(0);
+
+ if (value != UNSPEC) {
+ for (i = 0; i < 3; i++)
+ if (value == wrap_modes[i])
+ break;
+ if (i >= 3)
+ LispDestroy("%s: argument must be "
+ ":NEVER, :LINE, or :WORD, not %s",
+ STRFUN(builtin), STROBJ(value));
+ XtSetArg(arg[0], XtNwrap, (XawTextWrapMode)i);
+ XtSetValues(textwindow, arg, 1);
+ }
+ else {
+ XtSetArg(arg[0], XtNwrap, &wrap);
+ XtGetValues(textwindow, arg, 1);
+ i = (int)wrap;
+ if (i <= 0 || i >= 3)
+ i = 0;
+ value = wrap_modes[i];
+ }
+
+ return (value);
+}
+
+LispObj *
+Xedit_XrmStringToQuark(LispBuiltin *builtin)
+/*
+ xrm-string-to-quark string
+ */
+{
+ LispObj *string;
+
+ string = ARGUMENT(0);
+
+ CHECK_STRING(string);
+
+ return (INTEGER(XrmStringToQuark(THESTR(string))));
+}
diff --git a/lisp/xedit.h b/lisp/xedit.h
new file mode 100644
index 0000000..0beff56
--- /dev/null
+++ b/lisp/xedit.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/xedit.h,v 1.5 2002/11/10 23:22:00 paulo Exp $ */
+
+#ifndef Lisp_xedit_h
+#define Lisp_xedit_h
+
+#ifdef XEDIT_LISP_PRIVATE
+#include "private.h"
+#include "io.h"
+#include "read.h"
+#include "write.h"
+
+LispObj *Xedit_AddEntity(LispBuiltin*);
+LispObj *Xedit_AutoFill(LispBuiltin*);
+LispObj *Xedit_Background(LispBuiltin*);
+LispObj *Xedit_CharAfter(LispBuiltin*);
+LispObj *Xedit_CharBefore(LispBuiltin*);
+LispObj *Xedit_ClearEntities(LispBuiltin*);
+LispObj *Xedit_ConvertPropertyList(LispBuiltin*);
+LispObj *Xedit_Font(LispBuiltin*);
+LispObj *Xedit_Foreground(LispBuiltin*);
+LispObj *Xedit_GotoChar(LispBuiltin*);
+LispObj *Xedit_HorizontalScrollbar(LispBuiltin*);
+LispObj *Xedit_Insert(LispBuiltin*);
+LispObj *Xedit_Justification(LispBuiltin*);
+LispObj *Xedit_LeftColumn(LispBuiltin*);
+LispObj *Xedit_Point(LispBuiltin*);
+LispObj *Xedit_PointMax(LispBuiltin*);
+LispObj *Xedit_PointMin(LispBuiltin*);
+LispObj *Xedit_PropertyList(LispBuiltin*);
+LispObj *Xedit_ReadText(LispBuiltin*);
+LispObj *Xedit_ReplaceText(LispBuiltin*);
+LispObj *Xedit_RightColumn(LispBuiltin*);
+LispObj *Xedit_Scan(LispBuiltin*);
+LispObj *Xedit_SearchBackward(LispBuiltin*);
+LispObj *Xedit_SearchForward(LispBuiltin*);
+LispObj *Xedit_VerticalScrollbar(LispBuiltin*);
+LispObj *Xedit_WrapMode(LispBuiltin*);
+LispObj *Xedit_XrmStringToQuark(LispBuiltin*);
+#else
+#define LispObj void
+#endif /* XEDIT_LISP_PRIVATE */
+
+typedef struct _EditModeInfo {
+ char *desc; /* Mode description */
+ Widget sme; /* Menu entry */
+ LispObj *symbol; /* Symbol holding syntax data */
+ LispObj *syntax; /* The syntax definition */
+} EditModeInfo;
+
+/* Typedef'ed to XeditLispData in ../xedit.h */
+struct _XeditLispData {
+ LispObj *syntax; /* Syntax definition */
+ LispObj *syntable; /* Syntax-table the cursor is located */
+ int disable_highlight; /* Working in the buffer */
+};
+
+void LispXeditInitialize(void);
+void XeditLispExecute(Widget, XawTextPosition, XawTextPosition);
+void XeditLispSetEditMode(xedit_flist_item*, LispObj*);
+void XeditLispUnsetEditMode(xedit_flist_item*);
+
+extern EditModeInfo *mode_infos;
+extern Cardinal num_mode_infos;
+
+#endif /* Lisp_xedit_h */
diff --git a/options.c b/options.c
new file mode 100644
index 0000000..c929505
--- /dev/null
+++ b/options.c
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 1999 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.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/options.c,v 1.14 2002/11/10 23:21:56 paulo Exp $ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "xedit.h"
+
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SimpleMenu.h>
+
+/*
+ * Prototypes
+ */
+static void SetColumns(Widget, XEvent*, String*, Cardinal*);
+static void ChangeField(Widget, XEvent*, String*, Cardinal*);
+static void EditCallback(Widget, XtPointer, XtPointer);
+static void PopupColumnsCallback(Widget, XtPointer, XtPointer);
+static void CreateColumnsShell(void);
+static void ProcessColumnsCallback(Widget, XtPointer, XtPointer);
+
+/*
+ * Initialization
+ */
+extern Widget texts[3];
+
+static Widget edit_popup, wrap_popup, justify_popup, scroll_popup,
+ columns_shell, left_text, right_text;
+
+static XFontStruct *fonts[3];
+static Pixel foregrounds[3], backgrounds[3];
+
+static XtActionsRec actions[] = {
+ {"set-columns", SetColumns},
+ {"change-field", ChangeField},
+};
+
+#define WRAP_NEVER 1
+#define WRAP_LINE 2
+#define WRAP_WORD 3
+#define AUTO_FILL 4
+#define JUST_LEFT 5
+#define JUST_RIGHT 6
+#define JUST_CENTER 7
+#define JUST_FULL 8
+#define SCROLL_VERT 9
+#define SCROLL_HORIZ 10
+
+static Widget autoFill, wrapNever, wrapLine, wrapWord,
+ justifyLeft, justifyRight, justifyCenter, justifyFull,
+ breakColumns, scrollVert, scrollHoriz;
+
+void
+CreateEditPopup(void)
+{
+ Arg args[1];
+
+ edit_popup = XtCreatePopupShell("editMenu", simpleMenuWidgetClass,
+ topwindow, NULL, 0);
+ XtRealizeWidget(edit_popup);
+
+ wrap_popup = XtCreatePopupShell("wrapMenu", simpleMenuWidgetClass,
+ edit_popup, NULL, 0);
+ XtRealizeWidget(wrap_popup);
+
+ XtSetArg(args[0], XtNmenuName, "wrapMenu");
+ XtCreateManagedWidget("wrapMenuItem", smeBSBObjectClass, edit_popup, args, 1);
+
+ wrapNever = XtCreateManagedWidget("never", smeBSBObjectClass,
+ wrap_popup, NULL, 0);
+ XtAddCallback(wrapNever, XtNcallback, EditCallback, (XtPointer)WRAP_NEVER);
+ wrapLine = XtCreateManagedWidget("line", smeBSBObjectClass,
+ wrap_popup, NULL, 0);
+ XtAddCallback(wrapLine, XtNcallback, EditCallback, (XtPointer)WRAP_LINE);
+ wrapWord = XtCreateManagedWidget("word", smeBSBObjectClass,
+ wrap_popup, NULL, 0);
+ XtAddCallback(wrapWord, XtNcallback, EditCallback, (XtPointer)WRAP_WORD);
+
+ autoFill = XtCreateManagedWidget("autoFill", smeBSBObjectClass,
+ edit_popup, NULL, 0);
+ XtAddCallback(autoFill, XtNcallback, EditCallback, (XtPointer)AUTO_FILL);
+
+ justify_popup = XtCreatePopupShell("justifyMenu", simpleMenuWidgetClass,
+ edit_popup, NULL, 0);
+ XtRealizeWidget(justify_popup);
+
+ XtSetArg(args[0], XtNmenuName, "justifyMenu");
+ XtCreateManagedWidget("justifyMenuItem", smeBSBObjectClass, edit_popup, args, 1);
+
+ justifyLeft = XtCreateManagedWidget("left", smeBSBObjectClass,
+ justify_popup, NULL, 0);
+ XtAddCallback(justifyLeft, XtNcallback, EditCallback, (XtPointer)JUST_LEFT);
+ justifyRight = XtCreateManagedWidget("right", smeBSBObjectClass,
+ justify_popup, NULL, 0);
+ XtAddCallback(justifyRight, XtNcallback, EditCallback, (XtPointer)JUST_RIGHT);
+ justifyCenter = XtCreateManagedWidget("center", smeBSBObjectClass,
+ justify_popup, NULL, 0);
+ XtAddCallback(justifyCenter, XtNcallback, EditCallback, (XtPointer)JUST_CENTER);
+ justifyFull = XtCreateManagedWidget("full", smeBSBObjectClass,
+ justify_popup, NULL, 0);
+ XtAddCallback(justifyFull, XtNcallback, EditCallback, (XtPointer)JUST_FULL);
+
+ breakColumns = XtCreateManagedWidget("breakColumns", smeBSBObjectClass,
+ edit_popup, NULL, 0);
+ XtAddCallback(breakColumns, XtNcallback, PopupColumnsCallback, NULL);
+
+ scroll_popup = XtCreatePopupShell("scrollMenu", simpleMenuWidgetClass,
+ edit_popup, NULL, 0);
+ XtRealizeWidget(scroll_popup);
+
+ XtSetArg(args[0], XtNmenuName, "scrollMenu");
+ XtCreateManagedWidget("scrollMenuItem", smeBSBObjectClass, edit_popup, args, 1);
+
+ scrollVert = XtCreateManagedWidget("vertical", smeBSBObjectClass,
+ scroll_popup, NULL, 0);
+ XtAddCallback(scrollVert, XtNcallback, EditCallback, (XtPointer)SCROLL_VERT);
+ scrollHoriz = XtCreateManagedWidget("horizontal", smeBSBObjectClass,
+ scroll_popup, NULL, 0);
+ XtAddCallback(scrollHoriz, XtNcallback, EditCallback, (XtPointer)SCROLL_HORIZ);
+
+ CreateEditModePopup(edit_popup);
+}
+
+void
+SetEditMenu(void)
+{
+ Arg args[7];
+ Cardinal num_args;
+ Boolean auto_fill;
+ XawTextWrapMode wrap_mode;
+ XawTextJustifyMode justify;
+ XawTextScrollMode vscroll, hscroll;
+ short left, right;
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNwrap, &wrap_mode); ++num_args;
+ XtSetArg(args[num_args], XtNautoFill, &auto_fill); ++num_args;
+ XtSetArg(args[num_args], XtNjustifyMode, &justify); ++num_args;
+ XtSetArg(args[num_args], XtNleftColumn, &left); ++num_args;
+ XtSetArg(args[num_args], XtNrightColumn, &right); ++num_args;
+ XtSetArg(args[num_args], XtNscrollVertical, &vscroll); ++num_args;
+ XtSetArg(args[num_args], XtNscrollHorizontal, &hscroll); ++num_args;
+ XtGetValues(textwindow, args, num_args);
+
+ if (flist.pixmap) {
+ XtSetArg(args[0], XtNleftBitmap, None);
+ XtSetArg(args[1], XtNleftBitmap, flist.pixmap);
+ if (!auto_fill)
+ XtSetValues(autoFill, &args[0], 1);
+ else
+ XtSetValues(autoFill, &args[1], 1);
+ switch (wrap_mode) {
+ case XawtextWrapNever:
+ XtSetValues(wrapLine, &args[0], 1);
+ XtSetValues(wrapWord, &args[0], 1);
+ XtSetValues(wrapNever, &args[1], 1);
+ break;
+ case XawtextWrapLine:
+ XtSetValues(wrapNever, &args[0], 1);
+ XtSetValues(wrapWord, &args[0], 1);
+ XtSetValues(wrapLine, &args[1], 1);
+ break;
+ case XawtextWrapWord:
+ XtSetValues(wrapNever, &args[0], 1);
+ XtSetValues(wrapLine, &args[0], 1);
+ XtSetValues(wrapWord, &args[1], 1);
+ break;
+ }
+ switch (justify) {
+ case XawjustifyLeft:
+ XtSetValues(justifyRight, &args[0], 1);
+ XtSetValues(justifyCenter, &args[0], 1);
+ XtSetValues(justifyFull, &args[0], 1);
+ XtSetValues(justifyLeft, &args[1], 1);
+ break;
+ case XawjustifyRight:
+ XtSetValues(justifyLeft, &args[0], 1);
+ XtSetValues(justifyCenter, &args[0], 1);
+ XtSetValues(justifyFull, &args[0], 1);
+ XtSetValues(justifyRight, &args[1], 1);
+ break;
+ case XawjustifyCenter:
+ XtSetValues(justifyLeft, &args[0], 1);
+ XtSetValues(justifyRight, &args[0], 1);
+ XtSetValues(justifyFull, &args[0], 1);
+ XtSetValues(justifyCenter, &args[1], 1);
+ break;
+ case XawjustifyFull:
+ XtSetValues(justifyLeft, &args[0], 1);
+ XtSetValues(justifyRight, &args[0], 1);
+ XtSetValues(justifyCenter, &args[0], 1);
+ XtSetValues(justifyFull, &args[1], 1);
+ break;
+ }
+ if (!vscroll)
+ XtSetValues(scrollVert, &args[0], 1);
+ else
+ XtSetValues(scrollVert, &args[1], 1);
+ if (!hscroll)
+ XtSetValues(scrollHoriz, &args[0], 1);
+ else
+ XtSetValues(scrollHoriz, &args[1], 1);
+ }
+ if (!auto_fill) {
+ XtSetSensitive(wrapNever, True);
+ XtSetSensitive(wrapLine, True);
+ XtSetSensitive(wrapWord, True);
+
+ XtSetSensitive(justifyLeft, False);
+ XtSetSensitive(justifyRight, False);
+ XtSetSensitive(justifyCenter, False);
+ XtSetSensitive(justifyFull, False);
+ XtSetSensitive(breakColumns, False);
+ }
+ else {
+ XtSetSensitive(wrapNever, False);
+ XtSetSensitive(wrapLine, False);
+ XtSetSensitive(wrapWord, False);
+
+ XtSetSensitive(justifyLeft, left < right);
+ XtSetSensitive(justifyRight, left < right);
+ XtSetSensitive(justifyCenter, left < right);
+ XtSetSensitive(justifyFull, left < right);
+ XtSetSensitive(breakColumns, True);
+ }
+
+ SetEditModeMenu();
+}
+
+/*ARGSUSED*/
+static void
+EditCallback(Widget sme, XtPointer client_data, XtPointer call_data)
+{
+ Arg args[1];
+ Boolean auto_fill;
+ XawTextScrollMode scroll;
+ xedit_flist_item *item = FindTextSource(XawTextGetSource(textwindow), NULL);
+ Bool foreach = False;
+
+ switch ((long)client_data) {
+ case WRAP_NEVER:
+ XtSetArg(args[0], XtNwrap, XawtextWrapNever);
+ if (item) {
+ item->flags |= WRAP_BIT;
+ item->wrap = XawtextWrapNever;
+ foreach = True;
+ }
+ break;
+ case WRAP_LINE:
+ XtSetArg(args[0], XtNwrap, XawtextWrapLine);
+ if (item) {
+ item->flags |= WRAP_BIT;
+ item->wrap = XawtextWrapLine;
+ foreach = True;
+ }
+ break;
+ case WRAP_WORD:
+ XtSetArg(args[0], XtNwrap, XawtextWrapWord);
+ if (item) {
+ item->flags |= WRAP_BIT;
+ item->wrap = XawtextWrapWord;
+ foreach = True;
+ }
+ break;
+ case AUTO_FILL:
+ XtSetArg(args[0], XtNautoFill, &auto_fill);
+ XtGetValues(textwindow, args, 1);
+ XtSetArg(args[0], XtNautoFill, !auto_fill);
+ break;
+ case JUST_LEFT:
+ XtSetArg(args[0], XtNjustifyMode, XawjustifyLeft);
+ break;
+ case JUST_RIGHT:
+ XtSetArg(args[0], XtNjustifyMode, XawjustifyRight);
+ break;
+ case JUST_CENTER:
+ XtSetArg(args[0], XtNjustifyMode, XawjustifyCenter);
+ break;
+ case JUST_FULL:
+ XtSetArg(args[0], XtNjustifyMode, XawjustifyFull);
+ break;
+ case SCROLL_VERT:
+ XtSetArg(args[0], XtNscrollVertical, &scroll);
+ XtGetValues(textwindow, args, 1);
+ XtSetArg(args[0], XtNscrollVertical, scroll == XawtextScrollNever ?
+ XawtextScrollAlways : XawtextScrollNever);
+ break;
+ case SCROLL_HORIZ:
+ XtSetArg(args[0], XtNscrollHorizontal, &scroll);
+ XtGetValues(textwindow, args, 1);
+ XtSetArg(args[0], XtNscrollHorizontal, scroll == XawtextScrollNever ?
+ XawtextScrollAlways : XawtextScrollNever);
+ break;
+ }
+
+ if (foreach) {
+ int i;
+
+ for (i = 0; i < 3; i++)
+ XtSetValues(texts[i], args, 1);
+ }
+ else
+ XtSetValues(textwindow, args, 1);
+}
+
+static void
+CreateColumnsShell(void)
+{
+ Atom delete_window;
+ Widget form, ok, cancel;
+
+ if (columns_shell)
+ return;
+
+ XtAppAddActions(XtWidgetToApplicationContext(topwindow),
+ actions, XtNumber(actions));
+
+ columns_shell = XtCreatePopupShell("columns", transientShellWidgetClass,
+ topwindow, NULL, 0);
+ form = XtCreateManagedWidget("form", formWidgetClass,
+ columns_shell, NULL, 0);
+ XtCreateManagedWidget("leftLabel", labelWidgetClass, form, NULL, 0);
+ left_text = XtVaCreateManagedWidget("left", asciiTextWidgetClass,
+ form, XtNeditType, XawtextEdit,
+ NULL, 0);
+ XtCreateManagedWidget("rightLabel", labelWidgetClass, form, NULL, 0);
+ right_text = XtVaCreateManagedWidget("right", asciiTextWidgetClass,
+ form, XtNeditType, XawtextEdit,
+ NULL, 0);
+ ok = XtCreateManagedWidget("ok", commandWidgetClass,
+ form, NULL, 0);
+ XtAddCallback(ok, XtNcallback, ProcessColumnsCallback, (XtPointer)True);
+ cancel = XtCreateManagedWidget("cancel", commandWidgetClass,
+ form, NULL, 0);
+ XtAddCallback(cancel, XtNcallback, ProcessColumnsCallback, (XtPointer)False);
+
+ XtRealizeWidget(columns_shell);
+ delete_window = XInternAtom(XtDisplay(columns_shell), "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(XtDisplay(columns_shell), XtWindow(columns_shell), &delete_window, 1);
+
+ XtSetKeyboardFocus(columns_shell, left_text);
+}
+
+/*ARGSUSED*/
+static void
+PopupColumnsCallback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ Arg args[3];
+ char sleft[6], sright[6];
+ short left, right;
+ Dimension width, height, b_width;
+ Window r, c;
+ int x, y, wx, wy, max_x, max_y;
+ unsigned mask;
+
+ CreateColumnsShell();
+
+ XQueryPointer(XtDisplay(columns_shell), XtWindow(columns_shell),
+ &r, &c, &x, &y, &wx, &wy, &mask);
+
+ XtSetArg(args[0], XtNwidth, &width);
+ XtSetArg(args[1], XtNheight, &height);
+ XtSetArg(args[2], XtNborderWidth, &b_width);
+ XtGetValues(columns_shell, args, 3);
+
+ width += b_width << 1;
+ height += b_width << 1;
+
+ x -= (Position)(width >> 1);
+ if (x < 0)
+ x = 0;
+ if (x > (max_x = (Position)(XtScreen(columns_shell)->width - width)))
+ x = max_x;
+
+ y -= (Position)(height >> 1);
+ if (y < 0)
+ y = 0;
+ if (y > (max_y = (Position)(XtScreen(columns_shell)->height - height)))
+ y = max_y;
+
+ XtSetArg(args[0], XtNx, x);
+ XtSetArg(args[1], XtNy, y);
+ XtSetValues(columns_shell, args, 2);
+
+ XtSetArg(args[0], XtNleftColumn, &left);
+ XtSetArg(args[1], XtNrightColumn, &right);
+ XtGetValues(textwindow, args, 2);
+ XmuSnprintf(sleft, sizeof(sleft), "%d", left);
+ XmuSnprintf(sright, sizeof(sright), "%d", right);
+ XtSetArg(args[0], XtNstring, sleft);
+ XtSetValues(left_text, args, 1);
+ XtSetArg(args[0], XtNstring, sright);
+ XtSetValues(right_text, args, 1);
+ XtPopup(columns_shell, XtGrabExclusive);
+}
+
+/*ARGSUSED*/
+static void
+ProcessColumnsCallback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ if (client_data) {
+ Arg args[2];
+ char *left, *right;
+ short leftc, rightc;
+
+ left = GetString(left_text);
+ right = GetString(right_text);
+
+ leftc = atoi(left);
+ rightc = atoi(right);
+ XtSetArg(args[0], XtNleftColumn, leftc);
+ XtSetArg(args[1], XtNrightColumn, rightc);
+
+ XtSetValues(textwindow, args, 2);
+ }
+
+ XtPopdown(columns_shell);
+}
+
+/*ARGSUSED*/
+static void
+SetColumns(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Bool ok = False;
+
+ if (*num_params &&
+ (params[0][0] == 'o' || params[0][0] == 'O'))
+ ok = True;
+
+ ProcessColumnsCallback(w, (XtPointer)(long)ok, NULL);
+}
+
+/*ARGSUSED*/
+static void
+ChangeField(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Widget focus = XtGetKeyboardFocusWidget(columns_shell);
+
+ XtSetKeyboardFocus(columns_shell,
+ focus == left_text ? right_text : left_text);
+}
+
+void
+UpdateTextProperties(int force)
+{
+ Arg args[4];
+ Cardinal num_args;
+ xedit_flist_item *item;
+ XawTextPropertyList *prop;
+ Widget text, source, sink;
+ int i;
+
+ /* save default information */
+ if (fonts[0] == NULL) {
+ for (i = 0; i < 3; i++) {
+ num_args = 0;
+ XtSetArg(args[num_args], XtNfont, &fonts[i]); ++num_args;
+ XtSetArg(args[num_args], XtNforeground, &foregrounds[i]); ++num_args;
+ XtSetArg(args[num_args], XtNbackground, &backgrounds[i]); ++num_args;
+ XtGetValues(XawTextGetSink(texts[i]), args, num_args);
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ text = texts[i];
+ source = XawTextGetSource(text);
+ sink = XawTextGetSink(text);
+ item = FindTextSource(source, NULL);
+
+ XtSetArg(args[0], XawNtextProperties, &prop);
+ XtGetValues(sink, args, 1);
+
+ if (item == NULL || (!force && prop == item->properties))
+ continue;
+
+ XtSetArg(args[0], XawNtextProperties, item->properties);
+ num_args = 1;
+ if (item->properties == NULL) {
+ XtSetArg(args[num_args], XtNfont, fonts[i]); ++num_args;
+ XtSetArg(args[num_args], XtNforeground, foregrounds[i]); ++num_args;
+ XtSetArg(args[num_args], XtNbackground, backgrounds[i]); ++num_args;
+ }
+ XtSetValues(sink, args, num_args);
+
+ if (text == textwindow) {
+ XtSetArg(args[0], XtNdisplayCaret, False);
+ XtSetValues(text, args, 1);
+ }
+ _XawTextBuildLineTable((TextWidget)text,
+ XawTextTopPosition(text), True);
+ XawTextDisplay(text);
+ if (text == textwindow) {
+ XtSetArg(args[0], XtNdisplayCaret, True);
+ XtSetValues(text, args, 1);
+ }
+ }
+}
diff --git a/realpath.c b/realpath.c
new file mode 100644
index 0000000..97c5771
--- /dev/null
+++ b/realpath.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* $XFree86: xc/programs/xedit/realpath.c,v 1.6 2002/02/10 02:50:05 paulo Exp $ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)realpath.c 8.1 (Berkeley) 2/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef LISP
+#include "xedit.h"
+#endif
+
+#if defined(__Lynx__)
+#define NO_FCHDIR
+#endif
+
+#if defined(ISC)
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+#endif
+#ifndef MAXSYMLINKS
+#define MAXSYMLINKS 256
+#endif
+
+/*
+ * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
+ *
+ * Find the real name of path, by removing all ".", ".." and symlink
+ * components. Returns (resolved) on success, or (NULL) on failure,
+ * in which case the path which caused trouble is left in (resolved).
+ */
+char *
+realpath(path, resolved)
+ const char *path;
+ char *resolved;
+{
+ struct stat sb;
+#ifdef NO_FCHDIR
+ char start[MAXPATHLEN];
+#else
+ int fd;
+#endif
+ int n, rootd, serrno;
+ char *p, *q, wbuf[MAXPATHLEN];
+ int symlinks = 0;
+
+ /* Save the starting point. */
+#ifdef NO_FCHDIR
+ if (getcwd(start, MAXPATHLEN) == NULL) {
+ (void)strcpy(resolved, ".");
+ return (NULL);
+ }
+#else
+ if ((fd = open(".", O_RDONLY)) < 0) {
+ (void)strcpy(resolved, ".");
+ return (NULL);
+ }
+#endif
+
+ /*
+ * Find the dirname and basename from the path to be resolved.
+ * Change directory to the dirname component.
+ * lstat the basename part.
+ * if it is a symlink, read in the value and loop.
+ * if it is a directory, then change to that directory.
+ * get the current directory name and append the basename.
+ */
+ (void)strncpy(resolved, path, MAXPATHLEN - 1);
+ resolved[MAXPATHLEN - 1] = '\0';
+loop:
+ q = strrchr(resolved, '/');
+ if (q != NULL) {
+ p = q + 1;
+ if (q == resolved)
+ q = "/";
+ else {
+ do {
+ --q;
+ } while (q > resolved && *q == '/');
+ q[1] = '\0';
+ q = resolved;
+ }
+ if (chdir(q) < 0)
+ goto err1;
+ } else
+ p = resolved;
+
+ /* Deal with the last component. */
+ if (*p != '\0' && lstat(p, &sb) == 0) {
+ if (S_ISLNK(sb.st_mode)) {
+ if (++symlinks > MAXSYMLINKS) {
+ errno = ELOOP;
+ goto err1;
+ }
+ n = readlink(p, resolved, MAXPATHLEN);
+ if (n < 0)
+ goto err1;
+ resolved[n] = '\0';
+ goto loop;
+ }
+ if (S_ISDIR(sb.st_mode)) {
+ if (chdir(p) < 0)
+ goto err1;
+ p = "";
+ }
+ }
+
+ /*
+ * Save the last component name and get the full pathname of
+ * the current directory.
+ */
+ (void)strcpy(wbuf, p);
+ if (getcwd(resolved, MAXPATHLEN) == 0)
+ goto err1;
+
+ /*
+ * Join the two strings together, ensuring that the right thing
+ * happens if the last component is empty, or the dirname is root.
+ */
+ if (resolved[0] == '/' && resolved[1] == '\0')
+ rootd = 1;
+ else
+ rootd = 0;
+
+ if (*wbuf) {
+ if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ goto err1;
+ }
+ if (rootd == 0)
+ (void)strcat(resolved, "/");
+ (void)strcat(resolved, wbuf);
+ }
+
+ /* Go back to where we came from. */
+#ifdef NO_FCHDIR
+ if (chdir(start) < 0)
+#else
+ if (fchdir(fd) < 0)
+#endif
+ {
+ serrno = errno;
+ goto err2;
+ }
+
+ /* It's okay if the close fails, what's an fd more or less? */
+#ifndef NO_FCHDIR
+ (void)close(fd);
+#endif
+ return (resolved);
+
+err1: serrno = errno;
+#ifdef NO_FCHDIR
+ (void)chdir(start);
+#else
+ (void)fchdir(fd);
+#endif
+err2:
+#ifndef NO_FCHDIR
+ (void)close(fd);
+#endif
+ errno = serrno;
+ return (NULL);
+}
diff --git a/strcasecmp.c b/strcasecmp.c
new file mode 100644
index 0000000..e8ff2e9
--- /dev/null
+++ b/strcasecmp.c
@@ -0,0 +1,40 @@
+/* $XFree86: xc/programs/xedit/strcasecmp.c,v 1.3 2002/02/10 02:50:05 paulo Exp $ */
+
+#include <ctype.h>
+#include <sys/types.h>
+
+#ifndef LISP
+#include "xedit.h"
+#endif
+
+/* Just like the BSD version. It assumes that tolower() is ANSI-compliant */
+
+int
+strcasecmp(const char *s1, const char *s2)
+{
+ const unsigned char *us1 = (const unsigned char *)s1;
+ const unsigned char *us2 = (const unsigned char *)s2;
+
+ while (tolower(*us1) == tolower(*us2++))
+ if (*us1++ == '\0')
+ return 0;
+ return tolower(*us1) - tolower(*--us2);
+}
+
+int
+strncasecmp(const char *s1, const char *s2, size_t n)
+{
+ if (n != 0) {
+ const unsigned char *us1 = (const unsigned char *)s1;
+ const unsigned char *us2 = (const unsigned char *)s2;
+
+ do {
+ if (tolower(*us1) != tolower(*us2++))
+ return tolower(*us1) - tolower(*--us2);
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+ return 0;
+}
+
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..d38e7ae
--- /dev/null
+++ b/util.c
@@ -0,0 +1,896 @@
+/* $XConsortium: util.c,v 1.16 92/08/12 16:46:22 converse Exp $ */
+
+/*
+ * 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.
+ */
+/* $XFree86: xc/programs/xedit/util.c,v 1.25 2002/12/04 05:27:56 paulo Exp $ */
+
+#include <stdio.h>
+#include <stdlib.h> /* for realpath() */
+#include "xedit.h"
+
+#include <X11/Xfuncs.h>
+#include <X11/Xos.h> /* for types.h */
+
+#include <sys/stat.h>
+#include <X11/Xmu/CharSet.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SimpleMenu.h>
+
+/*
+ * Prototypes
+ */
+static void SwitchSourceCallback(Widget, XtPointer, XtPointer);
+static int WindowIndex(Widget);
+static void ChangeTextWindow(Widget);
+
+/*
+ * External
+ */
+extern void _XawTextShowPosition(TextWidget);
+
+/*
+ * Initialization
+ */
+extern Widget scratch;
+extern Widget vpanes[2], labels[3], texts[3], forms[3];
+extern XawTextWrapMode wrapmodes[3];
+
+/*
+ * Implementation
+ */
+void
+XeditPrintf(char *str)
+{
+ XawTextBlock text;
+ XawTextPosition pos = XawTextSourceScan(XawTextGetSource(messwidget),
+ 0, XawstAll, XawsdRight, 1, True);
+
+ text.length = strlen(str);
+ text.ptr = str;
+ text.firstPos = 0;
+ text.format = FMT8BIT;
+
+ XawTextReplace(messwidget, pos, pos, &text);
+
+ XawTextSetInsertionPoint(messwidget, pos + text.length);
+}
+
+Widget
+MakeCommandButton(Widget box, char *name, XtCallbackProc function)
+{
+ Widget w = XtCreateManagedWidget(name, commandWidgetClass, box, NULL, ZERO);
+ if (function != NULL)
+ XtAddCallback(w, XtNcallback, function, (caddr_t) NULL);
+ return w;
+}
+
+Widget
+MakeStringBox(Widget parent, String name, String string)
+{
+ Arg args[5];
+ Cardinal numargs = 0;
+ Widget StringW;
+
+ XtSetArg(args[numargs], XtNeditType, XawtextEdit); numargs++;
+ XtSetArg(args[numargs], XtNstring, string); numargs++;
+
+ StringW = XtCreateManagedWidget(name, asciiTextWidgetClass,
+ parent, args, numargs);
+ return(StringW);
+}
+
+/* Function Name: GetString
+ * Description: retrieves the string from a asciiText widget.
+ * Arguments: w - the ascii text widget.
+ * Returns: the filename.
+ */
+
+String
+GetString(Widget w)
+{
+ String str;
+ Arg arglist[1];
+
+ XtSetArg(arglist[0], XtNstring, &str);
+ XtGetValues( w, arglist, ONE);
+ return(str);
+}
+
+/* Function Name: MaybeCreateFile
+ * Description: Checks to see if file exists, and if not, creates it.
+ * Arguments: file - name of file to check.
+ * Returns: permissions status
+ */
+
+FileAccess
+MaybeCreateFile(char *file)
+{
+ Boolean exists;
+ int fd;
+
+ if (access(file, F_OK) != 0) {
+ fd = creat(file, 0666);
+ if (fd != -1)
+ close(fd);
+ }
+
+ return(CheckFilePermissions(file, &exists));
+}
+
+
+FileAccess
+CheckFilePermissions(char *file, Boolean *exists)
+{
+ char temp[BUFSIZ], *ptr;
+
+ if (access(file, F_OK) == 0) {
+ *exists = TRUE;
+
+ if (access(file, R_OK) != 0)
+ return(NO_READ);
+
+ if (access(file, R_OK | W_OK) == 0)
+ return(WRITE_OK);
+ return(READ_OK);
+ }
+
+ *exists = FALSE;
+
+ strncpy(temp, file, sizeof(temp) - 1);
+ temp[sizeof(temp) - 1] = '\0';
+ if ( (ptr = rindex(temp, '/')) == NULL)
+ strcpy(temp, ".");
+ else
+ *ptr = '\0';
+
+ if (access(temp, R_OK | W_OK | X_OK) == 0)
+ return(WRITE_OK);
+ return(NO_READ);
+}
+
+xedit_flist_item *
+AddTextSource(Widget source, char *name, char *filename, int flags,
+ FileAccess file_access)
+{
+ xedit_flist_item *item;
+ char *wid_name;
+
+ item = (xedit_flist_item*)XtMalloc(sizeof(xedit_flist_item));
+ item->source = source;
+ item->name = XtNewString(name);
+ item->filename = XtNewString(filename);
+ item->flags = flags;
+ item->file_access = file_access;
+ item->display_position = item->insert_position = 0;
+ item->mode = 0;
+ item->properties = NULL;
+ item->xldata = NULL;
+
+ flist.itens = (xedit_flist_item**)
+ XtRealloc((char*)flist.itens, sizeof(xedit_flist_item*)
+ * (flist.num_itens + 1));
+
+ flist.itens[flist.num_itens++] = item;
+
+ if (!flist.popup) {
+ flist.popup = XtCreatePopupShell("fileMenu", simpleMenuWidgetClass,
+ topwindow, NULL, 0);
+ /* XXX hack: this don't allow setting the geometry of the popup widget
+ * as it will override any settings when a menu entry is inserted
+ * or deleted, but saves us the trouble generated by a resource
+ * setting like: '*geometry: <width>x<height>'
+ */
+ XtRealizeWidget(flist.popup);
+ }
+ if ((wid_name = strrchr(item->name, '/')) == NULL)
+ wid_name = item->name;
+ else
+ ++wid_name;
+ item->sme = XtVaCreateManagedWidget(wid_name, smeBSBObjectClass,
+ flist.popup, XtNlabel, filename,
+ NULL, NULL);
+ XtAddCallback(item->sme, XtNcallback,
+ SwitchSourceCallback, (XtPointer)item);
+
+ SetTextProperties(item);
+
+ return (item);
+}
+
+Bool
+KillTextSource(xedit_flist_item *item)
+{
+ xedit_flist_item *nitem = NULL;
+ unsigned idx, i;
+ Arg targs[3];
+ Cardinal tnum_args;
+ Arg largs[2];
+ Cardinal lnum_args;
+ char label_buf[BUFSIZ];
+
+ for (idx = 0; idx < flist.num_itens; idx++)
+ if (flist.itens[idx] == item) {
+ if (idx + 1 < flist.num_itens)
+ nitem = flist.itens[idx + 1];
+ else if (idx - 1 >= 0)
+ nitem = flist.itens[idx - 1];
+ break;
+ }
+
+ if (idx >= flist.num_itens)
+ return (False);
+
+ flist.current = nitem;
+ if (item == flist.other)
+ flist.other = NULL;
+
+ if (nitem->file_access == READ_OK)
+ XmuSnprintf(label_buf, sizeof(label_buf), "%s READ ONLY",
+ nitem->name);
+ else if (nitem->file_access == WRITE_OK)
+ XmuSnprintf(label_buf, sizeof(label_buf), "%s Read - Write",
+ nitem->name);
+ lnum_args = 0;
+ XtSetArg(largs[lnum_args], XtNlabel, label_buf); ++lnum_args;
+ if (nitem->flags & CHANGED_BIT)
+ XtSetArg(largs[lnum_args], XtNleftBitmap, flist.pixmap);
+ else
+ XtSetArg(largs[lnum_args], XtNleftBitmap, None);
+ ++lnum_args;
+
+ tnum_args = 0;
+ XtSetArg(targs[tnum_args], XtNtextSource,
+ nitem->source); ++tnum_args;
+ XtSetArg(targs[tnum_args], XtNdisplayPosition,
+ nitem->display_position); ++tnum_args;
+ XtSetArg(targs[tnum_args], XtNinsertPosition,
+ nitem->insert_position); ++tnum_args;
+ for (i = 0; i < 3; i++)
+ if (XawTextGetSource(texts[i]) == item->source) {
+ XtSetValues(labels[i], largs, lnum_args);
+ XawTextDisableRedisplay(texts[i]);
+ XtSetValues(texts[i], targs, tnum_args);
+
+ UpdateTextProperties(0);
+
+ _XawTextShowPosition((TextWidget)texts[i]);
+ XawTextEnableRedisplay(texts[i]);
+ if (texts[i] == textwindow) {
+ Arg args[1];
+
+ if (nitem->source != scratch)
+ XtSetArg(args[0], XtNstring, nitem->name);
+ else
+ XtSetArg(args[0], XtNstring, NULL);
+ XtSetValues(filenamewindow, args, 1);
+ }
+ }
+
+ UnsetTextProperties(item);
+ XtFree(item->name);
+ XtFree(item->filename);
+ XtDestroyWidget(item->sme);
+ XtDestroyWidget(item->source);
+ XtFree((char*)item);
+
+ if (idx < flist.num_itens - 1)
+ memmove(&flist.itens[idx], &flist.itens[idx + 1],
+ (flist.num_itens - idx) * sizeof(xedit_flist_item*));
+
+ --flist.num_itens;
+
+ return (True);
+}
+
+xedit_flist_item *
+FindTextSource(Widget source, char *filename)
+{
+ unsigned i;
+
+ if (source) {
+ for (i = 0; i < flist.num_itens; i++)
+ if (flist.itens[i]->source == source)
+ return (flist.itens[i]);
+ }
+ else if (filename) {
+ for (i = 0; i < flist.num_itens; i++)
+ if (strcmp(flist.itens[i]->filename, filename) == 0)
+ return (flist.itens[i]);
+ }
+
+ return (NULL);
+}
+
+void
+SwitchTextSource(xedit_flist_item *item)
+{
+ Arg args[4];
+ Cardinal num_args;
+ char label_buf[BUFSIZ];
+ xedit_flist_item *old_item =
+ FindTextSource(XawTextGetSource(textwindow), NULL);
+ int i;
+
+ if (old_item != item)
+ flist.other = old_item;
+ flist.current = item;
+
+ XawTextDisableRedisplay(textwindow);
+ if (item->file_access == READ_OK)
+ XmuSnprintf(label_buf, sizeof(label_buf), "%s READ ONLY",
+ item->name);
+ else if (item->file_access == WRITE_OK)
+ XmuSnprintf(label_buf, sizeof(label_buf), "%s Read - Write",
+ item->name);
+ num_args = 0;
+ XtSetArg(args[num_args], XtNlabel, label_buf); ++num_args;
+ if (item->flags & CHANGED_BIT)
+ XtSetArg(args[num_args], XtNleftBitmap, flist.pixmap);
+ else
+ XtSetArg(args[num_args], XtNleftBitmap, None);
+ ++num_args;
+ XtSetValues(labelwindow, args, num_args);
+
+ for (i = 0; i < 3; i++)
+ if (XawTextGetSource(texts[i]) == item->source
+ && XtIsManaged(texts[i]))
+ break;
+
+ if (i < 3) {
+ num_args = 0;
+ XtSetArg(args[num_args], XtNdisplayPosition,
+ &(item->display_position)); ++num_args;
+ XtSetArg(args[num_args], XtNinsertPosition,
+ &(item->insert_position)); ++num_args;
+ XtGetValues(texts[i], args, num_args);
+ }
+ if (old_item != item) {
+ int count, idx = 0;
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNdisplayPosition,
+ &(old_item->display_position)); ++num_args;
+ XtSetArg(args[num_args], XtNinsertPosition,
+ &(old_item->insert_position)); ++num_args;
+ XtGetValues(textwindow, args, num_args);
+
+ for (count = 0, i = 0; i < 3; i++)
+ if (XawTextGetSource(texts[i]) == old_item->source
+ && XtIsManaged(texts[i])) {
+ if (++count > 1)
+ break;
+ idx = i;
+ }
+
+ if (count == 1) {
+ num_args = 0;
+ XtSetArg(args[num_args], XtNdisplayPosition,
+ &(old_item->display_position)); ++num_args;
+ XtSetArg(args[num_args], XtNinsertPosition,
+ &(old_item->insert_position)); ++num_args;
+ XtGetValues(texts[idx], args, num_args);
+ }
+ }
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNtextSource, item->source); ++num_args;
+ XtSetArg(args[num_args], XtNdisplayPosition, item->display_position);
+ ++num_args;
+ XtSetArg(args[num_args], XtNinsertPosition, item->insert_position);
+ ++num_args;
+ if (item->flags & WRAP_BIT)
+ XtSetArg(args[num_args], XtNwrap, item->wrap);
+ else
+ XtSetArg(args[num_args], XtNwrap, wrapmodes[WindowIndex(textwindow)]);
+ ++num_args;
+ XtSetValues(textwindow, args, num_args);
+
+ UpdateTextProperties(0);
+
+ _XawTextShowPosition((TextWidget)textwindow);
+ XawTextEnableRedisplay(textwindow);
+
+ num_args = 0;
+ if (item->source != scratch) {
+ XtSetArg(args[num_args], XtNstring, item->name); ++num_args;
+ }
+ else {
+ XtSetArg(args[num_args], XtNstring, NULL); ++num_args;
+ }
+ XtSetValues(filenamewindow, args, num_args);
+}
+
+/* XXX sizeof(name) must match argument size for realpath */
+static char name[BUFSIZ];
+char *
+ResolveName(char *filename)
+{
+ if (filename == NULL)
+ filename = GetString(filenamewindow);
+
+#ifndef __UNIXOS2__
+ return (realpath(filename, name));
+#else
+ return filename;
+#endif
+}
+
+static void
+ChangeTextWindow(Widget w)
+{
+ Arg args[1];
+
+ if (textwindow != w) {
+ xedit_flist_item *other, *current;
+
+ other = FindTextSource(XawTextGetSource(textwindow), NULL);
+ current = FindTextSource(XawTextGetSource(w), NULL);
+ if (other != current)
+ flist.other = other;
+ if (current)
+ flist.current = current;
+ XtSetArg(args[0], XtNdisplayCaret, False);
+ XtSetValues(textwindow, args, 1);
+ XtSetArg(args[0], XtNdisplayCaret, True);
+ XtSetValues(w, args, 1);
+ XawTextUnsetSelection(textwindow);
+ textwindow = w;
+ }
+}
+
+/*ARGSUSED*/
+void
+XeditFocus(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Arg args[1];
+ xedit_flist_item *item;
+ int idx = WindowIndex(w);
+
+ XtSetKeyboardFocus(topwindow, w);
+
+ ChangeTextWindow(w);
+
+ labelwindow = labels[idx];
+ item = FindTextSource(XawTextGetSource(textwindow), NULL);
+
+ if (item->source != scratch)
+ XtSetArg(args[0], XtNstring, item->name);
+ else
+ XtSetArg(args[0], XtNstring, NULL);
+
+ XtSetValues(filenamewindow, args, 1);
+}
+
+void
+PopupMenu(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Cardinal n_params = num_params ? *num_params : 0;
+
+ if (*num_params && XmuCompareISOLatin1(*params, "editMenu") == 0)
+ SetEditMenu();
+
+ XtCallActionProc(w, "XawPositionSimpleMenu", event, params, n_params);
+ XtCallActionProc(w, "XtMenuPopup", event, params, n_params);
+}
+
+/*ARGSUSED*/
+static void
+SwitchSourceCallback(Widget entry, XtPointer client_data, XtPointer call_data)
+{
+ SwitchTextSource((xedit_flist_item*)client_data);
+}
+
+static int
+WindowIndex(Widget w)
+{
+ int i;
+
+ for (i = 0; i < 3; i++)
+ if (texts[i] == w)
+ return (i);
+
+ return (-1);
+}
+
+void
+DeleteWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Widget unmanage[2];
+ int idx = WindowIndex(w), uidx;
+ Bool current = False;
+
+ if (*num_params == 1 && (*params[0] == 'd' || *params[0] == 'D')) {
+ if (XtIsManaged(XtParent(dirwindow)))
+ SwitchDirWindow(False);
+ return;
+ }
+
+ if (idx < 0 || (!XtIsManaged(texts[1]) && !XtIsManaged(texts[2]))) {
+ Feep();
+ return;
+ }
+
+ if (num_params && *num_params == 1 &&
+ (*params[0] == 'o' || *params[0] == 'O'))
+ current = True;
+
+ uidx = XtIsManaged(texts[1]) ? 1 : 2;
+
+ unmanage[0] = forms[uidx];
+ unmanage[1] = texts[uidx];
+ XtUnmanageChildren(unmanage, 2);
+
+ if (!XtIsManaged(texts[2]))
+ XtUnmanageChild(vpanes[1]);
+
+ if ((!current && idx == 0) || (current && idx != 0)) {
+ Arg args[4];
+ Cardinal num_args;
+ String label_str;
+ Pixmap label_pix;
+ XawTextPosition d_pos, i_pos;
+ Widget source;
+ xedit_flist_item *item;
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNlabel, &label_str); ++num_args;
+ XtSetArg(args[num_args], XtNleftBitmap, &label_pix); ++num_args;
+ XtGetValues(labels[current ? idx : uidx], args, num_args);
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNlabel, label_str); ++num_args;
+ XtSetArg(args[num_args], XtNleftBitmap, label_pix); ++num_args;
+ XtSetValues(labels[0], args, num_args);
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNdisplayPosition, &d_pos); ++num_args;
+ XtSetArg(args[num_args], XtNinsertPosition, &i_pos); ++num_args;
+ XtSetArg(args[num_args], XtNtextSource, &source); ++num_args;
+ XtGetValues(texts[current ? idx : uidx], args, num_args);
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNdisplayPosition, d_pos); ++num_args;
+ XtSetArg(args[num_args], XtNinsertPosition, i_pos); ++num_args;
+ XtSetArg(args[num_args], XtNtextSource, source); ++num_args;
+
+ item = FindTextSource(source, NULL);
+ if (item && (item->flags & WRAP_BIT))
+ XtSetArg(args[num_args], XtNwrap, item->wrap);
+ else
+ XtSetArg(args[num_args], XtNwrap,
+ wrapmodes[WindowIndex(texts[current ? idx : uidx])]);
+ XtSetValues(texts[0], args, num_args);
+
+ UpdateTextProperties(0);
+ }
+
+ labelwindow = labels[0];
+ XeditFocus(texts[0], NULL, NULL, NULL);
+}
+
+void
+SwitchSource(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ int idx = WindowIndex(w);
+ Widget source;
+ int i;
+
+ if (idx < 0 || textwindow != texts[idx]) {
+ Feep();
+ return;
+ }
+
+ source = XawTextGetSource(textwindow);
+
+ for (i = 0; i < flist.num_itens; i++)
+ if (flist.itens[i]->source == source) {
+ if (i > 0 && i == flist.num_itens - 1)
+ i = 0;
+ else if (i < flist.num_itens - 1)
+ ++i;
+ else {
+ Feep();
+ return;
+ }
+ break;
+ }
+
+ if (i >= flist.num_itens) {
+ Feep();
+ return;
+ }
+
+ SwitchTextSource(flist.itens[i]);
+}
+
+void
+OtherWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ int oidx, idx = WindowIndex(w);
+
+ if (idx < 0 || (!XtIsManaged(texts[1]) && !XtIsManaged(texts[2]))) {
+ Feep();
+ return;
+ }
+
+ if (idx == 0)
+ oidx = XtIsManaged(texts[1]) ? 1 : 2;
+ else
+ oidx = 0;
+
+ XeditFocus(texts[oidx], event, params, num_params);
+}
+
+void
+SplitWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Arg args[6];
+ Cardinal num_args;
+ Widget nlabel, ntext, source, sink, manage[2];
+ Dimension width, height, bw;
+ XawTextPosition i_pos, d_pos;
+ String label_str;
+ Pixmap label_pix;
+ int idx = WindowIndex(w), dimension;
+ Bool vert = True;
+ xedit_flist_item *item;
+
+ if (num_params && *num_params == 1
+ && (*params[0] == 'h' || *params[0] == 'H'))
+ vert = False;
+
+ if (idx < 0
+ || (vert && XtIsManaged(texts[1]))
+ || (!vert && XtIsManaged(vpanes[1]))) {
+ Feep();
+ return;
+ }
+
+ if (vert) {
+ nlabel = labels[1];
+ ntext = texts[1];
+ }
+ else {
+ nlabel = labels[2];
+ ntext = texts[2];
+ }
+ ChangeTextWindow(texts[idx]);
+ labelwindow = labels[idx];
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNinsertPosition, &i_pos); ++num_args;
+ XtSetArg(args[num_args], XtNdisplayPosition, &d_pos); ++num_args;
+ XtSetArg(args[num_args], XtNtextSource, &source); ++num_args;
+ XtSetArg(args[num_args], XtNtextSink, &sink); ++num_args;
+ XtSetArg(args[num_args], XtNwidth, &width); ++num_args;
+ XtSetArg(args[num_args], XtNheight, &height); ++num_args;
+ XtGetValues(w, args, num_args);
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNinternalBorderWidth, &bw); ++num_args;
+ XtGetValues(XtParent(w), args, num_args);
+
+ if (vert) {
+ dimension = (int)height - (((int)bw) << 1);
+ num_args = 0;
+ XtSetArg(args[num_args], XtNheight, &height); ++num_args;
+ XtGetValues(labelwindow, args, num_args);
+ dimension -= (int)height;
+ }
+ else
+ dimension = (int)width - (int)bw;
+
+ dimension >>= 1;
+
+ if (dimension <= 0 || dimension < XawTextSinkMaxHeight(sink, 3)) {
+ Feep();
+ return;
+ }
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNlabel, &label_str); ++num_args;
+ XtSetArg(args[num_args], XtNleftBitmap, &label_pix); ++num_args;
+ XtGetValues(labelwindow, args, num_args);
+
+ if (vert) {
+ if (XtIsManaged(texts[2])) {
+ manage[0] = forms[2];
+ manage[1] = texts[2];
+ XtUnmanageChildren(manage, 2);
+ XtUnmanageChild(vpanes[1]);
+ }
+ }
+ else {
+ if (XtIsManaged(texts[1])) {
+ manage[0] = forms[1];
+ manage[1] = texts[1];
+ XtUnmanageChildren(manage, 2);
+ }
+ }
+
+ XawTextDisableRedisplay(texts[0]);
+ XawTextDisableRedisplay(ntext);
+ if (textwindow == texts[1] || textwindow == texts[2]) {
+ num_args = 0;
+ XtSetArg(args[num_args], XtNdisplayPosition, d_pos); ++num_args;
+ XtSetArg(args[num_args], XtNinsertPosition, i_pos); ++num_args;
+ XtSetArg(args[num_args], XtNtextSource, source); ++num_args;
+ ChangeTextWindow(texts[0]);
+ XtSetValues(textwindow, args, num_args);
+ XtSetKeyboardFocus(topwindow, textwindow);
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNlabel, label_str); ++num_args;
+ XtSetArg(args[num_args], XtNleftBitmap, label_pix); ++num_args;
+ XtSetValues(labelwindow = labels[0], args, num_args);
+ }
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNlabel, label_str); ++num_args;
+ XtSetArg(args[num_args], XtNleftBitmap, label_pix); ++num_args;
+ XtSetValues(nlabel, args, num_args);
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNmin, dimension); ++num_args;
+ XtSetArg(args[num_args], XtNmax, dimension); ++num_args;
+ if (!vert)
+ XtSetValues(vpanes[1], args, num_args);
+ else
+ XtSetValues(ntext, args, num_args);
+
+ manage[0] = XtParent(nlabel);
+ manage[1] = ntext;
+ XtManageChildren(manage, 2);
+ if (!vert)
+ XtManageChild(vpanes[1]);
+
+ num_args = 0;
+ XtSetArg(args[num_args], XtNmin, 1); ++num_args;
+ XtSetArg(args[num_args], XtNmax, 65535); ++num_args;
+ if (!vert) {
+ XtSetValues(vpanes[1], args, num_args);
+ num_args = 0;
+ }
+ XtSetArg(args[num_args], XtNtextSource, source); ++num_args;
+ XtSetArg(args[num_args], XtNdisplayPosition, d_pos); ++num_args;
+ XtSetArg(args[num_args], XtNinsertPosition, i_pos); ++num_args;
+ item = FindTextSource(source, NULL);
+ if (item && (item->flags & WRAP_BIT))
+ XtSetArg(args[num_args], XtNwrap, item->wrap);
+ else
+ XtSetArg(args[num_args], XtNwrap, wrapmodes[WindowIndex(ntext)]);
+ ++num_args;
+ XtSetValues(ntext, args, num_args);
+
+ UpdateTextProperties(0);
+
+ _XawTextShowPosition((TextWidget)textwindow);
+ _XawTextShowPosition((TextWidget)ntext);
+
+ XawTextEnableRedisplay(textwindow);
+ XawTextEnableRedisplay(ntext);
+}
+
+void
+SwitchDirWindow(Bool show)
+{
+ static int map; /* There must be one instance of this
+ * variable per top level window */
+ Widget manage[2];
+
+ if (!show && XtIsManaged(XtParent(dirwindow))) {
+ manage[0] = dirlabel;
+ manage[1] = XtParent(dirwindow);
+ XtUnmanageChildren(manage, 2);
+ XtUnmanageChild(vpanes[1]);
+
+ XtManageChild(vpanes[0]);
+ if (map == 2) {
+ Arg args[2];
+ Dimension width, bw;
+
+ XtSetArg(args[0], XtNwidth, &width);
+ XtGetValues(texts[0], args, 1);
+ XtSetArg(args[0], XtNinternalBorderWidth, &bw);
+ XtGetValues(XtParent(texts[0]), args, 1);
+ width = (width - bw) >> 1;
+ XtSetArg(args[0], XtNmin, width);
+ XtSetArg(args[0], XtNmax, width);
+ XtSetValues(vpanes[0], args, 1);
+ manage[0] = forms[2];
+ manage[1] = texts[2];
+ XtManageChildren(manage, 2);
+ XtManageChild(vpanes[1]);
+ XtSetArg(args[0], XtNmin, 1);
+ XtSetArg(args[0], XtNmax, 65535);
+ XtSetValues(vpanes[0], args, 1);
+ }
+ }
+ else if (show && !XtIsManaged(XtParent(dirwindow))) {
+ XtUnmanageChild(vpanes[0]);
+ if (XtIsManaged(texts[2])) {
+ manage[0] = forms[2];
+ manage[1] = texts[2];
+ XtUnmanageChildren(manage, 2);
+ map = 2;
+ }
+ else {
+ map = XtIsManaged(texts[1]);
+ XtManageChild(vpanes[1]);
+ }
+
+ manage[0] = dirlabel;
+ manage[1] = XtParent(dirwindow);
+ XtManageChildren(manage, 2);
+ }
+}
+
+/*ARGSUSED*/
+void
+DirWindow(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Arg args[1];
+ char path[BUFSIZ + 1], *dir;
+
+ if (XtIsManaged(XtParent(dirwindow)))
+ return;
+
+ if (*num_params == 1) {
+ strncpy(path, params[0], sizeof(path - 2));
+ path[sizeof(path) - 2] = '\0';
+ }
+ else {
+ char *slash;
+ xedit_flist_item *item = FindTextSource(XawTextGetSource(textwindow), NULL);
+
+ if (item == NULL || item->source == scratch
+ || (slash = rindex(item->filename, '/')) == NULL)
+ strcpy(path, "./");
+ else {
+ int len = slash - item->filename + 1;
+
+ if (len > sizeof(path) - 2)
+ len = sizeof(path) - 2;
+ strncpy(path, item->filename, len);
+ path[len] = '\0';
+ }
+ }
+
+ dir = ResolveName(path);
+ if (dir != NULL) {
+ strncpy(path, dir, sizeof(path) - 2);
+ path[sizeof(path) - 2] = '\0';
+ if (*path && path[strlen(path) - 1] != '/')
+ strcat(path, "/");
+
+ XtSetArg(args[0], XtNlabel, "");
+ XtSetValues(dirlabel, args, 1);
+
+ SwitchDirWindow(True);
+ DirWindowCB(dirwindow, path, NULL);
+ }
+ else
+ Feep();
+}
diff --git a/xedit.c b/xedit.c
new file mode 100644
index 0000000..87d81bc
--- /dev/null
+++ b/xedit.c
@@ -0,0 +1,629 @@
+/* $XConsortium: xedit.c,v 1.28 94/03/26 17:06:28 rws Exp $ */
+
+/*
+ * 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.
+ */
+/* $XFree86: xc/programs/xedit/xedit.c,v 1.18 2002/11/10 23:21:57 paulo Exp $ */
+
+#include <X11/IntrinsicP.h>
+#include "xedit.h"
+#include <X11/Xaw/SmeBSB.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <X11/CoreP.h>
+
+#include <stdlib.h>
+
+#define randomize() srand((unsigned)time((time_t*)NULL))
+
+static XtActionsRec actions[] = {
+{"quit", QuitAction},
+{"save-file", SaveFile},
+{"load-file", LoadFile},
+{"find-file", FindFile},
+{"cancel-find-file", CancelFindFile},
+{"file-completion", FileCompletion},
+{"popup-menu", PopupMenu},
+{"kill-file", KillFile},
+{"split-window", SplitWindow},
+{"dir-window", DirWindow},
+{"delete-window", DeleteWindow},
+{"xedit-focus", XeditFocus},
+{"other-window", OtherWindow},
+{"switch-source", SwitchSource},
+#ifndef __UNIXOS2__
+{"lisp-eval", XeditLispEval},
+{"xedit-print-lisp-eval", XeditPrintLispEval},
+{"xedit-keyboard-reset",XeditKeyboardReset},
+#endif
+{"ispell", IspellAction},
+{"line-edit", LineEditAction}
+};
+
+#define DEF_HINT_INTERVAL 300 /* in seconds, 5 minutes */
+
+static Atom wm_delete_window;
+static Widget hintswindow;
+static int position_format_mask;
+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];
+
+extern void ResetSourceChanged(xedit_flist_item*);
+
+static void makeButtonsAndBoxes(Widget);
+static void HintsTimer(XtPointer, XtIntervalId*);
+static void PositionChanged(Widget, XtPointer, XtPointer);
+static void StartFormatPosition(void);
+static void StartHints(void);
+
+Display *CurDpy;
+
+struct _app_resources app_resources;
+struct _xedit_flist flist;
+
+#define Offset(field) XtOffsetOf(struct _app_resources, field)
+
+static XtResource resources[] = {
+ {"enableBackups", "EnableBackups", XtRBoolean, sizeof(Boolean),
+ Offset(enableBackups), XtRImmediate, FALSE},
+ {"backupNamePrefix", "BackupNamePrefix", XtRString, sizeof(char *),
+ Offset(backupNamePrefix),XtRString, ""},
+ {"backupNameSuffix", "BackupNameSuffix", XtRString, sizeof(char *),
+ Offset(backupNameSuffix),XtRString, ".BAK"},
+ {"hints", "Hint", XtRString, sizeof(char *),
+ Offset(hints.resource), XtRImmediate, NULL},
+ {"hintsInterval", XtCInterval, XtRInt, sizeof(long),
+ Offset(hints.interval), XtRImmediate, (XtPointer)DEF_HINT_INTERVAL},
+ {"changedBitmap", XtRBitmap, XtRString, sizeof(char*),
+ Offset(changed_pixmap_name), XtRString, "dot"},
+ {"positionFormat", "Format", XtRString, sizeof(char*),
+ Offset(position_format), XtRString, "L%l"},
+ {"autoReplace", "Replace", XtRString, sizeof(char*),
+ Offset(auto_replace), XtRImmediate, NULL},
+};
+
+#undef Offset
+
+int
+main(int argc, char *argv[])
+{
+ XtAppContext appcon;
+ unsigned num_loaded = 0;
+
+ XtSetLanguageProc(NULL, NULL, NULL);
+ topwindow = XtAppInitialize(&appcon, "Xedit", NULL, 0, &argc, argv, NULL, NULL, 0);
+
+ XtAppAddActions(appcon, actions, XtNumber(actions));
+ XtOverrideTranslations
+ (topwindow, XtParseTranslationTable ("<Message>WM_PROTOCOLS: quit()"));
+
+ XtGetApplicationResources(topwindow, (XtPointer) &app_resources, resources,
+ XtNumber(resources), NULL, 0);
+
+ CurDpy = XtDisplay(topwindow);
+ XawSimpleMenuAddGlobalActions(appcon);
+ XtRegisterGrabAction(PopupMenu, True,
+ ButtonPressMask | ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync);
+
+ makeButtonsAndBoxes(topwindow);
+
+ StartHints();
+ StartFormatPosition();
+ (void)StartHooks(appcon);
+ if (position_format_mask == 0) {
+ int i;
+
+ for (i = 0; i < 3; i++)
+ XtRemoveCallback(texts[i], XtNpositionCallback, PositionChanged, NULL);
+ }
+ XtRealizeWidget(topwindow);
+
+#ifndef __UNIXOS2__
+ XeditLispInitialize();
+#endif
+
+ options_popup = XtCreatePopupShell("optionsMenu", simpleMenuWidgetClass,
+ topwindow, NULL, 0);
+ XtRealizeWidget(options_popup);
+ XtAddCallback(XtCreateManagedWidget("ispell", smeBSBObjectClass,
+ options_popup, NULL, 0),
+ XtNcallback, IspellCallback, NULL);
+ CreateEditPopup();
+
+ wm_delete_window = XInternAtom(XtDisplay(topwindow), "WM_DELETE_WINDOW",
+ False);
+ (void) XSetWMProtocols (XtDisplay(topwindow), XtWindow(topwindow),
+ &wm_delete_window, 1);
+
+ /* This first call is just to save the default font and colors */
+ 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;
+ char buf[BUFSIZ];
+
+ for (i = 1; i < argc; i++) {
+ struct stat st;
+
+ num_args = 0;
+ filename = ResolveName(argv[i]);
+ if (filename == NULL || FindTextSource(NULL, filename) != NULL)
+ continue;
+ if (stat(filename, &st) == 0 && !S_ISREG(st.st_mode)) {
+ if (S_ISDIR(st.st_mode)) {
+ if (!num_loaded) {
+ char path[BUFSIZ + 1];
+
+ strncpy(path, filename, sizeof(path) - 2);
+ path[sizeof(path) - 2] = '\0';
+ if (*path) {
+ if (path[strlen(path) - 1] != '/')
+ strcat(path, "/");
+ }
+ else
+ strcpy(path, "./");
+ XtSetArg(args[0], XtNlabel, "");
+ XtSetValues(dirlabel, args, 1);
+ SwitchDirWindow(True);
+ DirWindowCB(dirwindow, path, NULL);
+ ++num_loaded;
+ }
+ continue;
+ }
+ }
+
+ switch (file_access = CheckFilePermissions(filename, &exists)) {
+ case NO_READ:
+ if (exists)
+ XmuSnprintf(buf, sizeof(buf), "File %s, %s %s", argv[i],
+ "exists, and could not be opened for",
+ "reading.\n");
+ else
+ XmuSnprintf(buf, sizeof(buf), "File %s %s %s %s", argv[i],
+ "does not exist, and",
+ "the directory could not be opened for",
+ "writing.\n");
+ break;
+ case READ_OK:
+ XtSetArg(args[num_args], XtNeditType, XawtextRead); num_args++;
+ XmuSnprintf(buf, sizeof(buf), "File %s opened READ ONLY.\n",
+ argv[i]);
+ break;
+ case WRITE_OK:
+ XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++;
+ XmuSnprintf(buf, sizeof(buf), "File %s opened read - write.\n",
+ argv[i]);
+ break;
+ }
+ if (file_access != NO_READ) {
+ int flags;
+
+ if (exists) {
+ flags = EXISTS_BIT;
+ XtSetArg(args[num_args], XtNstring, filename);num_args++;
+ }
+ else {
+ flags = 0;
+ XtSetArg(args[num_args], XtNstring, NULL); num_args++;
+ }
+ source = XtVaCreateWidget("textSource", international ?
+ multiSrcObjectClass
+ : asciiSrcObjectClass, topwindow,
+ XtNtype, XawAsciiFile,
+ XtNeditType, XawtextEdit,
+ NULL, NULL);
+ XtSetValues(source, args, num_args);
+ item = AddTextSource(source, argv[i], filename,
+ flags, file_access);
+ XtAddCallback(item->source, XtNcallback, SourceChanged,
+ (XtPointer)item);
+ if (exists && file_access == WRITE_OK)
+ item->mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ if (!num_loaded)
+ SwitchTextSource(item);
+ ++num_loaded;
+ ResetSourceChanged(item);
+ }
+ XeditPrintf(buf);
+ }
+ }
+
+ if (!flist.pixmap && strlen(app_resources.changed_pixmap_name)) {
+ XrmValue from, to;
+
+ from.size = strlen(app_resources.changed_pixmap_name);
+ from.addr = app_resources.changed_pixmap_name;
+ to.size = sizeof(Pixmap);
+ to.addr = (XtPointer)&(flist.pixmap);
+
+ XtConvertAndStore(flist.popup, XtRString, &from, XtRBitmap, &to);
+ }
+
+ if (num_loaded == 0) {
+ XtSetKeyboardFocus(topwindow, filenamewindow);
+ XtVaSetValues(textwindow, XtNwrap, XawtextWrapLine, NULL);
+ }
+ else
+ XtSetKeyboardFocus(topwindow, textwindow);
+
+ XtAppMainLoop(appcon);
+ exit(0);
+}
+
+static void
+makeButtonsAndBoxes(Widget parent)
+{
+ Widget outer, b_row, viewport;
+ Arg arglist[10];
+ Cardinal num_args;
+ xedit_flist_item *item;
+ static char *labelWindow = "labelWindow", *editWindow = "editWindow";
+ static char *formWindow = "formWindow", *positionWindow = "positionWindow";
+
+ outer = XtCreateManagedWidget("paned", panedWidgetClass, parent,
+ NULL, ZERO);
+
+ b_row = XtCreateManagedWidget("buttons", panedWidgetClass, outer, NULL, ZERO);
+ {
+ MakeCommandButton(b_row, "quit", DoQuit);
+ MakeCommandButton(b_row, "save", DoSave);
+ MakeCommandButton(b_row, "load", DoLoad);
+ filenamewindow = MakeStringBox(b_row, "filename", NULL);
+ }
+ hintswindow = XtCreateManagedWidget("bc_label", labelWidgetClass,
+ outer, NULL, ZERO);
+
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNeditType, XawtextEdit); ++num_args;
+ messwidget = XtCreateManagedWidget("messageWindow", asciiTextWidgetClass,
+ outer, arglist, num_args);
+
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNorientation, XtorientHorizontal); ++num_args;
+ hpane = XtCreateManagedWidget("hpane", panedWidgetClass, outer,
+ arglist, num_args);
+
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNorientation, XtorientVertical); ++num_args;
+ vpanes[0] = XtCreateManagedWidget("vpane", panedWidgetClass, hpane,
+ arglist, num_args);
+ XtSetArg(arglist[num_args], XtNheight, 1); ++num_args;
+ XtSetArg(arglist[num_args], XtNwidth, 1); ++num_args;
+ vpanes[1] = XtCreateWidget("vpane", panedWidgetClass, hpane,
+ arglist, num_args);
+
+ forms[0] = XtCreateManagedWidget(formWindow, formWidgetClass,
+ vpanes[0], NULL, 0);
+ labelwindow = XtCreateManagedWidget(labelWindow,labelWidgetClass,
+ forms[0], NULL, 0);
+ labels[0] = labelwindow;
+ positions[0] = XtCreateManagedWidget(positionWindow,labelWidgetClass,
+ forms[0], NULL, 0);
+
+ forms[2] = XtCreateWidget(formWindow, formWidgetClass,
+ vpanes[1], NULL, 0);
+ labels[2] = XtCreateManagedWidget(labelWindow,labelWidgetClass,
+ forms[2], NULL, 0);
+ positions[2] = XtCreateManagedWidget(positionWindow,labelWidgetClass,
+ forms[2], NULL, 0);
+
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNtype, XawAsciiFile); ++num_args;
+ XtSetArg(arglist[num_args], XtNeditType, XawtextEdit); ++num_args;
+ textwindow = XtCreateManagedWidget(editWindow, asciiTextWidgetClass,
+ vpanes[0], arglist, num_args);
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNinternational, &international); ++num_args;
+ XtGetValues(textwindow, arglist, num_args);
+
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNtype, XawAsciiFile); ++num_args;
+ XtSetArg(arglist[num_args], XtNeditType, XawtextEdit); ++num_args;
+ scratch = XtVaCreateWidget("textSource", international ?
+ multiSrcObjectClass
+ : asciiSrcObjectClass, topwindow,
+ XtNtype, XawAsciiFile,
+ XtNeditType, XawtextEdit,
+ NULL, NULL);
+ XtSetValues(scratch, arglist, num_args);
+
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNtextSource, scratch); ++num_args;
+ XtSetValues(textwindow, arglist, num_args);
+
+ texts[0] = textwindow;
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNtextSource, scratch); ++num_args;
+ XtSetArg(arglist[num_args], XtNdisplayCaret, False); ++num_args;
+ texts[2] = XtCreateWidget(editWindow, asciiTextWidgetClass,
+ vpanes[1], arglist, num_args);
+
+ forms[1] = XtCreateWidget(formWindow, formWidgetClass,
+ vpanes[0], NULL, 0);
+ labels[1] = XtCreateManagedWidget(labelWindow,labelWidgetClass,
+ forms[1], NULL, 0);
+ positions[1] = XtCreateManagedWidget(positionWindow,labelWidgetClass,
+ forms[1], NULL, 0);
+
+ texts[1] = XtCreateWidget(editWindow, asciiTextWidgetClass,
+ vpanes[0], arglist, num_args);
+
+ dirlabel = XtCreateWidget("dirlabel", labelWidgetClass,
+ vpanes[1], NULL, 0);
+ num_args = 0;
+ XtSetArg(arglist[num_args], XtNheight, 1); ++num_args;
+ XtSetArg(arglist[num_args], XtNwidth, 1); ++num_args;
+ viewport = XtCreateWidget("viewport", viewportWidgetClass,
+ vpanes[1], arglist, num_args);
+ dirwindow = XtCreateManagedWidget("dirwindow", listWidgetClass,
+ viewport, NULL, 0);
+
+ item = AddTextSource(scratch, "*scratch*", "*scratch*",
+ 0, WRITE_OK);
+ item->wrap = XawtextWrapLine;
+ item->flags |= WRAP_BIT;
+ XtAddCallback(item->source, XtNcallback, SourceChanged,
+ (XtPointer)item);
+ ResetSourceChanged(item);
+ flist.current = item;
+
+ for (num_args = 0; num_args < 3; num_args++)
+ XtAddCallback(texts[num_args], XtNpositionCallback, PositionChanged, NULL);
+
+ for (num_args = 0; num_args < 3; num_args++) {
+ XtSetArg(arglist[0], XtNwrap, &wrapmodes[num_args]);
+ XtGetValues(texts[num_args], arglist, 1);
+ }
+
+ XtAddCallback(dirwindow, XtNcallback, DirWindowCB, NULL);
+}
+
+/* Function Name: Feep
+ * Description: feeps the bell.
+ * Arguments: none.
+ * Returns: none.
+ */
+
+void
+Feep(void)
+{
+ XBell(CurDpy, 0);
+}
+
+#define l_BIT 0x01
+#define c_BIT 0x02
+#define p_BIT 0x04
+#define s_BIT 0x08
+#define MAX_FMT_LEN 30
+
+static void
+StartFormatPosition(void)
+{
+ char *fmt = app_resources.position_format;
+
+ if (fmt)
+ while (*fmt)
+ if (*fmt++ == '%') {
+ int len = 0;
+
+ if (*fmt == '-') {
+ ++fmt;
+ ++len;
+ }
+ while (*fmt >= '0' && *fmt <= '9') {
+ ++fmt;
+ if (++len >= MAX_FMT_LEN) {
+ XtAppWarning(XtWidgetToApplicationContext(topwindow),
+ "Format too large to formatPosition");
+ position_format_mask = 0;
+ return;
+ }
+ }
+ switch (*fmt++) {
+ case 'l': position_format_mask |= l_BIT; break;
+ case 'c': position_format_mask |= c_BIT; break;
+ case 'p': position_format_mask |= p_BIT; break;
+ case 's': position_format_mask |= s_BIT; break;
+ case '%': break;
+ default: {
+ char msg[256];
+
+ XmuSnprintf(msg, sizeof(msg),
+ "Unknown format \"%%%c\" in positionFormat",
+ fmt[-1]);
+ XtAppWarning(XtWidgetToApplicationContext(topwindow),
+ msg);
+ position_format_mask = 0;
+ return;
+ }
+ }
+ }
+}
+
+/*ARGSUSED*/
+static void
+PositionChanged(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ int idx;
+ XawTextPositionInfo *info = (XawTextPositionInfo*)call_data;
+
+ for (idx = 0; idx < 3; idx++)
+ if (w == texts[idx])
+ break;
+ if (idx > 2)
+ return;
+
+ if (((position_format_mask & l_BIT)
+ && infos[idx].line_number != info->line_number)
+ || ((position_format_mask & c_BIT)
+ && infos[idx].column_number != info->column_number)
+ || ((position_format_mask & p_BIT)
+ && infos[idx].insert_position != info->insert_position)
+ || ((position_format_mask & s_BIT)
+ && infos[idx].last_position != info->last_position)
+ || infos[idx].overwrite_mode != info->overwrite_mode) {
+ int len = 6;
+ Arg args[1];
+ char buffer[256], *str = app_resources.position_format;
+ char fmt_buf[MAX_FMT_LEN + 2], *fmt;
+
+ memcpy(&infos[idx], info, sizeof(XawTextPositionInfo));
+ if (info->overwrite_mode)
+ strcpy(buffer, "Ovrwt ");
+ else
+ strcpy(buffer, " ");
+ while (*str) {
+ switch (*str) {
+ case '%':
+ fmt = fmt_buf;
+ *fmt++ = *str++;
+ if (*str == '-')
+ *fmt++ = *str++;
+ /*CONSTCOND*/
+ while (*str >= '0' && *str <= '9') {
+ /* StartPositionFormat() already checked the format
+ * length.
+ */
+ *fmt++ = *str++;
+ }
+ *fmt++ = 'd';
+ *fmt = '\0';
+ switch (*str) {
+ case 'l':
+ XmuSnprintf(&buffer[len], sizeof(buffer) - len,
+ fmt_buf, info->line_number);
+ break;
+ case 'c':
+ XmuSnprintf(&buffer[len], sizeof(buffer) - len,
+ fmt_buf, info->column_number);
+ break;
+ case 'p':
+ XmuSnprintf(&buffer[len], sizeof(buffer) - len,
+ fmt_buf, info->insert_position);
+ break;
+ case 's':
+ XmuSnprintf(&buffer[len], sizeof(buffer) - len,
+ fmt_buf, info->last_position);
+ break;
+ case '%':
+ strcpy(&buffer[len], "%");
+ break;
+ }
+ len += strlen(&buffer[len]);
+ break;
+ default:
+ buffer[len++] = *str;
+ break;
+ }
+ if (len >= sizeof(buffer) - 1)
+ break;
+ ++str;
+ }
+ buffer[len] = '\0';
+
+ XtSetArg(args[0], XtNlabel, buffer);
+ XtSetValues(positions[idx], args, 1);
+ }
+}
+
+/*ARGSUSED*/
+static void
+HintsTimer(XtPointer closure, XtIntervalId *id)
+{
+ Arg args[1];
+ xedit_hints *hints = (xedit_hints*)closure;
+
+ hints->cur_hint = rand() % hints->num_hints;
+
+ XtSetArg(args[0], XtNlabel, hints->hints[hints->cur_hint]);
+ XtSetValues(hintswindow, args, 1);
+
+ hints->timer = XtAppAddTimeOut(XtWidgetToApplicationContext(topwindow),
+ hints->interval, HintsTimer, closure);
+}
+
+#define MAX_HINT_LEN 255
+#define MIN_HINT_INTERVAL 5
+static void
+StartHints(void)
+{
+ char *str, *p;
+ unsigned i, len;
+ xedit_hints *hints = &(app_resources.hints);
+
+ /* if resource was not set, or was overriden */
+ if (hints->resource == NULL || !*hints->resource)
+ return;
+
+ randomize();
+
+ if (hints->interval < MIN_HINT_INTERVAL)
+ hints->interval = DEF_HINT_INTERVAL;
+ hints->interval *= 1000;
+ hints->hints = (char**)XtMalloc(sizeof(char*));
+ hints->hints[hints->cur_hint = 0] = p = hints->resource;
+ hints->num_hints = 1;
+
+ while ((p = strchr(p, '\n')) != NULL) {
+ if (*++p == '\0')
+ break;
+ hints->hints = (char**)
+ XtRealloc((char*)hints->hints,
+ sizeof(char*) * (hints->num_hints + 1));
+ hints->hints[hints->num_hints++] = p;
+ }
+
+ /* make a private copy of the resource values, so that one can change
+ * the Xrm database safely.
+ */
+ for (i = 0; i < hints->num_hints; i++) {
+ if ((p = strchr(hints->hints[i], '\n')) != NULL)
+ len = p - hints->hints[i];
+ else
+ len = strlen(hints->hints[i]);
+ if (len > MAX_HINT_LEN)
+ len = MAX_HINT_LEN;
+ str = XtMalloc(len + 1);
+ strncpy(str, hints->hints[i], len);
+ str[len] = '\0';
+ hints->hints[i] = str;
+ }
+
+ hints->timer = XtAppAddTimeOut(XtWidgetToApplicationContext(topwindow),
+ hints->interval, HintsTimer,
+ (XtPointer)hints);
+}
diff --git a/xedit.h b/xedit.h
new file mode 100644
index 0000000..8ad7203
--- /dev/null
+++ b/xedit.h
@@ -0,0 +1,182 @@
+/*
+ * rcs_id[] = "$XConsortium: xedit.h,v 1.19 89/10/07 14:59:46 kit Exp $";
+ */
+
+/*
+ * 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.
+ */
+/* $XFree86: xc/programs/xedit/xedit.h,v 1.18 2002/11/10 23:21:57 paulo Exp $ */
+
+#include <stdio.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/cursorfont.h>
+#include <X11/Xatom.h>
+
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xaw/Cardinals.h>
+#include <X11/Xaw/SimpleMenu.h>
+
+#include <X11/IntrinsicP.h>
+#include <X11/Xaw/TextSinkP.h> /* for the property list definition */
+
+#include <X11/Xmu/SysUtil.h>
+
+typedef struct _xedit_hints {
+ char *resource;
+ unsigned long interval;
+ XtIntervalId timer;
+ char **hints;
+ unsigned num_hints;
+ unsigned cur_hint;
+} xedit_hints;
+
+typedef enum {NO_READ, READ_OK, WRITE_OK} FileAccess;
+
+typedef struct _XeditLispData XeditLispData;
+
+#define CHANGED_BIT 0x01
+#define EXISTS_BIT 0x02
+#define WRAP_BIT 0x10
+typedef struct _xedit_flist_item {
+ Widget source, sme;
+ String name;
+ String filename;
+ int flags;
+ FileAccess file_access;
+ XawTextPosition display_position, insert_position;
+ int mode;
+ XawTextPropertyList *properties;
+ XawTextWrapMode wrap;
+ XeditLispData *xldata;
+} xedit_flist_item;
+
+extern struct _xedit_flist {
+ Widget popup;
+ Pixmap pixmap;
+ xedit_flist_item **itens;
+ xedit_flist_item *current, *other;
+ Cardinal num_itens;
+} flist;
+
+extern Widget options_popup;
+
+extern struct _app_resources {
+ Boolean enableBackups;
+ char *backupNamePrefix;
+ char *backupNameSuffix;
+ xedit_hints hints;
+ char *changed_pixmap_name;
+ char *position_format;
+ char *auto_replace;
+} 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 */
+void XeditPrintf(char*);
+Widget MakeCommandButton(Widget, char*, XtCallbackProc);
+Widget MakeStringBox(Widget, String, String);
+String GetString(Widget);
+FileAccess MaybeCreateFile(char*), CheckFilePermissions(char*, Boolean*);
+xedit_flist_item *AddTextSource(Widget, String, String, int, FileAccess);
+xedit_flist_item *FindTextSource(Widget, char*);
+Bool KillTextSource(xedit_flist_item*);
+char *ResolveName(char*);
+void DeleteWindow(Widget, XEvent*, String*, Cardinal*);
+void SplitWindow(Widget, XEvent*, String*, Cardinal*);
+void SwitchTextSource(xedit_flist_item*);
+void PopupMenu(Widget, XEvent*, String*, Cardinal*);
+void OtherWindow(Widget, XEvent*, String*, Cardinal*);
+void SwitchSource(Widget, XEvent*, String*, Cardinal*);
+void XeditFocus(Widget, XEvent*, String*, Cardinal*);
+void SwitchDirWindow(Bool);
+void DirWindow(Widget, XEvent*, String*, Cardinal*);
+
+/* externs in commands.c */
+void DoQuit(Widget, XtPointer, XtPointer);
+void QuitAction(Widget, XEvent*, String*, Cardinal*);
+void DoSave(Widget, XtPointer, XtPointer);
+void SaveFile(Widget, XEvent*, String*, Cardinal*);
+void DoLoad(Widget, XtPointer, XtPointer);
+void CancelFindFile(Widget, XEvent*, String*, Cardinal*);
+void FindFile(Widget, XEvent*, String*, Cardinal*);
+void LoadFile(Widget, XEvent*, String*, Cardinal*);
+void FileCompletion(Widget, XEvent*, String*, Cardinal*);
+void KillFile(Widget, XEvent*, String*, Cardinal*);
+void DirWindowCB(Widget, XtPointer, XtPointer);
+void SourceChanged(Widget, XtPointer, XtPointer);
+
+/* externs in ispell.c */
+void IspellAction(Widget, XEvent*, String*, Cardinal*);
+void IspellCallback(Widget, XtPointer, XtPointer);
+
+/* externs in options.c */
+void CreateEditPopup(void);
+void SetEditMenu(void);
+void UpdateTextProperties(int);
+
+/* externs in hook.c */
+Bool StartHooks(XtAppContext);
+void LineEditAction(Widget, XEvent*, String*, Cardinal*);
+void LineEdit(Widget);
+
+/* externs in lisp.c */
+void XeditLispInitialize(void);
+void XeditLispEval(Widget, XEvent*, String*, Cardinal*);
+void XeditPrintLispEval(Widget, XEvent*, String*, Cardinal*);
+void XeditKeyboardReset(Widget, XEvent*, String*, Cardinal*);
+void XeditLispCleanUp(void);
+void SetTextProperties(xedit_flist_item*);
+void UnsetTextProperties(xedit_flist_item*);
+void CreateEditModePopup(Widget);
+void SetEditModeMenu(void);
+
+/* externs for system replacement functions */
+#ifdef NEED_STRCASECMP
+int strcasecmp(const char *s1, const char *s2);
+int strncasecmp(const char *s1, const char *s2, size_t n);
+#endif
+#ifdef NEED_REALPATH
+#include <sys/param.h>
+#if defined(ISC)
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+#endif
+char *realpath(const char *pathname, char resolvedname[MAXPATHLEN]);
+#endif
+
diff --git a/xedit.man b/xedit.man
new file mode 100644
index 0000000..96c5f91
--- /dev/null
+++ b/xedit.man
@@ -0,0 +1,443 @@
+.\" $XConsortium: xedit.man,v 1.23 94/04/17 20:43:39 matt Exp $
+.\" Copyright (c) 1994 X Consortium
+.\"
+.\" 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 X CONSORTIUM 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 X Consortium 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 X Consortium.
+.\"
+.\" $XFree86: xc/programs/xedit/xedit.man,v 1.18 2002/10/12 16:06:48 herrb Exp $
+.\"
+.TH XEDIT 1 __vendorversion__
+.SH NAME
+xedit \- simple text editor for X
+.SH SYNTAX
+.B xedit
+[
+.I \-toolkitoption
+\&.\|.\|. ] [
+.I filename
+\&.\|.\|. ]
+.SH DESCRIPTION
+.I Xedit
+provides a window consisting of the following four areas:
+.IP "Commands Section" 25
+A set of commands that allow you to exit \fIxedit\fP, save the file, or
+load a new file into the edit window.
+.IP "Message Window" 25
+Displays \fIxedit\fP messages. In addition, this window can be also used as
+a scratch pad.
+.IP "Filename Display"
+Displays the name of the file currently being edited, and whether this file
+is \fIRead-Write\fP or \fIRead Only\fP.
+.IP "Edit Window" 25
+Displays the text of the file that you are editing or creating.
+.SH OPTIONS
+.I Xedit
+accepts all of the standard X Toolkit command line
+options (see
+.IR X (__miscmansuffix__)).
+The order of the command line options is not important.
+.TP 8
+.I filename
+Specifies the file(s) that are to be loaded during start-up. This is the
+file which will be edited. If a file is not specified,
+.I xedit
+lets you load files or create new files after it has started up.
+.SH EDITING
+The Athena Text widget is used for the three sections of this
+application that allow text input. The characters typed will go to
+the Text widget that has the input focus, or the Text widget that the pointer
+cursor is currently over.
+.PP
+
+The following keystroke combinations are defined:
+.sp
+.nf
+.ta 1.0i 3.0i 4.5i
+Ctrl-a Beginning Of Line Meta-b Backward Word
+Ctrl-b Backward Character Meta-f Forward Word
+Ctrl-d Delete Next Character Meta-i Insert File
+Ctrl-e End Of Line Meta-k Kill To End Of Paragraph
+Ctrl-f Forward Character Meta-q Form Paragraph
+Ctrl-g Keyboard Reset Meta-v Previous Page
+Ctrl-h Delete Previous Character Meta-y Insert Current Selection
+Ctrl-j Newline And Indent Meta-z Scroll One Line Down
+Ctrl-k Kill To End Of Line Meta-d Delete Next Word
+Ctrl-l Redraw Display Meta-D Kill Word
+Ctrl-m Newline Meta-h Delete Previous Word
+Ctrl-n Next Line Meta-H Backward Kill Word
+Ctrl-o Newline And Backup Meta-< Beginning Of File
+Ctrl-p Previous Line Meta-> End Of File
+Ctrl-r Search/Replace Backward Meta-] Forward Paragraph
+Ctrl-s Search/Replace Forward Meta-[ Backward Paragraph
+Ctrl-t Transpose Characters
+Ctrl-u [\fInumber\fP] Multiply by 4 or \fInumber\fP Meta-Delete Delete Previous Word
+Ctrl-v Next Page Meta-Shift Delete Kill Previous Word
+Ctrl-w Kill Selection Meta-Backspace Delete Previous Word
+Ctrl-y Unkill Meta-Shift Backspace Kill Previous Word
+Ctrl-z Scroll One Line Up Meta-z Scroll One Line Down
+Ctrl-_ Undo
+Escape Line Edit Mode
+.sp
+In addition, the pointer may be used to cut and paste text:
+.ta .5i 2.0i
+ Button 1 Down Start Selection
+ Button 1 Motion Adjust Selection
+ Button 1 Up End Selection (cut)
+
+ Button 2 Down Insert Current Selection (paste)
+
+ Button 3 Down Extend Current Selection
+ Button 3 Motion Adjust Selection
+ Button 3 Up End Selection (cut)
+.fi
+.sp
+
+.SH LINE EDIT MODE
+.I Line edit mode
+enables several shortcut commands for searching and replacing text in a
+xedit buffer.
+.I Line edit mode
+commands have the format:
+.br
+.nf
+
+ [\fIline-number\fP[,\fIline-number\fP]]\fIcommand\fP[\fIparameters\fP]
+
+.fi
+.I Line number
+may be specified as:
+.TP 8
+\&.
+The current text line.
+.TP 8
+$
+The last line of the file.
+.TP 8
+number
+The literal line \fInumber\fP.
+.TP 8
+- or ^
+The previous line. Equivalent to \fI-1\fP.
+.TP 8
+-number or ^number
+The current line minus \fInumber\fP.
+.TP 8
++
+The next line. Equivalent to \fI+1\fP.
+.TP 8
++number
+The current line plus \fInumber\fP.
+.TP 8
+, or %
+From the first to the last line. Equivalent to \fI1,$\fP.
+.TP 8
+;
+From the current to the last line. Equivalent to \fI.,$\fP.
+
+.PP
+.I Command
+may be specified as:
+.TP 8
+s
+Substitute text in the specified lines.
+.TP 8
+/re/
+Search forward for the regular expression pattern \fIre\fP.
+.TP 8
+?re?
+Search backward for the regular expression pattern \fIre\fP.
+
+.PP
+.I Parameters
+may be specified as:
+.TP 8
+/re/
+Works as a parameter to \fIi\fP or as a command.
+.TP 8
+/re/text/
+Search forward for \fIre\fP and substitute by \fItext\fP.
+
+.PP
+.I Options
+may follow or be parameters, known values are:
+.TP 8
+i
+Case insensitive search.
+.TP 8
+g
+\fIGlobal\fP match when replacing text. Unless specified, only the nth,
+that defaults to 1, match will be replaced.
+.TP 8
+c
+\fIConfirm\fP before replacing text.
+.TP 8
+number
+Replace only the occurrence referenced by \fInumber\fP.
+.PP
+
+Commands accept some variations, examples:
+.br
+.nf
+ /pattern/i
+ i/pattern/
+ i/pattern
+.fi
+Search forward for \fIpattern\fP.
+.br
+.nf
+
+ ,sc/pattern/text
+ ,sc/pattern/text/
+ ,s/pattern/text/c
+.fi
+Search the entire buffer and ask confirmation to replace \fIpattern\fP
+with \fItext\fP.
+.br
+.nf
+
+ ,s/pattern/text/number
+.fi
+Replace the match \fInumber\fP in the text line. If not specified,
+defaults to the first occurrence.
+
+.PP
+When searching for text, type <Return> to go to the next match.
+When interactively replacing text, type \fIy\fP or \fIY\fP to accept the
+change, and \fIn\fP or \fIN\fP to ignore it and go to the next match.
+
+.SH COMMANDS
+.IP Quit 8
+Quits the current editing session. If any changes have not been saved,
+.I xedit
+displays a warning message, allowing the user to save them.
+.IP Save
+If file backups are enabled (see RESOURCES, below)
+.I xedit
+stores a copy of the
+original, unedited file in <prefix>\fIfile\fP<suffix>,
+then overwrites the \fIfile\fP with the contents of the edit window. The
+filename is retrieved from the Text widget directly to the right of
+the \fILoad\fP button.
+.IP Load
+Loads the file named in the text widget immediately to the right
+of the this button and displays it in the Edit window.
+.SH RESOURCES
+For \fIxedit\fP the available resources are:
+.TP 8
+.B enableBackups (\fPClass\fB EnableBackups)
+Specifies that, when edits made to an existing file are saved,
+.I xedit
+is to copy the original version of that file to <prefix>\fIfile\fP<suffix>
+before it saves the changes. The default value for this resource is
+``on,'' stating that backups should be created.
+.TP 8
+.B backupNamePrefix (\fPClass\fB BackupNamePrefix)
+Specifies a string that is to be prepended to the backup filename. The
+default is that no string shall be prepended.
+.TP 8
+.B backupNameSuffix (\fPClass\fB BackupNameSuffix)
+Specifies a string that is to be appended to the backup filename. The
+default is to use ``~'' as the suffix.
+.TP 8
+.B positionFormat (\fPClass\fB Format)
+Specifies a format string used to display the cursor position. This
+string uses printf(3) like notation, where
+.B %l
+prints the line number,
+.B %c
+prints the column number,
+.B %p
+prints the insert position offset, and
+.B %s
+prints the current file size. It is also allowed to specifie field sizes,
+with the notation
+.B %\-?[0\-9]+
+\&. The default format string is ``L%l'', which shows the character ``L''
+followed by the line number.
+.TP 8
+.B hints (\fPClass\fB Hints)
+Specifies a list of strings, separated by new lines, that will be
+displayed in the bc_label window.
+.TP 8
+.B hintsInterval (\fPClass \fBInterval)
+Specifies the interval in seconds, which the hint string in the bc_label
+window will be changed.
+.TP 8
+.B changedBitmap (\fPClass \fBBitmap)
+Specifies the name of the Bitmap that will be displayed in the fileMenu, when
+the file being edited is changed.
+.TP 8
+.B autoReplace (\fPClass \fBReplace)
+This resource is useful to automatically correct common mispelling errors, but
+can also be used to create simple macros. The format is
+\fI{non-blanks}{blanks}[{string}]\fP. Fields are separeted by newlines.
+Example of use:
+\"
+.br
+.nf
+nto not\\n\\
+/macro some long string with \\\\\\n newlines \\\\\\n
+.fi
+\"
+.br
+Will automatically replace the word \fInto\fP by \fInot\fP, and \fI/macro\fP
+by \fIsome long string with
+ newlines
+\fP
+when you type that words.
+.TP 8
+.B ispell.dictionaries (\fPClass \fBispell.Dictionary)
+Specifies a list of dictionary names, separeted by spaces, available to the
+ispell program. The default value is \fI"american americamed+ english"\fP.
+.TP 8
+.B ispell.dictionary (\fPClass \fBispell.Dictionary)
+Specifies the default dictionary to use.
+.TP 8
+.B ispell*<DICTIONARY>.wordChars (\fPClass \fBispell*Chars)
+Specifies a set of characters that can be part of a legal word. The
+\fI<DICTIONARY>\fP field is one of the dictionaries specified in the
+\fIdictionaries\fP resource.
+.TP 8
+.B ispell.ispellCommand (\fPClass \fBispell.CommandLine)
+The path to the ispell program, and possibly, additional arguments. You don't
+need to specify the ``-w'' option, neither the ``-a'' option.
+Refer to the \fIispell\fP(1) manpage for more information on ispell options.
+.TP 8
+.B ispell.formatting (\fPClass \fPispell.TextFormat)
+Specifies which text formatting to use while spell checking the file. The
+available formats are \fItext\fP and \fIhtml\fP.
+.TP 8
+.B ispell*text.skipLines (\fPClass \fBispell*text.Skip)
+Lines starting with one of the characters in this string will not be spell
+checked. This resource is only used in \fItext\fP mode.
+.TP 8
+.B ispell.terseMode (\fPClass \fBispell.Terse)
+When enabled, runs ispell in terse mode, not asking user interaction for words
+generated through compound formation (when using the ispell ``-C'' option), or
+words generated through affix removal. The default value is False.
+.TP 8
+.B ispell.lookCommand (\fPClass \fBispell.CommandLine)
+The path to the program to search for alternate words, and possibily,
+additional arguments. The default program used is \fI/usr/bin/egrep\fP.
+.TP 8
+.B ispell.wordsFile (\fPClass \fBispell.Words)
+The path to the file[s] to search for alternate words. The default file is
+\fI/usr/share/dict/words\fP.
+.TP 8
+.B ispell.guessLabel (\fPClass \fBispell.Status)
+String displayed in the ispell status bar when ispell returns a guess list
+of one or more words. The default value is \fIGuess\fP.
+.TP 8
+.B ispell.missLabel (\fPClass \fBispell.Status)
+String displayed in the ispell status bar when ispell returns a list of one
+or more words to match a mispelled one. The default value is \fIMiss\fP.
+.TP 8
+.B ispell.rootLabel (\fPClass \fBispell.Status)
+String displayed in the ispell status bar when the word is not in the dictionary,
+but it can be formed through a root one. The default value is \fIRoot:\fP, and is
+followed by a space and the root word.
+.TP 8
+.B ispell.noneLabel (\fPClass \fBispell.Status)
+String displayed in the ispell status bar when there is no near misses. The default
+value is \fINone\fP.
+.TP 8
+.B ispell.compoundLabel (\fPClass \fBispell.Status)
+String displayed in the ispell status bar when the word being checked is formed by
+concatenation of two words. The default value is \fICompound\fP.
+.TP 8
+.B ispell.okLabel (\fPClass \fBispell.Status)
+String displayed in the ispell status bar when the checked word is in the dictionary.
+This string is only displayed when using the \fIcheck\fP button in the xedit ispell
+interface. The default value is \fIOk\fP.
+.TP 8
+.B ispell.eofLabel (\fPClass \fBispell.Status)
+The string displayed in the ispell status bar when the end of the file is reached.
+The default value is \fIEnd Of File\fP.
+.TP 8
+.B ispell.repeatLabel (\fPClass \fBispell.Status)
+The string displayed in the ispell status bar when two indentical words are found
+together in the file. The default value is \fIRepeat\fP.
+.TP 8
+.B ispell.lookLabel (\fPClass \fBispell.Status)
+The string displayed in the ispell status bar after displaying the results of
+the \fILook\fP command. If no results are found, the value of the
+\fIispell.noneLabel\fP resource is shown.
+.TP 8
+.B ispell.workingLabel (\fPClass \fBispell.Status)
+The string displayed in the ispell status bar while xedit is communicating with
+ispell. The default value is \fI...\fP.
+.SH WIDGETS
+In order to specify resources, it is useful to know the hierarchy of
+the widgets which compose \fIxedit\fR. In the notation below,
+indentation indicates hierarchical structure. The widget class name
+is given first, followed by the widget instance name.
+.sp
+.nf
+.ta .5i 1.0i 1.5i 2.0i
+Xedit xedit
+ Paned paned
+ Paned buttons
+ Command quit
+ Command save
+ Command load
+ Text filename
+ Label bc_label
+ Text messageWindow
+ Label labelWindow
+ Text editWindow
+.fi
+.sp
+.SH ENVIRONMENT
+.PP
+.TP 20
+.I DISPLAY
+to get the default host and display number.
+.TP 20
+.I XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH FILES
+.TP 20
+.I __apploaddir__/Xedit
+specifies required resources
+.SH "SEE ALSO"
+.IR X (__miscmansuffix__),
+.IR xrdb (1),
+.I "Athena Widget Set"
+.SH RESTRICTIONS
+Xedit is not a replacement to Emacs.
+.SH COPYRIGHT
+Copyright 1988, Digital Equipment Corporation.
+.br
+Copyright 1989, X Consortium
+.br
+Copyright 1998, The XFree86 Project
+.br
+See
+.IR X (__miscmansuffix__)
+for a full statement of rights and permissions.
+.SH AUTHORS
+Chris D. Peterson, MIT X Consortium
+.br
+Paulo Cesar Pereira de Andrade, The XFree86 Project