]> granicus.if.org Git - graphviz/commitdiff
add lefty, dotty, lneato to graphviz2 tree
authorellson <devnull@localhost>
Thu, 6 Jan 2005 15:01:43 +0000 (15:01 +0000)
committerellson <devnull@localhost>
Thu, 6 Jan 2005 15:01:43 +0000 (15:01 +0000)
21 files changed:
cmd/lefty/Makefile.am [new file with mode: 0644]
cmd/lefty/Makefile.old [new file with mode: 0644]
cmd/lefty/aix_mods/common.h [new file with mode: 0644]
cmd/lefty/aix_mods/exec.c [new file with mode: 0644]
cmd/lefty/aix_mods/tbl.c [new file with mode: 0644]
cmd/lefty/code.c [new file with mode: 0644]
cmd/lefty/code.h [new file with mode: 0644]
cmd/lefty/colors.txt [new file with mode: 0644]
cmd/lefty/common.c [new file with mode: 0644]
cmd/lefty/common.h [new file with mode: 0644]
cmd/lefty/display.c [new file with mode: 0644]
cmd/lefty/display.h [new file with mode: 0644]
cmd/lefty/exec.c [new file with mode: 0644]
cmd/lefty/exec.h [new file with mode: 0644]
cmd/lefty/g.c [new file with mode: 0644]
cmd/lefty/g.h [new file with mode: 0644]
cmd/lefty/gfxview.c [new file with mode: 0644]
cmd/lefty/gfxview.h [new file with mode: 0644]
cmd/lefty/internal.c [new file with mode: 0644]
cmd/lefty/internal.h [new file with mode: 0644]
cmd/lefty/lefty.pdf [new file with mode: 0644]

diff --git a/cmd/lefty/Makefile.am b/cmd/lefty/Makefile.am
new file mode 100644 (file)
index 0000000..3f1a99e
--- /dev/null
@@ -0,0 +1,48 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = ws os cs2l dot2l examples
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/lefty/ws/x11 \
+       -I$(top_srcdir)/lefty/os/unix \
+       -I$(top_srcdir)/lefty/dot2l @XAW_INCLUDES@
+
+man = lefty.1
+pdf = lefty.pdf
+
+leftydir = $(pkgdatadir)/lefty
+pdfdir = $(pkgdatadir)/doc/pdf
+
+AM_CFLAGS = -DLEFTYPATH=\"$(leftydir)\" -DHAVEDOT @X_CFLAGS@
+
+noinst_HEADERS = code.h common.h display.h exec.h g.h gfxview.h internal.h \
+       io.h lex.h mem.h parse.h str.h tbl.h txtview.h
+if WITH_X
+bin_PROGRAMS = lefty
+man_MANS = $(man)
+pdf_DATA = $(pdf)
+endif
+
+lefty_SCRIPTS = lefty.psp
+
+lefty_SOURCES = code.c common.c display.c exec.c g.c gfxview.c internal.c \
+       lefty.c lex.c mem.c parse.c str.c tbl.c txtview.c
+
+#
+# NB. -lXaw must be before -lXt or strange inability-to-get-mouse-focus
+# problems can occur on some systems.
+#
+lefty_LDADD = $(top_builddir)/lefty/dot2l/libdot2l.la \
+       $(top_builddir)/lefty/ws/x11/libws.la \
+       $(top_builddir)/lefty/ws/x11/libfilereq/libfilereq.la \
+       $(top_builddir)/lefty/os/unix/libos.la \
+       @X_LIBS@ @XAW_LIBS@ @X_PRE_LIBS@ @XPM_LIBS@ \
+       -lXt -lXmu -lXext -lX11 @SOCKET_LIBS@ @MATH_LIBS@
+
+.1.pdf:
+       groff -Tps -man $< | ps2pdf - - >$@
+
+EXTRA_DIST = $(lefty_SCRIPTS) $(lefty_SOURCES) $(man) $(pdf) \
+       colors.txt Makefile.old aix_mods
+
+DISTCLEANFILES = $(pdf)
diff --git a/cmd/lefty/Makefile.old b/cmd/lefty/Makefile.old
new file mode 100644 (file)
index 0000000..22a08b7
--- /dev/null
@@ -0,0 +1,158 @@
+all:   lefty
+
+include ../Config.mk
+include ../makearch/$(ARCH)
+
+DOT2L = dot2l
+WM = ws/x11
+OS = os/unix
+
+LEFTYLIBDIR= $(LIBDIR)/lefty
+DEFINES=-DLEFTYPATH=\"$(LEFTYLIBDIR)\" -DHAVEDOT -DHAVE_CONFIG_H
+
+INCS = -I. -I.. \
+       -I$(DOT2L) \
+       -I$(X11INC) \
+       -I$(WM) \
+       -I$(WM)/libfilereq \
+       -I$(OS)
+
+DOT2LEFTYSRC = $(DOT2L)/dotparse.y $(DOT2L)/dot2l.c \
+       $(DOT2L)/dotlex.c $(DOT2L)/dottrie.c
+DOT2LEFTYOBJ = dotparse.o dot2l.o dotlex.o dottrie.o
+
+OSSRC = $(OS)/io.c
+OSOBJ = io.o
+
+SRCS = lefty.c gfxview.c txtview.c internal.c display.c str.c exec.c parse.c \
+       lex.c $(DOT2LEFTYSRC) tbl.c code.c
+
+OBJS = lefty.o gfxview.o txtview.o internal.o display.o str.o exec.o parse.o \
+       lex.o $(DOT2LEFTYOBJ) tbl.o code.o io.o
+
+GSRC = g.c gcommon.c garray.c gbutton.c gcanvas.c glabel.c gmenu.c \
+    gpcanvas.c gquery.c gscroll.c gtext.c gview.c mem.c common.c \
+    SelFile.c Draw.c Path.c Dir.c
+
+GSRC =  g.c $(WM)/gcommon.c $(WM)/garray.c $(WM)/gbutton.c $(WM)/gcanvas.c \
+               $(WM)/glabel.c $(WM)/gmenu.c $(WM)/gpcanvas.c $(WM)/gquery.c \
+               $(WM)/gscroll.c $(WM)/gtext.c $(WM)/gview.c mem.c common.c \
+               $(WM)/libfilereq/SelFile.c $(WM)/libfilereq/Draw.c \
+        $(WM)/libfilereq/Path.c $(WM)/libfilereq/Dir.c 
+
+GOBJS = g.o gcommon.o garray.o gbutton.o gcanvas.o glabel.o gmenu.o \
+    gpcanvas.o gquery.o gscroll.o gtext.o gview.o mem.o common.o \
+    SelFile.o Draw.o Path.o Dir.o
+
+lefty: $(OBJS) libgfx.a
+       $(CC) $(LDFLAGS) -L. -L$(X11LIB) $(OBJS) -lgfx -lXaw -lXmu -lXt -lXext -lX11 -lm  $(LEFTYLIBS) -o lefty
+
+libgfx.a : $(GOBJS)
+       $(AR) cr libgfx.a $(GOBJS)
+
+# lefty.static is not completely static because we don't want to
+# bind libc unnecessarily.  we mostly wanted to bind things like X11
+# and tcl that are likely to cause headaches when porting binaries.
+lefty.static : lefty.static.$(ARCH)
+
+lefty.static.linux: $(OBJS)
+       $(CC) -o lefty $(OBJS) \
+               $(X11LIB)/libXaw.a $(X11LIB)/libXmu.a $(X11LIB)/libXt.a \
+               $(X11LIB)/libXext.a $(X11LIB)/libX11.a $(X11LIB)/libSM.a \
+               $(X11LIB)/libICE.a -lm
+
+lefty.static.hp.pa: $(OBJS)
+       $(CC) $(LDFLAGS) -o lefty $(OBJS) \
+       $(HOME)/lib/libXaw.a $(HOME)/lib/libXmu.a $(X11LIB)/libXt.a \
+       $(X11LIB)/libXext.a $(X11LIB)/libX11.a -lm
+
+lefty.static.sol.sun4: $(OBJS)
+       $(CC) -o lefty $(OBJS) \
+               $(X11LIB)/libXaw.a $(X11LIB)/libXmu.a $(X11LIB)/libXt.a \
+               $(X11LIB)/libX11.a $(X11LIB)/libXext.a \
+               -ldl /lib/libsocket.a /lib/libnsl.a /lib/libw.a /lib/libintl.a -lm
+
+lefty.static.sun4: $(OBJS)
+       $(CC) -o lefty $(OBJS) \
+               $(X11LIB)/libXaw.a $(X11LIB)/libXmu.a $(X11LIB)/libXt.a \
+               $(X11LIB)/libX11.a $(X11LIB)/libXext.a -lm
+
+dot2l.o:       $(DOT2L)/dot2l.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+dotlex.o:      $(DOT2L)/dotlex.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+dottrie.o:     $(DOT2L)/dottrie.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gcommon.o:     $(WM)/gcommon.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+garray.o:      $(WM)/garray.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gbutton.o:     $(WM)/gbutton.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gcanvas.o:     $(WM)/gcanvas.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+glabel.o:      $(WM)/glabel.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gmenu.o:       $(WM)/gmenu.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gpcanvas.o:    $(WM)/gpcanvas.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gquery.o:      $(WM)/gquery.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gscroll.o:     $(WM)/gscroll.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gtext.o:       $(WM)/gtext.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+gview.o:       $(WM)/gview.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+Dir.o: $(WM)/libfilereq/Dir.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+Draw.o:        $(WM)/libfilereq/Draw.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+Path.o:        $(WM)/libfilereq/Path.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+SelFile.o:     $(WM)/libfilereq/SelFile.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+io.o:  $(OS)/io.c
+       $(CC) -c $(CCFLAGS) $(INCS) $(DEFINES) $?
+
+dotparse.c: $(DOT2L)/dotparse.y
+       $(YACC) -d $(DOT2L)/dotparse.y
+       $(MV) y.tab.c dotparse.c
+       $(MV) y.tab.h dotparse.h
+
+dotparse.o: dotparse.c
+
+install: lefty
+       $(MKPATH) $(BINDIR)
+       $(INSTALL) lefty $(BINDIR)
+       $(MKPATH) $(MANDIR)
+       $(INSTALL) lefty.1 $(MANDIR)
+       $(MKPATH) $(LIBDIR)
+       $(INSTALL) libgfx.a $(LIBDIR)
+       $(MKPATH) $(LIBDIR)/lefty
+       $(INSTALL) lefty.psp $(LIBDIR)/lefty
+
+clean:
+       $(RM) core *.o dotparse.[ch]
+
+distclean: clean
+       $(RM) lefty *.a
diff --git a/cmd/lefty/aix_mods/common.h b/cmd/lefty/aix_mods/common.h
new file mode 100644 (file)
index 0000000..28a7adc
--- /dev/null
@@ -0,0 +1,92 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+#ifdef HAVECS
+#include <ast.h>
+#else
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#include <sys/select.h>
+#include <math.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <ctype.h>
+
+#ifdef MSWIN32
+#include <windows.h>
+#include <commdlg.h>
+#include <malloc.h>
+#endif
+
+#define POS __FILE__, __LINE__
+
+#ifndef TRUE
+#define TRUE  1
+#define FALSE 0
+#endif
+
+#ifndef L_SUCCESS
+#define L_SUCCESS 1
+#define L_FAILURE 0
+#endif
+
+#define CHARSRC 0
+#define FILESRC 1
+
+/*#define M_PI 3.14159265358979323846 */
+
+#ifndef REALSTRCMP
+#define Strcmp(s1, s2) ( \
+    *(s1) == *(s2) ? ( \
+        (*s1) ? strcmp ((s1) + 1, (s2) + 1) : 0 \
+    ) : (*(s1) < *(s2) ? -1 : 1) \
+)
+#else
+#define Strcmp(s1, s2) strcmp ((s1), (s2))
+#endif
+
+    extern int warnflag;
+    extern char *leftypath, *leftyoptions, *shellpath;
+    extern jmp_buf exitljbuf;
+    extern int idlerunmode;
+    extern fd_set inputfds;
+
+    int init(char *);
+    void term(void);
+    char *buildpath(char *, int);
+    char *buildcommand(char *, char *, int, int, char *);
+    void warning(char *, int, char *, char *, ...);
+    void panic(char *, int, char *, char *, ...);
+    void panic2(char *, int, char *, char *, ...);
+#endif                         /* _COMMON_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/aix_mods/exec.c b/cmd/lefty/aix_mods/exec.c
new file mode 100644 (file)
index 0000000..eb776d2
--- /dev/null
@@ -0,0 +1,1004 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "mem.h"
+#include "code.h"
+#include "tbl.h"
+#include "str.h"
+#include "exec.h"
+#include "internal.h"
+
+static lvar_t *lvarp;
+static int lvarn, llvari, flvari;
+#define LVARINCR 1000
+#define LVARSIZE sizeof (lvar_t)
+
+Tobj root, null;
+Tobj rtno;
+int Erun;
+int Eerrlevel, Estackdepth, Eshowbody, Eshowcalls, Eoktorun;
+
+#define PUSHJMP(op, np, b) op = (volatile jmp_buf *) np, np = (jmp_buf *) &b
+#define POPJMP(op, np) np = (jmp_buf *) op
+
+/* longjmps for normal program execution */
+typedef enum {
+    PLJ_BREAK, PLJ_CONTINUE, PLJ_RETURN, PLJ_SIZE
+} PLJtype_t;
+static jmp_buf *pljbufp1, *pljbufp2;
+static PLJtype_t pljtype;
+
+/* longjmp for error handling */
+static jmp_buf *eljbufp;
+
+/* error levels and types */
+typedef enum {
+    ERR0, ERR1, ERR2, ERR3, ERR4, ERR5
+} errlevel_t;
+typedef enum {
+    ERRNOLHS, ERRNORHS, ERRNOSUCHFUNC, ERRBADARG, ERRARGMIS, ERRNOTATABLE,
+    ERRIFUNCERR, ERRRECRUN, ERRTABLECHANGED
+} errnum_t;
+static char *errnam[] = {
+    "no variable",
+    "no value",
+    "no such function",
+    "bad argument",
+    "argument number mismatch",
+    "not a table",
+    "internal function call error",
+    "recursive run attempt",
+    "table changed during a forin loop",
+};
+
+static int errdo;
+
+/* stack information */
+typedef struct sinfo_t {
+    Tobj co, fco;
+    int ci, fci;
+    int flvari, llvari;
+} sinfo_t;
+#define SINFOSIZE sizeof (sinfo_t)
+#define SINFOINCR 100
+static sinfo_t *sinfop;
+static int sinfoi, sinfon;
+
+typedef enum {
+    TNK_LI, TNK_O, TNK_S
+} tnktype_t;
+typedef struct tnk_t {
+    tnktype_t type;
+    union {
+       int li;
+       struct {
+           Tobj to, ko;
+       } tnko;
+       struct {
+           Ctype_t kt;
+           Tobj to, co;
+           int vi;
+       } tnks;
+    } u;
+} tnk_t;
+
+typedef struct num_tt {
+    Ctype_t type;
+    union {
+       long i;
+       double d;
+       Tobj no;
+    } u;
+} num_tt;
+
+static long rootm;
+static int running;
+
+static Tobj eeval(Tobj, int);
+static Tobj efcall(Tobj, int);
+static void ewhilest(Tobj, int);
+static void eforst(Tobj, int);
+static void eforinst(Tobj, int);
+
+static Tobj getval(Tobj, int);
+static int getvar(Tobj, int, tnk_t *);
+static void setvar(tnk_t, Tobj);
+static int boolop(Tobj);
+static int orderop(Tobj, Ctype_t, Tobj);
+static Tobj arithop(num_tt *, Ctype_t, num_tt *);
+static void err(int, int, Tobj, int);
+static void printbody(char *, int);
+
+void Einit(void)
+{
+    root = Ttable(100);
+    rootm = Mpushmark(root);
+    Tinss(root, "null", (null = Ttable(2)));
+    rtno = NULL;
+    pljbufp1 = pljbufp2 = NULL, pljtype = 0;
+    eljbufp = NULL;
+    lvarp = Marrayalloc((long) LVARINCR * LVARSIZE);
+    lvarn = LVARINCR;
+    llvari = 0;
+    flvari = 0;
+    sinfop = Marrayalloc((long) SINFOINCR * SINFOSIZE);
+    sinfon = SINFOINCR;
+    sinfoi = 0;
+    Erun = FALSE;
+    running = 0;
+    Eoktorun = FALSE;
+}
+
+void Eterm(void)
+{
+    Marrayfree(sinfop), sinfop = NULL, sinfon = 0, sinfoi = 0;
+    Marrayfree(lvarp), lvarp = NULL, lvarn = 0, llvari = 0, flvari = 0;
+    rtno = NULL;
+    null = NULL;
+    Mpopmark(rootm);
+}
+
+Tobj Eunit(Tobj co)
+{
+    volatile jmp_buf *oeljbufp;
+    volatile int ownsinfoi;
+    volatile long m;
+    volatile Tobj lrtno;
+
+    jmp_buf eljbuf;
+
+#if 0
+    if (running && !Eoktorun) {
+       err(ERRRECRUN, ERR2, NULL, 0);
+       return NULL;
+    }
+#endif
+    Eoktorun = FALSE;
+
+    if (!co)
+       return NULL;
+
+    if (Tgettype(co) != T_CODE)
+       panic(POS, "Eunit", "argument type is not T_CODE");
+
+    m = Mpushmark(co);
+    PUSHJMP(oeljbufp, eljbufp, eljbuf);
+    ownsinfoi = sinfoi++;
+    if (sinfoi == sinfon) {
+       sinfop =
+           Marraygrow(sinfop, (long) (sinfon + SINFOINCR) * SINFOSIZE);
+       sinfon += SINFOINCR;
+    }
+    sinfop[ownsinfoi].co = co;
+    sinfop[ownsinfoi].ci = TCgetfp(co, 0);
+    sinfop[ownsinfoi].fco = NULL;
+    sinfop[ownsinfoi].flvari = flvari;
+    sinfop[ownsinfoi].llvari = llvari;
+    running++;
+    if (setjmp(*eljbufp))
+       lrtno = NULL;
+    else
+       lrtno = eeval(co, TCgetfp(co, 0));
+    running--;
+    rtno = NULL;
+    flvari = sinfop[ownsinfoi].flvari;
+    llvari = sinfop[ownsinfoi].llvari;
+    sinfoi = ownsinfoi;
+    POPJMP(oeljbufp, eljbufp);
+    Mpopmark(m);
+    Erun = TRUE;
+    return lrtno;
+}
+
+/* shortcut: this function executes a piece of code that corresponds to
+   <internal func name> = function () internal "<internal func name>";
+*/
+Tobj Efunction(Tobj co, char *ifnam)
+{
+    Tobj v1o;
+    int fi;
+
+    fi = TCgetnext(co, TCgetfp(co, TCgetfp(co, 0)));
+    v1o = Tcode(TCgetaddr(co, fi), fi,
+               (int) TCgetinteger(co, TCgetfp(co, fi)));
+    Tinss(root, ifnam, v1o);
+    return v1o;
+}
+
+static Tobj eeval(Tobj co, int ci)
+{
+    Tobj v1o, v2o, v3o;
+    Ttype_t ttype;
+    Ctype_t ctype;
+    tnk_t tnk;
+    num_tt lnum, rnum;
+    long m1, m2;
+    int i1, i2, res;
+
+  tailrec:
+    errdo = TRUE;
+    v1o = NULL;
+    ctype = TCgettype(co, ci);
+    switch (ctype) {
+    case C_ASSIGN:
+       i1 = TCgetfp(co, ci);
+       if ((v1o = eeval(co, TCgetnext(co, i1))) == NULL) {
+           err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+           return NULL;
+       }
+       m1 = Mpushmark(v1o);
+       res = getvar(co, i1, &tnk);
+       Mpopmark(m1);
+       if (res == -1) {
+           err(ERRNOLHS, ERR3, co, i1);
+           return NULL;
+       }
+       setvar(tnk, v1o);
+       return v1o;
+    case C_OR:
+    case C_AND:
+    case C_NOT:
+       i1 = TCgetfp(co, ci);
+       if ((v1o = eeval(co, i1)) == NULL)
+           err(ERRNORHS, ERR4, co, i1);
+       switch (ctype) {
+       case C_OR:
+           if (boolop(v1o) == TRUE)
+               return Ttrue;
+           if ((v1o = eeval(co, TCgetnext(co, i1))) == NULL)
+               err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+           return (boolop(v1o) == TRUE) ? Ttrue : Tfalse;
+       case C_AND:
+           if (boolop(v1o) == FALSE)
+               return Tfalse;
+           if ((v1o = eeval(co, TCgetnext(co, i1))) == NULL)
+               err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+           return (boolop(v1o) == FALSE) ? Tfalse : Ttrue;
+       case C_NOT:
+           return (boolop(v1o) == TRUE) ? Tfalse : Ttrue;
+       }
+       /* NOT REACHED */
+       return Tfalse;
+    case C_EQ:
+    case C_NE:
+    case C_LT:
+    case C_LE:
+    case C_GT:
+    case C_GE:
+       i1 = TCgetfp(co, ci);
+       if ((v1o = eeval(co, i1)) == NULL)
+           err(ERRNORHS, ERR4, co, i1);
+       else
+           m1 = Mpushmark(v1o);
+       if ((v2o = eeval(co, TCgetnext(co, i1))) == NULL)
+           err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+       if (v1o)
+           Mpopmark(m1);
+       return (orderop(v1o, ctype, v2o) == TRUE) ? Ttrue : Tfalse;
+    case C_PLUS:
+    case C_MINUS:
+    case C_MUL:
+    case C_DIV:
+    case C_MOD:
+    case C_UMINUS:
+       i1 = TCgetfp(co, ci);
+       if ((lnum.type = TCgettype(co, i1)) == C_INTEGER)
+           lnum.u.i = TCgetinteger(co, i1);
+       else if (lnum.type == C_REAL)
+           lnum.u.d = TCgetreal(co, i1);
+       else if ((lnum.u.no = eeval(co, i1)) == NULL) {
+           err(ERRNORHS, ERR4, co, i1);
+           return NULL;
+       }
+       if (ctype == C_UMINUS) {
+           if (!(v1o = arithop(&lnum, ctype, NULL)))
+               err(ERRNORHS, ERR4, co, ci);
+           return v1o;
+       }
+       if (lnum.type != C_INTEGER && lnum.type != C_REAL)
+           m1 = Mpushmark(lnum.u.no);
+       i1 = TCgetnext(co, i1);
+       if ((rnum.type = TCgettype(co, i1)) == C_INTEGER)
+           rnum.u.i = TCgetinteger(co, i1);
+       else if (rnum.type == C_REAL)
+           rnum.u.d = TCgetreal(co, i1);
+       else if ((rnum.u.no = eeval(co, i1)) == NULL)
+           err(ERRNORHS, ERR4, co, i1);
+       if (lnum.type != C_INTEGER && lnum.type != C_REAL)
+           Mpopmark(m1);
+       if (!(v1o = arithop(&lnum, ctype, &rnum)))
+           err(ERRNORHS, ERR4, co, ci);
+       return v1o;
+    case C_PEXPR:
+       ci = TCgetfp(co, ci);
+       goto tailrec;
+    case C_FCALL:
+       return efcall(co, ci);
+    case C_INTEGER:
+       return Tinteger(TCgetinteger(co, ci));
+    case C_REAL:
+       return Treal(TCgetreal(co, ci));
+    case C_STRING:
+       return Tstring(TCgetstring(co, ci));
+    case C_GVAR:
+    case C_LVAR:
+    case C_PVAR:
+       return getval(co, ci);
+    case C_FUNCTION:
+       return Tcode(TCgetaddr(co, ci), ci,
+                    (int) TCgetinteger(co, TCgetfp(co, ci)));
+    case C_TCONS:
+       v1o = Ttable(0);
+       m1 = Mpushmark(v1o);
+       for (i1 = TCgetfp(co, ci); i1 != C_NULL;
+            i1 = TCgetnext(co, TCgetnext(co, i1))) {
+           if (!(v3o = eeval(co, TCgetnext(co, i1)))) {
+               err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+               continue;
+           }
+           m2 = Mpushmark(v3o);
+           if (!(v2o = eeval(co, i1))) {
+               err(ERRNOLHS, ERR3, co, i1);
+               Mpopmark(m2);
+               continue;
+           }
+           ttype = Tgettype(v2o);
+           if (ttype == T_INTEGER || ttype == T_REAL || ttype == T_STRING)
+               Tinso(v1o, v2o, v3o);
+           else
+               err(ERRNOLHS, ERR1, co, i1);
+       }
+       Mpopmark(m1);
+       return v1o;
+    case C_STMT:
+       for (i1 = TCgetfp(co, ci); i1 != C_NULL;)
+           if ((i2 = TCgetnext(co, i1)) != C_NULL) {
+               eeval(co, i1);
+               i1 = i2;
+           } else {
+               ci = i1;
+               goto tailrec;
+           }
+       /* NOT REACHED */
+       break;
+    case C_IF:
+       i1 = TCgetfp(co, ci);
+       if (!(v1o = eeval(co, i1)))
+           err(ERRNORHS, ERR5, co, i1);
+       if (boolop(v1o) == TRUE) {
+           ci = TCgetnext(co, i1);
+           goto tailrec;
+       } else if ((ci = TCgetnext(co, TCgetnext(co, i1))) != C_NULL)
+           goto tailrec;
+       break;
+    case C_WHILE:
+       ewhilest(co, ci);
+       break;
+    case C_FOR:
+       eforst(co, ci);
+       break;
+    case C_FORIN:
+       eforinst(co, ci);
+       break;
+    case C_BREAK:
+       pljtype = PLJ_BREAK;
+       longjmp(*pljbufp1, 1);
+       /* NOT REACHED */
+       break;
+    case C_CONTINUE:
+       pljtype = PLJ_CONTINUE;
+       longjmp(*pljbufp1, 1);
+       /* NOT REACHED */
+       break;
+    case C_RETURN:
+       if ((i1 = TCgetfp(co, ci)) != C_NULL)
+           rtno = eeval(co, i1);
+       pljtype = PLJ_RETURN;
+       longjmp(*pljbufp2, 1);
+       /* NOT REACHED */
+       break;
+    default:
+       panic(POS, "eeval", "unknown program token type %d", ctype);
+    }
+    return v1o;
+}
+
+static Tobj efcall(Tobj co, int ci)
+{
+    volatile jmp_buf *opljbufp1, *opljbufp2;
+    volatile long m;
+    volatile int bi, ownsinfoi, li, ln;
+
+    jmp_buf pljbuf;
+    Tobj fdo, vo, lrtno;
+    int i, fci, ai, di, di1, fid;
+
+    ownsinfoi = sinfoi++;
+    if (sinfoi == sinfon) {
+       sinfop =
+           Marraygrow(sinfop, (long) (sinfon + SINFOINCR) * SINFOSIZE);
+       sinfon += SINFOINCR;
+    }
+    sinfop[ownsinfoi].co = co;
+    sinfop[ownsinfoi].ci = ci;
+    sinfop[ownsinfoi].fco = NULL;
+    sinfop[ownsinfoi].flvari = flvari;
+    sinfop[ownsinfoi].llvari = llvari;
+    fci = TCgetfp(co, ci);
+    if (!(fdo = getval(co, fci)) || Tgettype(fdo) != T_CODE) {
+       err(ERRNOSUCHFUNC, ERR2, co, fci);
+       sinfoi = ownsinfoi;
+       return NULL;
+    }
+
+    m = Mpushmark((Tobj) fdo);
+    ai = TCgetfp(co, TCgetnext(co, fci));
+    ln = (int) TCgetinteger(fdo, (li = TCgetnext(fdo, TCgetfp(fdo, 0))));
+    di = TCgetnext(fdo, li);
+    bi = TCgetnext(fdo, di);
+    if (bi != C_NULL && TCgettype(fdo, bi) == C_INTERNAL) {
+       for (i = 0; ai != C_NULL; ai = TCgetnext(co, ai), i++) {
+           if (!(vo = eeval(co, ai))) {
+               err(ERRBADARG, ERR2, co, ai);
+               Mpopmark(m);
+               llvari = sinfop[ownsinfoi].llvari;
+               sinfoi = ownsinfoi;
+               return NULL;
+           }
+           if (llvari + 1 > lvarn) {
+               lvarp = Marraygrow(lvarp, (long) (llvari + 1) * LVARSIZE);
+               lvarn = llvari + 1;
+           }
+           lvarp[llvari].m = Mpushmark((lvarp[llvari].o = vo));
+           llvari++;
+       }
+       fid = (int) TCgetinteger(fdo, TCgetfp(fdo, bi));
+       if (Ifuncs[fid].min > i || Ifuncs[fid].max < i) {
+           err(ERRARGMIS, ERR2, co, ci);
+           Mpopmark(m);
+           llvari = sinfop[ownsinfoi].llvari;
+           sinfoi = ownsinfoi;
+           return NULL;
+       }
+       flvari = sinfop[ownsinfoi].llvari;
+       sinfop[ownsinfoi].fco = fdo;
+       sinfop[ownsinfoi].fci = bi;
+       if (fid < 0 || fid >= Ifuncn)
+           panic(POS, "efcall", "no such internal function: %d", fid);
+       rtno = Ttrue;
+       if ((*Ifuncs[fid].func) (i, &lvarp[flvari]) == L_FAILURE) {
+           rtno = NULL;
+           err(ERRIFUNCERR, ERR2, co, ci);
+       }
+    } else {
+       if (llvari + ln > lvarn) {
+           lvarp = Marraygrow(lvarp, (long) (llvari + ln) * LVARSIZE);
+           lvarn = llvari + ln;
+       }
+       di1 = TCgetfp(fdo, di);
+       for (i = 0; i < ln && di1 != C_NULL && ai != C_NULL;
+            i++, ai = TCgetnext(co, ai)) {
+           if (!(vo = eeval(co, ai))) {
+               err(ERRBADARG, ERR2, co, ai);
+               Mpopmark(m);
+               llvari = sinfop[ownsinfoi].llvari;
+               sinfoi = ownsinfoi;
+               return NULL;
+           }
+           lvarp[llvari].m = Mpushmark((lvarp[llvari].o = vo));
+           llvari++;
+           di1 = TCgetnext(fdo, di1);
+       }
+       if (di1 != C_NULL || ai != C_NULL) {
+           err(ERRARGMIS, ERR2, co, ci);
+           Mpopmark(m);
+           llvari = sinfop[ownsinfoi].llvari;
+           sinfoi = ownsinfoi;
+           return NULL;
+       }
+       for (; i < ln; i++, llvari++)
+           lvarp[llvari].m = Mpushmark((lvarp[llvari].o = NULL));
+       flvari = sinfop[ownsinfoi].llvari;
+       PUSHJMP(opljbufp2, pljbufp2, pljbuf);
+       opljbufp1 = (volatile jmp_buf *) pljbufp1;
+       if (setjmp(*pljbufp2)) {
+           ;
+       } else {
+           sinfop[ownsinfoi].fco = fdo;
+           for (; bi != C_NULL; bi = TCgetnext(fdo, bi)) {
+               sinfop[ownsinfoi].fci = bi;
+               if (TCgettype(fdo, bi) != C_DECL)
+                   eeval((Tobj) fdo, bi);
+           }
+       }
+       POPJMP(opljbufp2, pljbufp2);
+       pljbufp1 = (jmp_buf *) opljbufp1;
+    }
+    flvari = sinfop[ownsinfoi].flvari;
+    llvari = sinfop[ownsinfoi].llvari;
+    sinfoi = ownsinfoi;
+    Mpopmark(m);
+    lrtno = rtno, rtno = NULL;
+    errdo = TRUE;
+    return lrtno;
+}
+
+static void ewhilest(Tobj co, int ci)
+{
+    volatile jmp_buf *opljbufp;
+    volatile jmp_buf pljbuf;
+    volatile Tobj c1o;
+    volatile int ei, si;
+
+    Tobj v1o;
+
+    c1o = (volatile Tobj) co;  /* protect argument from longjmp */
+    ei = TCgetfp(c1o, ci);
+    si = TCgetnext(c1o, ei);
+    PUSHJMP(opljbufp, pljbufp1, pljbuf);
+    for (;;) {
+       if (!(v1o = eeval((Tobj) c1o, ei)))
+           err(ERRNORHS, ERR5, c1o, ei);
+       if (boolop(v1o) == FALSE)
+           break;
+       if (setjmp(*pljbufp1)) {
+           if (pljtype == PLJ_CONTINUE)
+               continue;
+           else if (pljtype == PLJ_BREAK)
+               break;
+       }
+       eeval((Tobj) c1o, si);
+    }
+    POPJMP(opljbufp, pljbufp1);
+}
+
+static void eforst(Tobj co, int ci)
+{
+    volatile jmp_buf *opljbufp;
+    volatile jmp_buf pljbuf;
+    volatile Tobj c1o;
+    volatile int ei1, ei2, ei3, si, eisnop1, eisnop2, eisnop3;
+
+    Tobj v1o;
+
+    c1o = (volatile Tobj) co;  /* protect argument from longjmp */
+    ei1 = TCgetfp(c1o, ci);
+    ei2 = TCgetnext(c1o, ei1);
+    ei3 = TCgetnext(c1o, ei2);
+    si = TCgetnext(c1o, ei3);
+    eisnop1 = (TCgettype(c1o, ei1) == C_NOP);
+    eisnop2 = (TCgettype(c1o, ei2) == C_NOP);
+    eisnop3 = (TCgettype(c1o, ei3) == C_NOP);
+    PUSHJMP(opljbufp, pljbufp1, pljbuf);
+    if (!eisnop1)
+       eeval((Tobj) c1o, ei1);
+    for (;;) {
+       if (!eisnop2) {
+           if (!(v1o = eeval((Tobj) c1o, ei2)))
+               err(ERRNORHS, ERR5, c1o, ei2);
+           if (boolop(v1o) == FALSE)
+               break;
+       }
+       if (setjmp(*pljbufp1) != 0) {
+           if (pljtype == PLJ_CONTINUE);
+           else if (pljtype == PLJ_BREAK)
+               break;
+       } else {
+           eeval((Tobj) c1o, si);
+       }
+       if (!eisnop3)
+           eeval((Tobj) c1o, ei3);
+    }
+    POPJMP(opljbufp, pljbufp1);
+}
+
+static void eforinst(Tobj co, int ci)
+{
+    volatile jmp_buf *opljbufp;
+    volatile jmp_buf pljbuf;
+    volatile Tobj tblo, c1o;
+    volatile Tkvindex_t tkvi;
+    volatile tnk_t tnk;
+    volatile long km, t;
+    volatile int ei1, ei2, si;
+
+    c1o = (volatile Tobj) co;  /* protect argument from longjmp */
+    ei1 = TCgetfp(c1o, ci);
+    ei2 = TCgetnext(c1o, ei1);
+    si = TCgetnext(c1o, ei2);
+    if (getvar((Tobj) c1o, ei1, (tnk_t *) & tnk) == -1) {
+       err(ERRNOLHS, ERR3, c1o, ei1);
+       return;
+    }
+    if (tnk.type == TNK_O)
+       km = Mpushmark(tnk.u.tnko.ko);
+    if (!(tblo = (volatile Tobj) eeval((Tobj) c1o, ei2))) {
+       if (tnk.type == TNK_O)
+           Mpopmark(km);
+       err(ERRNORHS, ERR4, c1o, ei2);
+       return;
+    }
+    if (Tgettype(tblo) != T_TABLE) {
+       err(ERRNOTATABLE, ERR1, c1o, ei2);
+       return;
+    }
+    PUSHJMP(opljbufp, pljbufp1, pljbuf);
+    t = Tgettime(tblo);
+    for (Tgetfirst((Tobj) tblo, (Tkvindex_t *) & tkvi); tkvi.kvp;
+        Tgetnext((Tkvindex_t *) & tkvi)) {
+       setvar(tnk, tkvi.kvp->ko);
+       if (setjmp(*pljbufp1) != 0) {
+           if (pljtype == PLJ_CONTINUE)
+               continue;
+           else if (pljtype == PLJ_BREAK)
+               break;
+       }
+       eeval((Tobj) c1o, si);
+       if (t != Tgettime(tblo)) {
+           err(ERRTABLECHANGED, ERR1, c1o, ei2);
+           break;
+       }
+    }
+    POPJMP(opljbufp, pljbufp1);
+    if (tnk.type == TNK_O)
+       Mpopmark(km);
+}
+
+static Tobj getval(Tobj co, int ci)
+{
+    Tobj cvo, cko, cto;
+    Ctype_t ct, vt;
+    int vi, ni, nn;
+
+    if ((ct = TCgettype(co, ci)) == C_LVAR) {
+       nn = (int) TCgetinteger(co, (ni = TCgetnext(co, TCgetfp(co, ci))));
+       cto = cvo = lvarp[flvari + nn].o;
+       if (!cto)
+           return NULL;
+       vi = TCgetnext(co, ni);
+    } else if (ct == C_GVAR) {
+       cto = root;
+       vi = TCgetfp(co, ci);
+    } else if (ct == C_PVAR)
+       return TCgetobject(co, ci);
+    else
+       return NULL;
+
+    while (vi != C_NULL) {
+       if (Tgettype(cto) != T_TABLE)
+           return NULL;
+       if ((vt = TCgettype(co, vi)) == C_STRING) {
+           if (!(cvo = Tfinds(cto, TCgetstring(co, vi))))
+               return NULL;
+       } else if (vt == C_INTEGER) {
+           if (!(cvo = Tfindi(cto, TCgetinteger(co, vi))))
+               return NULL;
+       } else if (vt == C_REAL) {
+           if (!(cvo = Tfindr(cto, TCgetreal(co, vi))))
+               return NULL;
+       } else {
+           if (!(cko = eeval(co, vi)) || !(cvo = Tfindo(cto, cko)))
+               return NULL;
+       }
+       cto = cvo;
+       vi = TCgetnext(co, vi);
+    }
+    return cvo;
+}
+
+static int getvar(Tobj co, int ci, tnk_t * tnkp)
+{
+    Tobj cvo, cko, cto;
+    Ctype_t ct, vt;
+    long m;
+    int vi, ovi, nn, ni;
+
+    if ((ct = TCgettype(co, ci)) == C_LVAR) {
+       nn = (int) TCgetinteger(co, (ni = TCgetnext(co, TCgetfp(co, ci))));
+       cvo = cto = lvarp[flvari + nn].o;
+       vi = TCgetnext(co, ni);
+       if (vi != C_NULL && (!cvo || Tgettype(cvo) != T_TABLE))
+           Mresetmark(lvarp[flvari + nn].m,
+                      (lvarp[flvari + nn].o = cvo = cto = Ttable(0)));
+    } else if (ct == C_GVAR) { /* else it's a global variable */
+       cvo = root;
+       vi = TCgetfp(co, ci);
+    } else {
+       return -1;
+    }
+
+    ovi = -1;
+    while (vi != C_NULL) {
+       cto = cvo;
+       if ((vt = TCgettype(co, vi)) == C_STRING) {
+           cvo = Tfinds(cto, TCgetstring(co, vi));
+       } else if (vt == C_INTEGER) {
+           cvo = Tfindi(cto, TCgetinteger(co, vi));
+       } else if (vt == C_REAL) {
+           cvo = Tfindr(cto, TCgetreal(co, vi));
+       } else {
+           if (!(cko = eeval(co, vi)) || !(T_ISSTRING(cko) ||
+                                           T_ISNUMBER(cko)))
+               return -1;
+           cvo = Tfindo(cto, cko);
+       }
+       ovi = vi, vi = TCgetnext(co, vi);
+       if (vi != C_NULL && (!cvo || Tgettype(cvo) != T_TABLE)) {
+           if (vt == C_STRING)
+               Tinss(cto, TCgetstring(co, ovi), (cvo = Ttable(0)));
+           else if (vt == C_INTEGER)
+               Tinsi(cto, TCgetinteger(co, ovi), (cvo = Ttable(0)));
+           else if (vt == C_REAL)
+               Tinsr(cto, TCgetreal(co, ovi), (cvo = Ttable(0)));
+           else
+               m = Mpushmark(cko), Tinso(cto, cko, (cvo = Ttable(0))),
+                   Mpopmark(m);
+       }
+    }
+    if (ct == C_LVAR && ovi == -1) {
+       tnkp->type = TNK_LI;
+       tnkp->u.li = nn;
+    } else {
+       switch (vt) {
+       case C_STRING:
+       case C_INTEGER:
+       case C_REAL:
+           tnkp->type = TNK_S;
+           tnkp->u.tnks.kt = vt;
+           tnkp->u.tnks.to = cto;
+           tnkp->u.tnks.co = co;
+           tnkp->u.tnks.vi = ovi;
+           break;
+       default:
+           tnkp->type = TNK_O;
+           tnkp->u.tnko.to = cto;
+           tnkp->u.tnko.ko = cko;
+           break;
+       }
+    }
+    return 0;
+}
+
+static void setvar(tnk_t tnk, Tobj vo)
+{
+    switch (tnk.type) {
+    case TNK_LI:
+       Mresetmark(lvarp[flvari + tnk.u.li].m,
+                  (lvarp[flvari + tnk.u.li].o = vo));
+       break;
+    case TNK_O:
+       Tinso(tnk.u.tnko.to, tnk.u.tnko.ko, vo);
+       break;
+    default:
+       switch (tnk.u.tnks.kt) {
+       case C_STRING:
+           Tinss(tnk.u.tnks.to,
+                 TCgetstring(tnk.u.tnks.co, tnk.u.tnks.vi), vo);
+           break;
+       case C_INTEGER:
+           Tinsi(tnk.u.tnks.to,
+                 TCgetinteger(tnk.u.tnks.co, tnk.u.tnks.vi), vo);
+           break;
+       case C_REAL:
+           Tinsr(tnk.u.tnks.to, TCgetreal(tnk.u.tnks.co, tnk.u.tnks.vi),
+                 vo);
+           break;
+       }
+       break;
+    }
+}
+
+static int boolop(Tobj vo)
+{
+    long i;
+    double d;
+
+    if (!vo)
+       return FALSE;
+
+    switch (Tgettype(vo)) {
+    case T_INTEGER:
+       i = Tgetinteger(vo);
+       return (i == 0) ? FALSE : TRUE;
+    case T_REAL:
+       d = Tgetreal(vo);
+       return (d == 0.0) ? FALSE : TRUE;
+    case T_TABLE:
+       if (vo == null)
+           return FALSE;
+       return TRUE;
+    default:
+       return TRUE;
+    }
+}
+
+static int orderop(Tobj v1o, Ctype_t op, Tobj v2o)
+{
+    Ctype_t t1, t2;
+    long i1, i2;
+    int r;
+    double d1, d2;
+
+    if (!v1o || !v2o) {
+       if ((v1o || v2o) && op == C_NE)
+           return TRUE;
+       return FALSE;
+    }
+    t1 = Tgettype(v1o), t2 = Tgettype(v2o);
+    if (t1 == T_STRING && t2 == T_STRING) {
+       r = Strcmp(Tgetstring(v1o), Tgetstring(v2o));
+    } else if (t1 == T_INTEGER && t2 == T_INTEGER) {
+       i1 = Tgetinteger(v1o), i2 = Tgetinteger(v2o);
+       r = (i1 == i2) ? 0 : ((i1 < i2) ? -1 : 1);
+    } else if (t1 == T_INTEGER && t2 == T_REAL) {
+       i1 = Tgetinteger(v1o), d2 = Tgetreal(v2o);
+       r = (i1 == d2) ? 0 : ((i1 < d2) ? -1 : 1);
+    } else if (t1 == T_REAL && t2 == T_INTEGER) {
+       d1 = Tgetreal(v1o), i2 = Tgetinteger(v2o);
+       r = (d1 == i2) ? 0 : ((d1 < i2) ? -1 : 1);
+    } else if (t1 == T_REAL && t2 == T_REAL) {
+       d1 = Tgetreal(v1o), d2 = Tgetreal(v2o);
+       r = (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1);
+    } else if (t1 == t2) {
+       if (op != C_EQ && op != C_NE)
+           return FALSE;
+       r = (v1o == v2o) ? 0 : 1;
+    } else {
+       return FALSE;
+    }
+    switch (op) {
+    case C_EQ:
+       return (r == 0) ? TRUE : FALSE;
+    case C_NE:
+       return (r != 0) ? TRUE : FALSE;
+    case C_LT:
+       return (r < 0) ? TRUE : FALSE;
+    case C_LE:
+       return (r <= 0) ? TRUE : FALSE;
+    case C_GT:
+       return (r > 0) ? TRUE : FALSE;
+    case C_GE:
+       return (r >= 0) ? TRUE : FALSE;
+    }
+    panic(POS, "orderop", "bad op code");
+    return FALSE;              /* NOT REACHED */
+}
+
+static Tobj arithop(num_tt * lnum, Ctype_t op, num_tt * rnum)
+{
+    double d1, d2, d3;
+
+    if (!rnum && op != C_UMINUS)
+       return NULL;
+    if (lnum->type == C_INTEGER)
+       d1 = lnum->u.i;
+    else if (lnum->type == C_REAL)
+       d1 = lnum->u.d;
+    else if (!lnum->u.no)
+       return NULL;
+    else if (Tgettype(lnum->u.no) == T_INTEGER)
+       d1 = Tgetinteger(lnum->u.no);
+    else if (Tgettype(lnum->u.no) == T_REAL)
+       d1 = Tgetreal(lnum->u.no);
+    else
+       return NULL;
+    if (op == C_UMINUS) {
+       d3 = -d1;
+       goto result;
+    }
+    if (rnum->type == C_INTEGER)
+       d2 = rnum->u.i;
+    else if (rnum->type == C_REAL)
+       d2 = rnum->u.d;
+    else if (!rnum->u.no)
+       return NULL;
+    else if (Tgettype(rnum->u.no) == T_INTEGER)
+       d2 = Tgetinteger(rnum->u.no);
+    else if (Tgettype(rnum->u.no) == T_REAL)
+       d2 = Tgetreal(rnum->u.no);
+    else
+       return NULL;
+    switch (op) {
+    case C_PLUS:
+       d3 = d1 + d2;
+       break;
+    case C_MINUS:
+       d3 = d1 - d2;
+       break;
+    case C_MUL:
+       d3 = d1 * d2;
+       break;
+    case C_DIV:
+       d3 = d1 / d2;
+       break;
+    case C_MOD:
+       d3 = (long) d1 % (long) d2;
+       break;
+    }
+  result:
+    if (d3 == (double) (long) d3)
+       return Tinteger((long) d3);
+    return Treal(d3);
+}
+
+static void err(int errnum, int level, Tobj co, int ci)
+{
+    char *s;
+    int si, i;
+
+    if (level > Eerrlevel || !errdo)
+       return;
+    s = "";
+    fprintf(stderr, "runtime error: %s\n", errnam[errnum]);
+    if (!co)
+       return;
+    if (Estackdepth < 1)
+       return;
+    if (!sinfop[(si = sinfoi - 1)].fco && si > 0)
+       si--;
+    if (Eshowbody > 0) {
+       if (co == sinfop[si].fco)
+           s = Scfull(co, 0, ci);
+       else if (co == sinfop[si].co)
+           s = Scfull(co, TCgetfp(co, 0), ci);
+       printbody(s, Eshowbody), free(s);
+       if (Estackdepth == 1) {
+           fprintf(stderr, "\n");
+           errdo = FALSE;
+       }
+       for (i = si; i >= 0; i--) {
+           if (sinfop[i].fco) {
+               s = Scfull(sinfop[i].fco, 0, sinfop[i].fci);
+               printbody(s, Eshowbody), free(s);
+           }
+       }
+       s = Scfull(sinfop[0].co, TCgetfp(sinfop[0].co, 0), sinfop[0].ci);
+       printbody(s, Eshowbody), free(s);
+    }
+    fprintf(stderr, "\n");
+    errdo = FALSE;
+}
+
+static void printbody(char *s, int mode)
+{
+    char *s1, *s2;
+    char c;
+
+    if (mode == 2) {
+       fprintf(stderr, "%s\n", s);
+       return;
+    }
+    c = '\000';
+    for (s1 = s; *s1; s1++)
+       if (*s1 == '>' && *(s1 + 1) && *(s1 + 1) == '>')
+           break;
+    if (!*s1)
+       return;
+    for (; s1 != s; s1--)
+       if (*(s1 - 1) == '\n')
+           break;
+    for (s2 = s1; *s2; s2++)
+       if (*s2 == '\n')
+           break;
+    if (*s2)
+       c = *s2, *s2 = '\000';
+    fprintf(stderr, "%s\n", s1);
+    if (c)
+       *s2 = c;
+}
diff --git a/cmd/lefty/aix_mods/tbl.c b/cmd/lefty/aix_mods/tbl.c
new file mode 100644 (file)
index 0000000..6d4d5d8
--- /dev/null
@@ -0,0 +1,721 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "mem.h"
+#include "code.h"
+#include "tbl.h"
+
+long Ttime = 0;
+int Tstringoffset, Tcodeoffset, Tkvoffset;
+Tobj Ttrue, Tfalse;
+
+#define ISEQIK(ik, ko) \
+    (T_ISNUMBER (ko) && Tgetnumber (ko) == (ik))
+#define ISEQRK(rk, ko) \
+    (T_ISNUMBER (ko) && Tgetnumber (ko) == (rk))
+#define ISEQSK(sk, ko) \
+    (T_ISSTRING (ko)  && Strcmp (((Tstring_t *) (ko))->s, (sk)) == 0)
+
+#define GETIKINDEX(tp, ik) (unsigned long) ik  % tp->ln
+#define GETRKINDEX(tp, rk) (unsigned long) rk  % tp->ln
+#define GETSKINDEX(tp, sk) (unsigned long) *sk % tp->ln
+
+typedef struct mapentry_t {
+    struct mapentry_t *next;
+    Tobj fmo, too;
+} mapentry_t;
+#define MAPENTRYSIZE sizeof (mapentry_t)
+#define MAPLISTN 100
+typedef struct map_tt {
+    struct mapentry_t *list[MAPLISTN];
+} map_tt;
+static map_tt map;
+static long mapentrybyte2size;
+
+static long truem, falsem;
+
+static Tinteger_t keyi;
+static Treal_t keyr;
+static Tstring_t keys;
+
+static void insert(Ttable_t *, Tobj, char *, Tobj);
+static Tobj find(Ttable_t *, Tobj, char *);
+static void delete(Ttable_t *, Tobj, char *);
+static void copytable(Ttable_t *, long);
+static void reccopytable(Ttable_t *, Ttable_t *);
+static void mapinit(void);
+static void mapterm(void);
+static void mapinsert(Tobj, Tobj);
+static Tobj mapfind(Tobj);
+
+void Tinit(void)
+{
+    Tstring_t s;
+    Tcode_t c;
+    Tkvlist_t kvl;
+
+    Mhaspointers[T_INTEGER] = FALSE;
+    Mhaspointers[T_REAL] = FALSE;
+    Mhaspointers[T_STRING] = FALSE;
+    Mhaspointers[T_CODE] = FALSE;
+    Mhaspointers[T_TABLE] = TRUE;
+    Ttrue = Tinteger(1);
+    truem = Mpushmark(Ttrue);
+    Tfalse = Tinteger(0);
+    falsem = Mpushmark(Tfalse);
+    Tstringoffset = (char *) &s.s[0] - (char *) &s + 1;
+    /* the + 1 above accounts for the null character */
+    Tcodeoffset = (char *) &c.c[0] - (char *) &c;
+    Tkvoffset = (char *) &kvl.kv[0] - (char *) &kvl;
+    keyi.head.type = T_INTEGER;
+    keyr.head.type = T_REAL;
+    keys.head.type = T_STRING;
+    mapentrybyte2size = M_BYTE2SIZE(MAPENTRYSIZE);
+}
+
+void Tterm(void)
+{
+    Mpopmark(falsem);
+    Tfalse = NULL;
+    Mpopmark(truem);
+    Ttrue = NULL;
+    Mdogc(M_GCFULL);
+    Mdogc(M_GCFULL);
+}
+
+void Tgchelper(void *p)
+{
+    Ttable_t *tp;
+    Tkvlist_t *kvlp;
+    long i, j;
+
+    /* must be a table */
+    tp = (Ttable_t *) p;
+    for (i = 0; i < tp->ln; i++)
+       if ((kvlp = tp->lp[i]))
+           for (j = 0; j < kvlp->i; j++)
+               Mmkcurr(kvlp->kv[j].ko), Mmkcurr(kvlp->kv[j].vo);
+}
+
+void Tfreehelper(void *p)
+{
+    Ttable_t *tp;
+    Tkvlist_t *kvlp;
+    long i;
+
+    /* must be a table */
+    tp = (Ttable_t *) p;
+    for (i = 0; i < tp->ln; i++)
+       if ((kvlp = tp->lp[i]))
+           Mfree(kvlp, M_BYTE2SIZE(T_KVLISTSIZE(kvlp->n)));
+    Mfree(tp->lp, M_BYTE2SIZE(tp->ln * T_KVLISTPTRSIZE));
+}
+
+Tobj Tinteger(long i)
+{
+    Tinteger_t *ip;
+
+    ip = Mnew(T_INTEGERSIZE, T_INTEGER);
+    ip->i = i;
+    return ip;
+}
+
+Tobj Treal(double d)
+{
+    Treal_t *rp;
+
+    if (d == (double) (long) d)
+       return Tinteger((long) d);
+    rp = Mnew(T_REALSIZE, T_REAL);
+    rp->d = d;
+    return rp;
+}
+
+Tobj Tstring(char *s)
+{
+    Tstring_t *sp;
+
+    sp = Mnew((long) T_STRINGSIZE(strlen(s)), T_STRING);
+    strcpy(&sp->s[0], s);
+    return sp;
+}
+
+Tobj Tcode(Code_t * cp, int ci, int cl)
+{
+    Tcode_t *codep;
+    Code_t *cp2;
+    char *s;
+    int i, j, cn;
+
+    codep = Mnew((long) T_CODESIZE(cl), T_CODE);
+    cp2 = codep->c;
+    for (i = 0; i < cl; i++) {
+       switch (cp[i].ctype) {
+       case C_INTEGER:
+           cp2[i] = cp[i];
+           if (cp2[i].next != C_NULL)
+               cp2[i].next -= ci;
+           break;
+       case C_REAL:
+           cp2[i] = cp[i];
+           if (cp2[i].next != C_NULL)
+               cp2[i].next -= ci;
+           break;
+       case C_STRING:
+           cp2[i] = cp[i];
+           if (cp2[i].next != C_NULL)
+               cp2[i].next -= ci;
+           s = (char *) &cp[i].u.s;
+           while (*s)
+               s++;
+           cn = (long) (s - (char *) &cp[i]) / sizeof(Code_t);
+           for (j = 0; j < cn; j++)
+               i++, cp2[i] = cp[i];
+           break;
+       default:
+           cp2[i] = cp[i];
+           if (cp2[i].next != C_NULL)
+               cp2[i].next -= ci;
+           if (cp2[i].u.fp != C_NULL)
+               cp2[i].u.fp -= ci;
+           break;
+       }
+    }
+    return codep;
+}
+
+Tobj Ttable(long sizehint)
+{
+    Ttable_t *tp;
+    Tkvlist_t **lp;
+    long i;
+
+    sizehint = (sizehint < 2) ? 2 : sizehint;
+    tp = Mnew(T_TABLESIZE, T_TABLE);
+    lp = Mallocate((long) (sizehint * T_KVLISTPTRSIZE));
+    tp->lp = lp;
+    tp->ln = sizehint;
+    tp->n = 0;
+    tp->time = Ttime;
+    for (i = 0; i < sizehint; i++)
+       lp[i] = NULL;
+    return tp;
+}
+
+
+void Tinsi(Tobj to, long ik, Tobj vo)
+{
+    long tm;
+
+    if (!to || !T_ISTABLE(to))
+       panic(POS, "Tinsi", "insert attempted on non-table");
+    tm = Mpushmark(to);
+    if (vo)
+       Mpushmark(vo);
+    keyi.i = ik;
+    insert(to, &keyi, NULL, vo);
+    Mpopmark(tm);
+}
+
+void Tinsr(Tobj to, double rk, Tobj vo)
+{
+    long tm;
+
+    if (!to || !T_ISTABLE(to))
+       panic(POS, "Tinsr", "insert attempted on non-table");
+    tm = Mpushmark(to);
+    if (vo)
+       Mpushmark(vo);
+    keyr.d = rk;
+    insert(to, &keyr, NULL, vo);
+    Mpopmark(tm);
+}
+
+void Tinss(Tobj to, char *sk, Tobj vo)
+{
+    long tm;
+
+    if (!to || !T_ISTABLE(to))
+       panic(POS, "Tinss", "insert attempted on non-table");
+    tm = Mpushmark(to);
+    if (vo)
+       Mpushmark(vo);
+    insert(to, &keys, sk, vo);
+    Mpopmark(tm);
+}
+
+void Tinso(Tobj to, Tobj ko, Tobj vo)
+{
+    long tm;
+
+    if (!to || !T_ISTABLE(to))
+       panic(POS, "Tinso", "insert attempted on non-table");
+    if (!ko || !(T_ISINTEGER(ko) || T_ISREAL(ko) || T_ISSTRING(ko)))
+       panic(POS, "Tinso", "bad key");
+    tm = Mpushmark(to);
+    Mpushmark(ko);
+    if (vo)
+       Mpushmark(vo);
+    insert(to, ko, NULL, vo);
+    Mpopmark(tm);
+}
+
+Tobj Tfindi(Tobj to, long ik)
+{
+    if (!to)
+       return NULL;
+    if (!T_ISTABLE(to))
+       panic(POS, "Tfindi", "find attempted on non-table");
+    keyi.i = ik;
+    return find(to, &keyi, NULL);
+}
+
+Tobj Tfindr(Tobj to, double rk)
+{
+    if (!to)
+       return NULL;
+    if (!T_ISTABLE(to))
+       panic(POS, "Tfindr", "find attempted on non-table");
+    keyr.d = rk;
+    return find(to, &keyr, NULL);
+}
+
+Tobj Tfinds(Tobj to, char *sk)
+{
+    if (!to)
+       return NULL;
+    if (!T_ISTABLE(to))
+       panic(POS, "Tfinds", "find attempted on non-table");
+    return find(to, &keys, sk);
+}
+
+Tobj Tfindo(Tobj to, Tobj ko)
+{
+    if (!to || !ko)
+       return NULL;
+    if (!T_ISTABLE(to))
+       panic(POS, "Tfindo", "find attempted on non-table");
+    if (!(T_ISINTEGER(ko) || T_ISREAL(ko) || T_ISSTRING(ko)))
+       panic(POS, "Tfindo", "bad key");
+    return find(to, ko, NULL);
+}
+
+void Tdeli(Tobj to, long ik)
+{
+    if (!to)
+       return;
+    if (!T_ISTABLE(to))
+       panic(POS, "Tdeli", "delete attempted on non-table");
+    keyi.i = ik;
+    delete(to, &keyi, NULL);
+}
+
+void Tdelr(Tobj to, double rk)
+{
+    if (!to)
+       return;
+    if (!T_ISTABLE(to))
+       panic(POS, "Tdelr", "delete attempted on non-table");
+    keyr.d = rk;
+    delete(to, &keyr, NULL);
+}
+
+void Tdels(Tobj to, char *sk)
+{
+    if (!to)
+       return;
+    if (!T_ISTABLE(to))
+       panic(POS, "Tdels", "delete attempted on non-table");
+    delete(to, &keys, sk);
+}
+
+void Tdelo(Tobj to, Tobj ko)
+{
+    if (!to || !ko)
+       return;
+    if (!T_ISTABLE(to))
+       panic(POS, "Tdelo", "delete attempted on non-table");
+    if (!(T_ISINTEGER(ko) || T_ISREAL(ko) || T_ISSTRING(ko)))
+       panic(POS, "Tdelo", "bad key");
+    delete(to, ko, NULL);
+}
+
+Tobj Tcopy(Tobj fmvo)
+{
+    Tobj tovo;
+    long m;
+
+    switch (M_TYPEOF(fmvo)) {
+    case T_INTEGER:
+    case T_REAL:
+    case T_STRING:
+    case T_CODE:
+       tovo = fmvo;
+       break;
+    case T_TABLE:
+       mapinit();
+       m = Mpushmark(fmvo);
+       tovo = Mnew(T_TABLESIZE, T_TABLE);
+       mapinsert(fmvo, tovo);
+       reccopytable(fmvo, tovo);
+       Mpopmark(m);
+       mapterm();
+       break;
+    }
+    return tovo;
+}
+
+void Tgetfirst(Tobj to, Tkvindex_t * p)
+{
+    if (!to || !T_ISTABLE(to))
+       return;
+    p->tp = to, p->kvp = NULL, p->i = 0, p->j = 0;
+    for (; p->i < p->tp->ln; p->i++) {
+       if (!p->tp->lp[p->i])
+           continue;
+       for (; p->j < p->tp->lp[p->i]->i; p->j++) {
+           if ((p->kvp = &p->tp->lp[p->i]->kv[p->j]))
+               return;
+       }
+       p->j = 0;
+    }
+}
+
+void Tgetnext(Tkvindex_t * p)
+{
+    p->kvp = NULL;
+    p->j++;
+    for (; p->i < p->tp->ln; p->i++) {
+       if (!p->tp->lp[p->i])
+           continue;
+       for (; p->j < p->tp->lp[p->i]->i; p->j++) {
+           if ((p->kvp = &p->tp->lp[p->i]->kv[p->j]))
+               return;
+       }
+       p->j = 0;
+    }
+}
+
+static void insert(Ttable_t * tp, Tobj ko, char *sk, Tobj vo)
+{
+    Tkvlist_t *kvlp, *nkvlp;
+    Tkv_t *kvp;
+    Ttype_t kt;
+    long ik, i, ind, nln;
+    double rk;
+
+    switch ((kt = M_TYPEOF(ko))) {
+    case T_INTEGER:
+       ik = ((Tinteger_t *) ko)->i;
+       if ((kvlp = tp->lp[(ind = GETIKINDEX(tp, ik))]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQIK(ik, kvp[i].ko))
+                   goto found;
+       break;
+    case T_REAL:
+       rk = ((Treal_t *) ko)->d;
+       if ((kvlp = tp->lp[(ind = GETRKINDEX(tp, rk))]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQRK(rk, kvp[i].ko))
+                   goto found;
+       break;
+    case T_STRING:
+       if (M_AREAOF(ko) != 0)
+           sk = ((Tstring_t *) ko)->s;
+       if ((kvlp = tp->lp[(ind = GETSKINDEX(tp, sk))]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQSK(sk, kvp[i].ko))
+                   goto found;
+       break;
+    }
+    if ((nln = tp->n + 1) > 4 * tp->ln && nln < SHRT_MAX) {
+       copytable(tp, nln);
+       switch (kt) {
+       case T_INTEGER:
+           ind = GETIKINDEX(tp, ik);
+           break;
+       case T_REAL:
+           ind = GETRKINDEX(tp, rk);
+           break;
+       case T_STRING:
+           ind = GETSKINDEX(tp, sk);
+           break;
+       }
+       kvlp = tp->lp[ind];
+    }
+    if (!kvlp) {
+       tp->lp[ind] = kvlp = Mallocate((long) T_KVLISTSIZE(1));
+       kvlp->i = 0, kvlp->n = 1;
+    } else if (kvlp->i == kvlp->n) {
+       tp->lp[ind] = nkvlp = Mallocate((long) T_KVLISTSIZE(kvlp->n * 2));
+       nkvlp->n = kvlp->n * 2;
+       for (i = 0; i < kvlp->n; i++)
+           nkvlp->kv[i] = kvlp->kv[i];
+       nkvlp->i = kvlp->i;
+       Mfree(kvlp, M_BYTE2SIZE(T_KVLISTSIZE(kvlp->n))), kvlp = nkvlp;
+    }
+    if (M_AREAOF(ko) == 0) {   /* ko must be allocated */
+       switch (kt) {
+       case T_INTEGER:
+           ko = Tinteger(ik);
+           break;
+       case T_REAL:
+           ko = Treal(rk);
+           break;
+       case T_STRING:
+           ko = Tstring(sk);
+           break;
+       }
+    }
+
+    kvlp->kv[kvlp->i].ko = ko, kvlp->kv[kvlp->i++].vo = vo;
+    tp->n++;
+    tp->time = Ttime;
+    return;
+
+  found:
+    kvp[i].vo = vo;
+    tp->time = Ttime;
+}
+
+static Tobj find(Ttable_t * tp, Tobj ko, char *sk)
+{
+    Tkvlist_t *kvlp;
+    Tkv_t *kvp;
+    long ik, i;
+    double rk;
+
+    switch (M_TYPEOF(ko)) {
+    case T_INTEGER:
+       ik = ((Tinteger_t *) ko)->i;
+       if ((kvlp = tp->lp[GETIKINDEX(tp, ik)]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQIK(ik, kvp[i].ko))
+                   goto found;
+       break;
+    case T_REAL:
+       rk = ((Treal_t *) ko)->d;
+       if ((kvlp = tp->lp[GETRKINDEX(tp, rk)]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQRK(rk, kvp[i].ko))
+                   goto found;
+       break;
+    case T_STRING:
+       if (M_AREAOF(ko) != 0)
+           sk = ((Tstring_t *) ko)->s;
+       if ((kvlp = tp->lp[GETSKINDEX(tp, sk)]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQSK(sk, kvp[i].ko))
+                   goto found;
+       break;
+    }
+    return NULL;
+
+  found:
+    return kvp[i].vo;
+}
+
+static void delete(Ttable_t * tp, Tobj ko, char *sk)
+{
+    Tkvlist_t *kvlp;
+    Tkv_t *kvp;
+    long ik, i, j;
+    double rk;
+
+    switch (M_TYPEOF(ko)) {
+    case T_INTEGER:
+       ik = ((Tinteger_t *) ko)->i;
+       if ((kvlp = tp->lp[GETIKINDEX(tp, ik)]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQIK(ik, kvp[i].ko))
+                   goto found;
+       break;
+    case T_REAL:
+       rk = ((Treal_t *) ko)->d;
+       if ((kvlp = tp->lp[GETRKINDEX(tp, rk)]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQRK(rk, kvp[i].ko))
+                   goto found;
+       break;
+    case T_STRING:
+       if (M_AREAOF(ko) != 0)
+           sk = ((Tstring_t *) ko)->s;
+       if ((kvlp = tp->lp[GETSKINDEX(tp, sk)]))
+           for (i = 0, kvp = &kvlp->kv[0]; i < kvlp->i; i++)
+               if (ISEQSK(sk, kvp[i].ko))
+                   goto found;
+       break;
+    }
+    return;
+
+  found:
+    for (j = i, kvp = &kvlp->kv[0]; j < kvlp->i - 1; j++)
+       kvp[j] = kvp[j + 1];
+    kvlp->i--;
+    tp->n--;
+    tp->time = Ttime;
+}
+
+static void copytable(Ttable_t * tp, long ln)
+{
+    Tkvlist_t **olp, **lp;
+    Tkvlist_t *okvlp, *kvlp, *nkvlp;
+    Tkv_t *kvp;
+    long ik, oln, i, j, k, ind;
+    double rk;
+    char *sk;
+
+    lp = Mallocate((long) (ln * T_KVLISTPTRSIZE));
+    oln = tp->ln, tp->ln = ln;
+    olp = tp->lp, tp->lp = lp;
+    for (i = 0; i < ln; i++)
+       lp[i] = NULL;
+    for (i = 0; i < oln; i++) {
+       if (!(okvlp = olp[i]))
+           continue;
+       for (j = 0; j < okvlp->i; j++) {
+           kvp = &okvlp->kv[j];
+           switch (M_TYPEOF(kvp->ko)) {
+           case T_INTEGER:
+               ik = ((Tinteger_t *) kvp->ko)->i;
+               kvlp = lp[(ind = GETIKINDEX(tp, ik))];
+               break;
+           case T_REAL:
+               rk = ((Treal_t *) kvp->ko)->d;
+               kvlp = lp[(ind = GETRKINDEX(tp, rk))];
+               break;
+           case T_STRING:
+               sk = ((Tstring_t *) kvp->ko)->s;
+               kvlp = lp[(ind = GETSKINDEX(tp, sk))];
+               break;
+           }
+           if (!kvlp) {
+               lp[ind] = kvlp = Mallocate((long) T_KVLISTSIZE(1));
+               kvlp->i = 0, kvlp->n = 1;
+           } else if (kvlp->i == kvlp->n) {
+               lp[ind] = nkvlp =
+                   Mallocate((long) T_KVLISTSIZE(kvlp->n * 2));
+               nkvlp->n = kvlp->n * 2;
+               for (k = 0; k < kvlp->i; k++)
+                   nkvlp->kv[k] = kvlp->kv[k];
+               nkvlp->i = kvlp->i;
+               Mfree(kvlp, M_BYTE2SIZE(T_KVLISTSIZE(kvlp->n)));
+               kvlp = nkvlp;
+           }
+           kvlp->kv[kvlp->i].ko = kvp->ko, kvlp->kv[kvlp->i++].vo =
+               kvp->vo;
+       }
+       Mfree(okvlp, M_BYTE2SIZE(T_KVLISTSIZE(okvlp->n)));
+    }
+    Mfree(olp, M_BYTE2SIZE(oln * T_KVLISTPTRSIZE));
+}
+
+static void reccopytable(Ttable_t * fmtp, Ttable_t * totp)
+{
+    Tkv_t *fmkvp, *tokvp;
+    long i, j, m;
+
+    totp->lp = Mallocate((long) (fmtp->ln * T_KVLISTPTRSIZE));
+    totp->ln = fmtp->ln;
+    totp->n = fmtp->n;
+    totp->time = Ttime;
+    for (i = 0; i < totp->ln; i++) {
+       if (!fmtp->lp[i]) {
+           totp->lp[i] = NULL;
+           continue;
+       }
+       totp->lp[i] = Mallocate((long) T_KVLISTSIZE(fmtp->lp[i]->n));
+       totp->lp[i]->n = fmtp->lp[i]->n;
+       totp->lp[i]->i = 0;
+    }
+    m = Mpushmark(totp);
+    for (i = 0; i < totp->ln; i++) {
+       if (!totp->lp[i])
+           continue;
+       for (j = 0; j < fmtp->lp[i]->i; j++) {
+           fmkvp = &fmtp->lp[i]->kv[j], tokvp = &totp->lp[i]->kv[j];
+           tokvp->ko = fmkvp->ko;
+           switch (M_TYPEOF(fmkvp->vo)) {
+           case T_INTEGER:
+           case T_REAL:
+           case T_STRING:
+           case T_CODE:
+               tokvp->vo = fmkvp->vo;
+               break;
+           case T_TABLE:
+               if (!(tokvp->vo = mapfind(fmkvp->vo))) {
+                   tokvp->vo = Mnew(T_TABLESIZE, T_TABLE);
+                   mapinsert(fmkvp->vo, tokvp->vo);
+                   reccopytable(fmkvp->vo, tokvp->vo);
+               }
+               break;
+           }
+           totp->lp[i]->i++;
+       }
+    }
+    Mpopmark(m);
+}
+
+static void mapinit(void)
+{
+    long li;
+
+    for (li = 0; li < MAPLISTN; li++)
+       map.list[li] = NULL;
+}
+
+static void mapterm(void)
+{
+    mapentry_t **lp;
+    mapentry_t *cep, *nep;
+    long li;
+
+    for (li = 0; li < MAPLISTN; li++) {
+       lp = &map.list[li];
+       for (cep = *lp; cep; cep = nep) {
+           nep = cep->next;
+           Mfree(cep, mapentrybyte2size);
+       }
+       *lp = NULL;
+    }
+}
+
+static void mapinsert(Tobj fmo, Tobj too)
+{
+    mapentry_t **lp;
+    mapentry_t *cep;
+
+    lp = &map.list[(unsigned long) fmo % MAPLISTN];
+    if (!(cep = Mallocate(MAPENTRYSIZE)))
+       panic(POS, "mapinsert", "cannot allocate mapentry");
+    cep->fmo = fmo, cep->too = too;
+    cep->next = *lp, *lp = cep;
+}
+
+static Tobj mapfind(Tobj fmo)
+{
+    mapentry_t **lp;
+    mapentry_t *cep;
+
+    lp = &map.list[(unsigned long) fmo % MAPLISTN];
+    for (cep = *lp; cep; cep = cep->next)
+       if (cep->fmo == fmo)
+           return cep->too;
+    return NULL;
+}
diff --git a/cmd/lefty/code.c b/cmd/lefty/code.c
new file mode 100644 (file)
index 0000000..f083bf4
--- /dev/null
@@ -0,0 +1,113 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "code.h"
+#include "mem.h"
+
+Code_t *cbufp;
+int cbufn, cbufi;
+#define CBUFINCR 1000
+#define CBUFSIZE sizeof (Code_t)
+
+static int Cstringoffset;
+
+void Cinit(void)
+{
+    Code_t c;
+
+    cbufp = Marrayalloc((long) CBUFINCR * CBUFSIZE);
+    cbufn = CBUFINCR;
+    cbufi = 0;
+    Cstringoffset = (char *) &c.u.s[0] - (char *) &c + 1;
+    /* the + 1 above accounts for the null character */
+}
+
+void Cterm(void)
+{
+    Marrayfree(cbufp);
+    cbufp = NULL;
+    cbufn = cbufi = 0;
+}
+
+void Creset(void)
+{
+    cbufi = 0;
+}
+
+int Cnew(Ctype_t ctype)
+{
+    int i;
+
+    if (cbufi >= cbufn) {
+       cbufp = Marraygrow(cbufp, (long) (cbufn + CBUFINCR) * CBUFSIZE);
+       cbufn += CBUFINCR;
+    }
+    i = cbufi++;
+    cbufp[i].ctype = ctype;
+    cbufp[i].next = C_NULL;
+    return i;
+}
+
+int Cinteger(long i)
+{
+    int j;
+
+    if (cbufi >= cbufn) {
+       cbufp = Marraygrow(cbufp, (long) (cbufn + CBUFINCR) * CBUFSIZE);
+       cbufn += CBUFINCR;
+    }
+    j = cbufi++;
+    cbufp[j].ctype = C_INTEGER;
+    cbufp[j].u.i = i;
+    cbufp[j].next = C_NULL;
+    return j;
+}
+
+int Creal(double d)
+{
+    int i;
+
+    if (cbufi >= cbufn) {
+       cbufp = Marraygrow(cbufp, (long) (cbufn + CBUFINCR) * CBUFSIZE);
+       cbufn += CBUFINCR;
+    }
+    i = cbufi++;
+    cbufp[i].ctype = C_REAL;
+    cbufp[i].u.d = d;
+    cbufp[i].next = C_NULL;
+    return i;
+}
+
+int Cstring(char *s)
+{
+    int i, size, incr;
+
+    size = (strlen(s) + Cstringoffset + CBUFSIZE - 1) / CBUFSIZE;
+    if (cbufi + size > cbufn) {
+       incr = size > CBUFINCR ? size : CBUFINCR;
+       cbufp = Marraygrow(cbufp, (long) (cbufn + incr) * CBUFSIZE);
+       cbufn += incr;
+    }
+    i = cbufi, cbufi += size;
+    cbufp[i].ctype = C_STRING;
+    strcpy((char *) &cbufp[i].u.s[0], s);
+    cbufp[i].next = C_NULL;
+    return i;
+}
diff --git a/cmd/lefty/code.h b/cmd/lefty/code.h
new file mode 100644 (file)
index 0000000..a3d4735
--- /dev/null
@@ -0,0 +1,76 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _CODE_H
+#define _CODE_H
+#define C_NULL -1
+
+#define C_ISSTMT(ct) (ct >= C_STMT && ct <= C_RETURN)
+
+    typedef enum {
+       C_CODE, C_ASSIGN, C_INTEGER, C_REAL, C_STRING, C_OR, C_AND,
+       C_EQ, C_NE, C_LT, C_LE, C_GT, C_GE, C_PLUS, C_MINUS, C_MUL,
+       C_DIV, C_MOD, C_UMINUS, C_NOT, C_PEXPR, C_FCALL, C_GVAR, C_LVAR,
+       C_PVAR, C_FUNCTION, C_TCONS, C_DECL, C_STMT, C_IF, C_WHILE,
+       C_FOR, C_FORIN, C_BREAK, C_CONTINUE, C_RETURN, C_INTERNAL,
+       C_ARGS, C_NOP, C_SIZE
+    } Ctype_t;
+
+    typedef struct Code_t {
+       Ctype_t ctype;
+       int next;
+       union {
+           char s[1];
+           double d;
+           long i;
+           int fp;
+           void *o;
+       } u;
+    } Code_t;
+#define C_CODESIZE sizeof (Code_t)
+
+#define Cgetstring(i) (char *) &cbufp[i].u.s[0]
+#define Cgetindex() cbufi
+
+#define Csettype(a, b) cbufp[a].ctype = b
+#define Csetfp(a, b) cbufp[a].u.fp = b
+#define Csetnext(a, b) cbufp[a].next = b
+#define Csetinteger(a, b) cbufp[a].u.i = b
+#define Csetobject(a, b) cbufp[a].u.o = b
+#define Csetreal(a, b) cbufp[a].u.d = b
+
+    extern Code_t *cbufp;
+    extern int cbufn, cbufi;
+
+    void Cinit(void);
+    void Cterm(void);
+    void Creset(void);
+    int Cnew(Ctype_t);
+    int Cinteger(long);
+    int Creal(double);
+    int Cstring(char *);
+#endif                         /* _CODE_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/colors.txt b/cmd/lefty/colors.txt
new file mode 100644 (file)
index 0000000..de049b3
--- /dev/null
@@ -0,0 +1,654 @@
+colorname_t colornames[] = {
+{"aliceblue",240,248,255},
+{"antiquewhite",250,235,215},
+{"antiquewhite1",255,239,219},
+{"antiquewhite2",238,223,204},
+{"antiquewhite3",205,192,176},
+{"antiquewhite4",139,131,120},
+{"aquamarine",127,255,212},
+{"aquamarine1",127,255,212},
+{"aquamarine2",118,238,198},
+{"aquamarine3",102,205,170},
+{"aquamarine4",69,139,116},
+{"azure",240,255,255},
+{"azure1",240,255,255},
+{"azure2",224,238,238},
+{"azure3",193,205,205},
+{"azure4",131,139,139},
+{"beige",245,245,220},
+{"bisque",255,228,196},
+{"bisque1",255,228,196},
+{"bisque2",238,213,183},
+{"bisque3",205,183,158},
+{"bisque4",139,125,107},
+{"black",0,0,0},
+{"blanchedalmond",255,235,205},
+{"blue",0,0,255},
+{"blue1",0,0,255},
+{"blue2",0,0,238},
+{"blue3",0,0,205},
+{"blue4",0,0,139},
+{"blueviolet",138,43,226},
+{"brown",165,42,42},
+{"brown1",255,64,64},
+{"brown2",238,59,59},
+{"brown3",205,51,51},
+{"brown4",139,35,35},
+{"burlywood",222,184,135},
+{"burlywood1",255,211,155},
+{"burlywood2",238,197,145},
+{"burlywood3",205,170,125},
+{"burlywood4",139,115,85},
+{"cadetblue",95,158,160},
+{"cadetblue1",152,245,255},
+{"cadetblue2",142,229,238},
+{"cadetblue3",122,197,205},
+{"cadetblue4",83,134,139},
+{"chartreuse",127,255,0},
+{"chartreuse1",127,255,0},
+{"chartreuse2",118,238,0},
+{"chartreuse3",102,205,0},
+{"chartreuse4",69,139,0},
+{"chocolate",210,105,30},
+{"chocolate1",255,127,36},
+{"chocolate2",238,118,33},
+{"chocolate3",205,102,29},
+{"chocolate4",139,69,19},
+{"coral",255,127,80},
+{"coral1",255,114,86},
+{"coral2",238,106,80},
+{"coral3",205,91,69},
+{"coral4",139,62,47},
+{"cornflowerblue",100,149,237},
+{"cornsilk",255,248,220},
+{"cornsilk1",255,248,220},
+{"cornsilk2",238,232,205},
+{"cornsilk3",205,200,177},
+{"cornsilk4",139,136,120},
+{"crimson",220,20,60},
+{"cyan",0,255,255},
+{"cyan1",0,255,255},
+{"cyan2",0,238,238},
+{"cyan3",0,205,205},
+{"cyan4",0,139,139},
+{"darkgoldenrod",184,134,11},
+{"darkgoldenrod1",255,185,15},
+{"darkgoldenrod2",238,173,14},
+{"darkgoldenrod3",205,149,12},
+{"darkgoldenrod4",139,101,8},
+{"darkgreen",0,100,0},
+{"darkkhaki",189,183,107},
+{"darkolivegreen",85,107,47},
+{"darkolivegreen1",202,255,112},
+{"darkolivegreen2",188,238,104},
+{"darkolivegreen3",162,205,90},
+{"darkolivegreen4",110,139,61},
+{"darkorange",255,140,0},
+{"darkorange1",255,127,0},
+{"darkorange2",238,118,0},
+{"darkorange3",205,102,0},
+{"darkorange4",139,69,0},
+{"darkorchid",153,50,204},
+{"darkorchid1",191,62,255},
+{"darkorchid2",178,58,238},
+{"darkorchid3",154,50,205},
+{"darkorchid4",104,34,139},
+{"darksalmon",233,150,122},
+{"darkseagreen",143,188,143},
+{"darkseagreen1",193,255,193},
+{"darkseagreen2",180,238,180},
+{"darkseagreen3",155,205,155},
+{"darkseagreen4",105,139,105},
+{"darkslateblue",72,61,139},
+{"darkslategray",47,79,79},
+{"darkslategray1",151,255,255},
+{"darkslategray2",141,238,238},
+{"darkslategray3",121,205,205},
+{"darkslategray4",82,139,139},
+{"darkslategrey",47,79,79},
+{"darkturquoise",0,206,209},
+{"darkviolet",148,0,211},
+{"deeppink",255,20,147},
+{"deeppink1",255,20,147},
+{"deeppink2",238,18,137},
+{"deeppink3",205,16,118},
+{"deeppink4",139,10,80},
+{"deepskyblue",0,191,255},
+{"deepskyblue1",0,191,255},
+{"deepskyblue2",0,178,238},
+{"deepskyblue3",0,154,205},
+{"deepskyblue4",0,104,139},
+{"dimgray",105,105,105},
+{"dimgrey",105,105,105},
+{"dodgerblue",30,144,255},
+{"dodgerblue1",30,144,255},
+{"dodgerblue2",28,134,238},
+{"dodgerblue3",24,116,205},
+{"dodgerblue4",16,78,139},
+{"firebrick",178,34,34},
+{"firebrick1",255,48,48},
+{"firebrick2",238,44,44},
+{"firebrick3",205,38,38},
+{"firebrick4",139,26,26},
+{"floralwhite",255,250,240},
+{"forestgreen",34,139,34},
+{"gainsboro",220,220,220},
+{"ghostwhite",248,248,255},
+{"gold",255,215,0},
+{"gold1",255,215,0},
+{"gold2",238,201,0},
+{"gold3",205,173,0},
+{"gold4",139,117,0},
+{"goldenrod",218,165,32},
+{"goldenrod1",255,193,37},
+{"goldenrod2",238,180,34},
+{"goldenrod3",205,155,29},
+{"goldenrod4",139,105,20},
+{"gray",192,192,192},
+{"gray0",0,0,0},
+{"gray1",3,3,3},
+{"gray10",26,26,26},
+{"gray100",255,255,255},
+{"gray11",28,28,28},
+{"gray12",31,31,31},
+{"gray13",33,33,33},
+{"gray14",36,36,36},
+{"gray15",38,38,38},
+{"gray16",41,41,41},
+{"gray17",43,43,43},
+{"gray18",46,46,46},
+{"gray19",48,48,48},
+{"gray2",5,5,5},
+{"gray20",51,51,51},
+{"gray21",54,54,54},
+{"gray22",56,56,56},
+{"gray23",59,59,59},
+{"gray24",61,61,61},
+{"gray25",64,64,64},
+{"gray26",66,66,66},
+{"gray27",69,69,69},
+{"gray28",71,71,71},
+{"gray29",74,74,74},
+{"gray3",8,8,8},
+{"gray30",77,77,77},
+{"gray31",79,79,79},
+{"gray32",82,82,82},
+{"gray33",84,84,84},
+{"gray34",87,87,87},
+{"gray35",89,89,89},
+{"gray36",92,92,92},
+{"gray37",94,94,94},
+{"gray38",97,97,97},
+{"gray39",99,99,99},
+{"gray4",10,10,10},
+{"gray40",102,102,102},
+{"gray41",105,105,105},
+{"gray42",107,107,107},
+{"gray43",110,110,110},
+{"gray44",112,112,112},
+{"gray45",115,115,115},
+{"gray46",117,117,117},
+{"gray47",120,120,120},
+{"gray48",122,122,122},
+{"gray49",125,125,125},
+{"gray5",13,13,13},
+{"gray50",127,127,127},
+{"gray51",130,130,130},
+{"gray52",133,133,133},
+{"gray53",135,135,135},
+{"gray54",138,138,138},
+{"gray55",140,140,140},
+{"gray56",143,143,143},
+{"gray57",145,145,145},
+{"gray58",148,148,148},
+{"gray59",150,150,150},
+{"gray6",15,15,15},
+{"gray60",153,153,153},
+{"gray61",156,156,156},
+{"gray62",158,158,158},
+{"gray63",161,161,161},
+{"gray64",163,163,163},
+{"gray65",166,166,166},
+{"gray66",168,168,168},
+{"gray67",171,171,171},
+{"gray68",173,173,173},
+{"gray69",176,176,176},
+{"gray7",18,18,18},
+{"gray70",179,179,179},
+{"gray71",181,181,181},
+{"gray72",184,184,184},
+{"gray73",186,186,186},
+{"gray74",189,189,189},
+{"gray75",191,191,191},
+{"gray76",194,194,194},
+{"gray77",196,196,196},
+{"gray78",199,199,199},
+{"gray79",201,201,201},
+{"gray8",20,20,20},
+{"gray80",204,204,204},
+{"gray81",207,207,207},
+{"gray82",209,209,209},
+{"gray83",212,212,212},
+{"gray84",214,214,214},
+{"gray85",217,217,217},
+{"gray86",219,219,219},
+{"gray87",222,222,222},
+{"gray88",224,224,224},
+{"gray89",227,227,227},
+{"gray9",23,23,23},
+{"gray90",229,229,229},
+{"gray91",232,232,232},
+{"gray92",235,235,235},
+{"gray93",237,237,237},
+{"gray94",240,240,240},
+{"gray95",242,242,242},
+{"gray96",245,245,245},
+{"gray97",247,247,247},
+{"gray98",250,250,250},
+{"gray99",252,252,252},
+{"green",0,255,0},
+{"green1",0,255,0},
+{"green2",0,238,0},
+{"green3",0,205,0},
+{"green4",0,139,0},
+{"greenyellow",173,255,47},
+{"grey",192,192,192},
+{"grey0",0,0,0},
+{"grey1",3,3,3},
+{"grey10",26,26,26},
+{"grey100",255,255,255},
+{"grey11",28,28,28},
+{"grey12",31,31,31},
+{"grey13",33,33,33},
+{"grey14",36,36,36},
+{"grey15",38,38,38},
+{"grey16",41,41,41},
+{"grey17",43,43,43},
+{"grey18",46,46,46},
+{"grey19",48,48,48},
+{"grey2",5,5,5},
+{"grey20",51,51,51},
+{"grey21",54,54,54},
+{"grey22",56,56,56},
+{"grey23",59,59,59},
+{"grey24",61,61,61},
+{"grey25",64,64,64},
+{"grey26",66,66,66},
+{"grey27",69,69,69},
+{"grey28",71,71,71},
+{"grey29",74,74,74},
+{"grey3",8,8,8},
+{"grey30",77,77,77},
+{"grey31",79,79,79},
+{"grey32",82,82,82},
+{"grey33",84,84,84},
+{"grey34",87,87,87},
+{"grey35",89,89,89},
+{"grey36",92,92,92},
+{"grey37",94,94,94},
+{"grey38",97,97,97},
+{"grey39",99,99,99},
+{"grey4",10,10,10},
+{"grey40",102,102,102},
+{"grey41",105,105,105},
+{"grey42",107,107,107},
+{"grey43",110,110,110},
+{"grey44",112,112,112},
+{"grey45",115,115,115},
+{"grey46",117,117,117},
+{"grey47",120,120,120},
+{"grey48",122,122,122},
+{"grey49",125,125,125},
+{"grey5",13,13,13},
+{"grey50",127,127,127},
+{"grey51",130,130,130},
+{"grey52",133,133,133},
+{"grey53",135,135,135},
+{"grey54",138,138,138},
+{"grey55",140,140,140},
+{"grey56",143,143,143},
+{"grey57",145,145,145},
+{"grey58",148,148,148},
+{"grey59",150,150,150},
+{"grey6",15,15,15},
+{"grey60",153,153,153},
+{"grey61",156,156,156},
+{"grey62",158,158,158},
+{"grey63",161,161,161},
+{"grey64",163,163,163},
+{"grey65",166,166,166},
+{"grey66",168,168,168},
+{"grey67",171,171,171},
+{"grey68",173,173,173},
+{"grey69",176,176,176},
+{"grey7",18,18,18},
+{"grey70",179,179,179},
+{"grey71",181,181,181},
+{"grey72",184,184,184},
+{"grey73",186,186,186},
+{"grey74",189,189,189},
+{"grey75",191,191,191},
+{"grey76",194,194,194},
+{"grey77",196,196,196},
+{"grey78",199,199,199},
+{"grey79",201,201,201},
+{"grey8",20,20,20},
+{"grey80",204,204,204},
+{"grey81",207,207,207},
+{"grey82",209,209,209},
+{"grey83",212,212,212},
+{"grey84",214,214,214},
+{"grey85",217,217,217},
+{"grey86",219,219,219},
+{"grey87",222,222,222},
+{"grey88",224,224,224},
+{"grey89",227,227,227},
+{"grey9",23,23,23},
+{"grey90",229,229,229},
+{"grey91",232,232,232},
+{"grey92",235,235,235},
+{"grey93",237,237,237},
+{"grey94",240,240,240},
+{"grey95",242,242,242},
+{"grey96",245,245,245},
+{"grey97",247,247,247},
+{"grey98",250,250,250},
+{"grey99",252,252,252},
+{"honeydew",240,255,240},
+{"honeydew1",240,255,240},
+{"honeydew2",224,238,224},
+{"honeydew3",193,205,193},
+{"honeydew4",131,139,131},
+{"hotpink",255,105,180},
+{"hotpink1",255,110,180},
+{"hotpink2",238,106,167},
+{"hotpink3",205,96,144},
+{"hotpink4",139,58,98},
+{"indianred",205,92,92},
+{"indianred1",255,106,106},
+{"indianred2",238,99,99},
+{"indianred3",205,85,85},
+{"indianred4",139,58,58},
+{"indigo",75,0,130},
+{"ivory",255,255,240},
+{"ivory1",255,255,240},
+{"ivory2",238,238,224},
+{"ivory3",205,205,193},
+{"ivory4",139,139,131},
+{"khaki",240,230,140},
+{"khaki1",255,246,143},
+{"khaki2",238,230,133},
+{"khaki3",205,198,115},
+{"khaki4",139,134,78},
+{"lavender",230,230,250},
+{"lavenderblush",255,240,245},
+{"lavenderblush1",255,240,245},
+{"lavenderblush2",238,224,229},
+{"lavenderblush3",205,193,197},
+{"lavenderblush4",139,131,134},
+{"lawngreen",124,252,0},
+{"lemonchiffon",255,250,205},
+{"lemonchiffon1",255,250,205},
+{"lemonchiffon2",238,233,191},
+{"lemonchiffon3",205,201,165},
+{"lemonchiffon4",139,137,112},
+{"lightblue",173,216,230},
+{"lightblue1",191,239,255},
+{"lightblue2",178,223,238},
+{"lightblue3",154,192,205},
+{"lightblue4",104,131,139},
+{"lightcoral",240,128,128},
+{"lightcyan",224,255,255},
+{"lightcyan1",224,255,255},
+{"lightcyan2",209,238,238},
+{"lightcyan3",180,205,205},
+{"lightcyan4",122,139,139},
+{"lightgoldenrod",238,221,130},
+{"lightgoldenrod1",255,236,139},
+{"lightgoldenrod2",238,220,130},
+{"lightgoldenrod3",205,190,112},
+{"lightgoldenrod4",139,129,76},
+{"lightgoldenrodyellow",250,250,210},
+{"lightgray",211,211,211},
+{"lightgrey",211,211,211},
+{"lightpink",255,182,193},
+{"lightpink1",255,174,185},
+{"lightpink2",238,162,173},
+{"lightpink3",205,140,149},
+{"lightpink4",139,95,101},
+{"lightsalmon",255,160,122},
+{"lightsalmon1",255,160,122},
+{"lightsalmon2",238,149,114},
+{"lightsalmon3",205,129,98},
+{"lightsalmon4",139,87,66},
+{"lightseagreen",32,178,170},
+{"lightskyblue",135,206,250},
+{"lightskyblue1",176,226,255},
+{"lightskyblue2",164,211,238},
+{"lightskyblue3",141,182,205},
+{"lightskyblue4",96,123,139},
+{"lightslateblue",132,112,255},
+{"lightslategray",119,136,153},
+{"lightslategrey",119,136,153},
+{"lightsteelblue",176,196,222},
+{"lightsteelblue1",202,225,255},
+{"lightsteelblue2",188,210,238},
+{"lightsteelblue3",162,181,205},
+{"lightsteelblue4",110,123,139},
+{"lightyellow",255,255,224},
+{"lightyellow1",255,255,224},
+{"lightyellow2",238,238,209},
+{"lightyellow3",205,205,180},
+{"lightyellow4",139,139,122},
+{"limegreen",50,205,50},
+{"linen",250,240,230},
+{"magenta",255,0,255},
+{"magenta1",255,0,255},
+{"magenta2",238,0,238},
+{"magenta3",205,0,205},
+{"magenta4",139,0,139},
+{"maroon",176,48,96},
+{"maroon1",255,52,179},
+{"maroon2",238,48,167},
+{"maroon3",205,41,144},
+{"maroon4",139,28,98},
+{"mediumaquamarine",102,205,170},
+{"mediumblue",0,0,205},
+{"mediumorchid",186,85,211},
+{"mediumorchid1",224,102,255},
+{"mediumorchid2",209,95,238},
+{"mediumorchid3",180,82,205},
+{"mediumorchid4",122,55,139},
+{"mediumpurple",147,112,219},
+{"mediumpurple1",171,130,255},
+{"mediumpurple2",159,121,238},
+{"mediumpurple3",137,104,205},
+{"mediumpurple4",93,71,139},
+{"mediumseagreen",60,179,113},
+{"mediumslateblue",123,104,238},
+{"mediumspringgreen",0,250,154},
+{"mediumturquoise",72,209,204},
+{"mediumvioletred",199,21,133},
+{"midnightblue",25,25,112},
+{"mintcream",245,255,250},
+{"mistyrose",255,228,225},
+{"mistyrose1",255,228,225},
+{"mistyrose2",238,213,210},
+{"mistyrose3",205,183,181},
+{"mistyrose4",139,125,123},
+{"moccasin",255,228,181},
+{"navajowhite",255,222,173},
+{"navajowhite1",255,222,173},
+{"navajowhite2",238,207,161},
+{"navajowhite3",205,179,139},
+{"navajowhite4",139,121,94},
+{"navy",0,0,128},
+{"navyblue",0,0,128},
+{"oldlace",253,245,230},
+{"olivedrab",107,142,35},
+{"olivedrab1",192,255,62},
+{"olivedrab2",179,238,58},
+{"olivedrab3",154,205,50},
+{"olivedrab4",105,139,34},
+{"orange",255,165,0},
+{"orange1",255,165,0},
+{"orange2",238,154,0},
+{"orange3",205,133,0},
+{"orange4",139,90,0},
+{"orangered",255,69,0},
+{"orangered1",255,69,0},
+{"orangered2",238,64,0},
+{"orangered3",205,55,0},
+{"orangered4",139,37,0},
+{"orchid",218,112,214},
+{"orchid1",255,131,250},
+{"orchid2",238,122,233},
+{"orchid3",205,105,201},
+{"orchid4",139,71,137},
+{"palegoldenrod",238,232,170},
+{"palegreen",152,251,152},
+{"palegreen1",154,255,154},
+{"palegreen2",144,238,144},
+{"palegreen3",124,205,124},
+{"palegreen4",84,139,84},
+{"paleturquoise",175,238,238},
+{"paleturquoise1",187,255,255},
+{"paleturquoise2",174,238,238},
+{"paleturquoise3",150,205,205},
+{"paleturquoise4",102,139,139},
+{"palevioletred",219,112,147},
+{"palevioletred1",255,130,171},
+{"palevioletred2",238,121,159},
+{"palevioletred3",205,104,137},
+{"palevioletred4",139,71,93},
+{"papayawhip",255,239,213},
+{"peachpuff",255,218,185},
+{"peachpuff1",255,218,185},
+{"peachpuff2",238,203,173},
+{"peachpuff3",205,175,149},
+{"peachpuff4",139,119,101},
+{"peru",205,133,63},
+{"pink",255,192,203},
+{"pink1",255,181,197},
+{"pink2",238,169,184},
+{"pink3",205,145,158},
+{"pink4",139,99,108},
+{"plum",221,160,221},
+{"plum1",255,187,255},
+{"plum2",238,174,238},
+{"plum3",205,150,205},
+{"plum4",139,102,139},
+{"powderblue",176,224,230},
+{"purple",160,32,240},
+{"purple1",155,48,255},
+{"purple2",145,44,238},
+{"purple3",125,38,205},
+{"purple4",85,26,139},
+{"red",255,0,0},
+{"red1",255,0,0},
+{"red2",238,0,0},
+{"red3",205,0,0},
+{"red4",139,0,0},
+{"rosybrown",188,143,143},
+{"rosybrown1",255,193,193},
+{"rosybrown2",238,180,180},
+{"rosybrown3",205,155,155},
+{"rosybrown4",139,105,105},
+{"royalblue",65,105,225},
+{"royalblue1",72,118,255},
+{"royalblue2",67,110,238},
+{"royalblue3",58,95,205},
+{"royalblue4",39,64,139},
+{"saddlebrown",139,69,19},
+{"salmon",250,128,114},
+{"salmon1",255,140,105},
+{"salmon2",238,130,98},
+{"salmon3",205,112,84},
+{"salmon4",139,76,57},
+{"sandybrown",244,164,96},
+{"seagreen",46,139,87},
+{"seagreen1",84,255,159},
+{"seagreen2",78,238,148},
+{"seagreen3",67,205,128},
+{"seagreen4",46,139,87},
+{"seashell",255,245,238},
+{"seashell1",255,245,238},
+{"seashell2",238,229,222},
+{"seashell3",205,197,191},
+{"seashell4",139,134,130},
+{"sienna",160,82,45},
+{"sienna1",255,130,71},
+{"sienna2",238,121,66},
+{"sienna3",205,104,57},
+{"sienna4",139,71,38},
+{"skyblue",135,206,235},
+{"skyblue1",135,206,255},
+{"skyblue2",126,192,238},
+{"skyblue3",108,166,205},
+{"skyblue4",74,112,139},
+{"slateblue",106,90,205},
+{"slateblue1",131,111,255},
+{"slateblue2",122,103,238},
+{"slateblue3",105,89,205},
+{"slateblue4",71,60,139},
+{"slategray",112,128,144},
+{"slategray1",198,226,255},
+{"slategray2",185,211,238},
+{"slategray3",159,182,205},
+{"slategray4",108,123,139},
+{"slategrey",112,128,144},
+{"snow",255,250,250},
+{"snow1",255,250,250},
+{"snow2",238,233,233},
+{"snow3",205,201,201},
+{"snow4",139,137,137},
+{"springgreen",0,255,127},
+{"springgreen1",0,255,127},
+{"springgreen2",0,238,118},
+{"springgreen3",0,205,102},
+{"springgreen4",0,139,69},
+{"steelblue",70,130,180},
+{"steelblue1",99,184,255},
+{"steelblue2",92,172,238},
+{"steelblue3",79,148,205},
+{"steelblue4",54,100,139},
+{"tan",210,180,140},
+{"tan1",255,165,79},
+{"tan2",238,154,73},
+{"tan3",205,133,63},
+{"tan4",139,90,43},
+{"thistle",216,191,216},
+{"thistle1",255,225,255},
+{"thistle2",238,210,238},
+{"thistle3",205,181,205},
+{"thistle4",139,123,139},
+{"tomato",255,99,71},
+{"tomato1",255,99,71},
+{"tomato2",238,92,66},
+{"tomato3",205,79,57},
+{"tomato4",139,54,38},
+{"turquoise",64,224,208},
+{"turquoise1",0,245,255},
+{"turquoise2",0,229,238},
+{"turquoise3",0,197,205},
+{"turquoise4",0,134,139},
+{"violet",238,130,238},
+{"violetred",208,32,144},
+{"violetred1",255,62,150},
+{"violetred2",238,58,140},
+{"violetred3",205,50,120},
+{"violetred4",139,34,82},
+{"wheat",245,222,179},
+{"wheat1",255,231,186},
+{"wheat2",238,216,174},
+{"wheat3",205,186,150},
+{"wheat4",139,126,102},
+{"white",255,255,255},
+{"whitesmoke",245,245,245},
+{"yellow",255,255,0},
+{"yellow1",255,255,0},
+{"yellow2",238,238,0},
+{"yellow3",205,205,0},
+{"yellow4",139,139,0},
+{"yellowgreen",154,205,50},
+};
diff --git a/cmd/lefty/common.c b/cmd/lefty/common.c
new file mode 100644 (file)
index 0000000..fd511dd
--- /dev/null
@@ -0,0 +1,462 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+
+int warnflag;
+char *leftypath, *leftyoptions, *shellpath;
+jmp_buf exitljbuf;
+int idlerunmode;
+fd_set inputfds;
+#ifdef FEATURE_NETSCAPE
+static int innetscape;
+static char *nswin;
+#endif
+
+#ifndef FEATURE_MS
+#define PATHDEL '/'
+#define PATHSEP ':'
+#define PATHSEPSTR ":"
+#define PATHLEFTY "/../lib/lefty"
+#else
+#define PATHDEL '\\'
+#define PATHSEP ';'
+#define PATHSEPSTR ";"
+#define PATHLEFTY "\\..\\lib\\lefty"
+#endif
+#ifdef FEATURE_X11
+#define WINSYS "LEFTYWINSYS=X11"
+#endif
+#ifdef FEATURE_MS
+#define WINSYS "LEFTYWINSYS=mswin"
+#endif
+#ifdef FEATURE_GTK
+#ifdef FEATURE_MS
+#define WINSYS "LEFTYWINSYS=mswin"
+#else
+#define WINSYS "LEFTYWINSYS=X11"
+#endif
+#endif
+#include "g.h"
+#include "gcommon.h"
+
+#ifdef FEATURE_GTK
+static int Gnocallbacks;
+#else
+#ifdef FEATURE_MS
+extern int Gnocallbacks;
+/* #else */
+/* static int Gnocallbacks; */
+#endif
+#endif
+
+static char *pathp;
+#define PATHINCR 10240
+#define PATHSIZE sizeof (char)
+static char *cmdp;
+#define CMDINCR 4096
+#define CMDSIZE sizeof (char)
+
+static char *lpathp;
+
+int init(char *aout)
+{
+    char *s1, *s2, c = 0;
+    int k;
+#ifdef FEATURE_WIN32
+#ifndef FEATURE_GTK
+    extern HANDLE hinstance;
+    char buf[260];
+#endif
+#endif
+
+#ifdef FEATURE_NETSCAPE
+    if (getenv("INNETSCAPE"))
+       innetscape = TRUE, nswin = getenv("NSWIN");
+#endif
+    if (!(pathp = malloc(PATHINCR * PATHSIZE)))
+       panic(POS, "init", "pathp malloc failed");
+    if (!(cmdp = malloc(CMDINCR * CMDSIZE)))
+       panic(POS, "init", "cmdp malloc failed");
+    shellpath = getenv("PATH");
+#if defined(FEATURE_GTK) || defined(FEATURE_X11)
+    if (!strchr(aout, PATHDEL)) {
+       leftypath = "";
+       if ((s1 = buildpath(aout, TRUE)))
+           aout = strdup(s1);
+    } else
+       aout = strdup(aout);
+#else
+    GetModuleFileName(hinstance, buf, 260);
+    aout = buf;
+#endif
+    if (!(s1 = strrchr(aout, PATHDEL)))
+       s1 = aout;
+    *s1 = 0;
+    if (!(leftypath = malloc(PATHINCR * PATHSIZE)))
+       panic(POS, "init", "leftypath malloc failed");
+    leftypath[0] = 0;
+    if ((s1 = getenv("LEFTYPATH")))
+       strcat(leftypath, s1), strcat(leftypath, PATHSEPSTR);
+    if (*aout)
+       strcat(leftypath, aout), strcat(leftypath, PATHSEPSTR);
+    for (k = 0; k < 2; k++) {
+       if (k == 0)
+           s1 = aout;
+       else
+           s1 = shellpath;
+       while (s1) {
+           if ((s2 = strchr(s1, PATHSEP)))
+               c = *s2, *s2 = 0;
+           strcat(leftypath, s1);
+           strcat(leftypath, PATHLEFTY);
+           if (s2) {
+               *s2 = c, s1 = s2 + 1;
+               strcat(leftypath, PATHSEPSTR);
+           } else
+               s1 = NULL;
+       }
+       if (leftypath[0])
+           strcat(leftypath, PATHSEPSTR);
+    }
+#ifdef LEFTYPATH
+    strcat(leftypath, LEFTYPATH), strcat(leftypath, PATHSEPSTR);
+#endif
+    if (!(leftyoptions = getenv("LEFTYOPTIONS")))
+       leftyoptions = "";
+    putenv(WINSYS);
+    return 0;
+}
+
+void term(void)
+{
+    if (lpathp)
+       free(lpathp);
+    if (pathp)
+       free(pathp);
+    if (cmdp)
+       free(cmdp);
+}
+
+/* given a file name, it looks for this file in LEFTYPATH
+   (and if flag == TRUE in PATH)
+
+   returns the first occurance of that file or NULL
+*/
+char *buildpath(char *file, int flag)
+{
+    struct stat statbuf;
+    char *s1, *s2;
+    int mode, pathi, i;
+
+#ifdef FEATURE_NETSCAPE
+    if (flag == FALSE && innetscape) {
+#ifdef FEATURE_WIN32
+       HWND hwnd;
+       char *s;
+
+       if (!nswin) {
+           MessageBox((HWND) NULL, "error: no peer window",
+                      "Lefty Warning", MB_APPLMODAL);
+           return NULL;
+       }
+       hwnd = atol(nswin);
+       fprintf(stdout, "file %s\n", file);
+       if (fflush(stdout) == -1) {
+           MessageBox((HWND) NULL, "Lost Connection to Netscape",
+                      "Lefty Warning", MB_APPLMODAL);
+           return NULL;
+       }
+       SendMessage(hwnd, WM_USER, 12, 34);
+       fgets(pathp, PATHINCR - 1, stdin);
+       pathp[strlen(pathp) - 1] = 0;
+       if (pathp[0] == 0)
+           return NULL;
+       return pathp;
+#else
+       Window window;
+       static XButtonEvent ev;
+
+       if (!nswin) {
+           fprintf(stderr, "error: no peer window\n");
+           return NULL;
+       }
+       window = strtol(nswin, NULL, 16);
+       fprintf(stdout, "file %s\n", file);
+       if (fflush(stdout) == -1) {
+           fprintf(stderr, "Lost Connection to Netscape\n");
+           return NULL;
+       }
+       ev.type = ButtonPress;
+       ev.window = window;
+       ev.x = -123, ev.y = -123;
+       XSendEvent(Gdisplay, window, False, 0, (XEvent *) & ev);
+       XFlush(Gdisplay);
+       fgets(pathp, PATHINCR - 1, stdin);
+       pathp[strlen(pathp) - 1] = 0;
+       if (pathp[0] == 0)
+           return NULL;
+       return pathp;
+#endif
+    }
+#endif
+
+#ifndef FEATURE_MS
+    if (file && file[0] && strchr(file, PATHDEL))
+       return file;
+    mode = S_IRUSR | (flag ? S_IXUSR : 0);
+    for (i = 0; i < 2; i++) {
+       if (i == 0)
+           s1 = leftypath;
+       else
+           s1 = shellpath;
+       while (*s1) {
+           pathi = 0;
+           while (*s1 && *s1 != PATHSEP)
+               if (pathi < PATHINCR)
+                   pathp[pathi++] = *s1++;
+           if (*s1)
+               s1++;
+           if (pathi + 3 + strlen(file) >= PATHINCR)
+               continue;
+           pathp[pathi++] = PATHDEL;
+           for (s2 = file; *s2; s2++)
+               pathp[pathi++] = *s2;
+           pathp[pathi] = '\000';
+           if (stat(pathp, &statbuf) == 0 && (statbuf.st_mode & mode))
+               return pathp;
+       }
+    }
+#else
+    if (file && file[0] && strchr(file, PATHDEL))
+       return file;
+    mode = ~0;
+    for (i = 0; i < 2; i++) {
+       if (i == 0)
+           s1 = leftypath;
+       else
+           s1 = shellpath;
+       while (*s1) {
+           pathi = 0;
+           while (*s1 && *s1 != PATHSEP)
+               if (pathi < PATHINCR)
+                   pathp[pathi++] = *s1++;
+           if (*s1)
+               s1++;
+           if (pathi + 7 + strlen(file) >= PATHINCR)
+               continue;
+           pathp[pathi++] = PATHDEL;
+           for (s2 = file; *s2; s2++)
+               pathp[pathi++] = *s2;
+           if (flag) {
+               pathp[pathi++] = '.';
+               pathp[pathi++] = 'e';
+               pathp[pathi++] = 'x';
+               pathp[pathi++] = 'e';
+           }
+           pathp[pathi] = '\000';
+           if (stat(pathp, &statbuf) == 0 && (statbuf.st_mode & mode))
+               return pathp;
+       }
+    }
+#endif
+    return NULL;
+}
+
+/* given a file name (path) and an optional format (fmt)
+   it builds a shell command.
+
+   %e is replaced by the command path
+   %i      ...       the input channel descriptor
+   %o      ...       the output channel descriptor
+   %h      ...       the hostname
+
+   returns the complete command string or NULL
+*/
+char *buildcommand(char *path, char *host, int infd, int outfd, char *fmt)
+{
+    char buf[10];
+    char *s1, *s2;
+    int bufi;
+
+    cmdp[0] = '\000';
+    for (bufi = 0, s1 = fmt; *s1; s1++) {
+       if (*s1 == '%') {
+           if (*(s1 + 1) == 'e') {
+               s1++;
+               if (bufi + strlen(path) >= CMDINCR)
+                   return NULL;
+               for (s2 = path; *s2; s2++)
+                   cmdp[bufi++] = *s2;
+               continue;
+           } else if (*(s1 + 1) == 'i') {
+               if (infd == -1)
+                   buf[0] = '%', buf[1] = 'd', buf[2] = '\000';
+               else
+                   sprintf(buf, "%d", infd);
+               s1++;
+               if (bufi + strlen(buf) >= CMDINCR)
+                   return NULL;
+               for (s2 = buf; *s2; s2++)
+                   cmdp[bufi++] = *s2;
+               continue;
+           } else if (*(s1 + 1) == 'o') {
+               if (outfd == -1)
+                   buf[0] = '%', buf[1] = 'd', buf[2] = '\000';
+               else
+                   sprintf(buf, "%d", outfd);
+               s1++;
+               if (bufi + strlen(buf) >= CMDINCR)
+                   return NULL;
+               for (s2 = buf; *s2; s2++)
+                   cmdp[bufi++] = *s2;
+               continue;
+           } else if (*(s1 + 1) == 'h') {
+               s1++;
+               if (bufi + strlen(host) >= CMDINCR)
+                   return NULL;
+               for (s2 = host; *s2; s2++)
+                   cmdp[bufi++] = *s2;
+               continue;
+           }
+       }
+       if (bufi + 1 >= CMDINCR)
+           return NULL;
+       cmdp[bufi++] = *s1;
+    }
+    if (bufi + 1 >= CMDINCR)
+       return NULL;
+    cmdp[bufi] = '\000';
+    return &cmdp[0];
+}
+
+/* varargs function for printing a warning */
+void warning(char *file, int line, char *func, char *fmt, ...)
+{
+    va_list args;
+
+#ifndef FEATURE_MS
+    if (!warnflag)
+       return;
+
+    va_start(args, fmt);
+    /* Gnocallbacks = TRUE; */
+    fprintf(stderr, "warning: (file %s, line %d, func %s) ", file, line,
+           func);
+    vfprintf(stderr, fmt, args);
+    fprintf(stderr, "\n");
+    /* Gnocallbacks = FALSE; */
+    va_end(args);
+#else
+    char buf[256];
+
+    if (!warnflag)
+       return;
+
+    va_start(args, fmt);
+    vsprintf(buf, fmt, args);
+    Gnocallbacks = TRUE;
+    MessageBox((HWND) NULL, buf, "Lefty Warning", MB_APPLMODAL);
+    Gnocallbacks = FALSE;
+    va_end(args);
+#endif
+}
+
+/* varargs function for printing an error message and aborting */
+void panic(char *file, int line, char *func, char *fmt, ...)
+{
+    va_list args;
+
+#ifndef FEATURE_MS
+    va_start(args, fmt);
+    /* Gnocallbacks = TRUE; */
+    fprintf(stderr, "panic: (file %s, line %d, func %s) ", file, line,
+           func);
+    vfprintf(stderr, fmt, args);
+    fprintf(stderr, "\n");
+    fflush(stdout);
+    /* Gnocallbacks = FALSE; */
+    va_end(args);
+#else
+    char buf[256];
+
+    va_start(args, fmt);
+    vsprintf(buf, fmt, args);
+    Gnocallbacks = TRUE;
+    MessageBox((HWND) NULL, buf, "Lefty PANIC", MB_APPLMODAL);
+    Gnocallbacks = FALSE;
+    va_end(args);
+#endif
+    abort();
+}
+
+/* varargs function for printing an error message, and the
+   error message corresponding to errno and aborting
+*/
+void panic2(char *file, int line, char *func, char *fmt, ...)
+{
+    va_list args;
+
+#ifndef FEATURE_MS
+    va_start(args, fmt);
+    /* Gnocallbacks = TRUE; */
+    fprintf(stderr, "panic: (file %s, line %d, func %s) ", file, line,
+           func);
+    vfprintf(stderr, fmt, args);
+    fprintf(stderr, "\n");
+    perror("");
+    fflush(stdout);
+    /* Gnocallbacks = FALSE; */
+    va_end(args);
+#else
+    char buf[256];
+
+    va_start(args, fmt);
+    vsprintf(buf, fmt, args);
+    Gnocallbacks = TRUE;
+    MessageBox((HWND) NULL, buf, "Lefty PANIC", MB_APPLMODAL);
+    Gnocallbacks = FALSE;
+    va_end(args);
+#endif
+    abort();
+}
+
+#ifdef FEATURE_MS
+int printf(const char *fmt, ...)
+{
+    va_list args;
+    char buf[256];
+    int l;
+
+    va_start(args, fmt);
+    vsprintf(buf, fmt, args);
+    l = strlen(buf);
+    if (l > 0) {
+       if (buf[l - 1] == '\n')
+           buf[l - 1] = 0;
+    }
+    if (buf[0]) {
+       Gnocallbacks = TRUE;
+       MessageBox((HWND) NULL, buf, "Lefty printf", MB_APPLMODAL);
+       Gnocallbacks = FALSE;
+    }
+    va_end(args);
+    return l;
+}
+#endif
diff --git a/cmd/lefty/common.h b/cmd/lefty/common.h
new file mode 100644 (file)
index 0000000..4b7f2ec
--- /dev/null
@@ -0,0 +1,145 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+/* some config and conversion definitions from graphviz distribution */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef MSWIN32
+#define FEATURE_WIN32
+#define FEATURE_MS
+#else
+#ifndef FEATURE_GTK
+#define FEATURE_X11
+#endif
+#endif
+#ifdef HAVECS
+#define FEATURE_CS
+#endif
+#ifdef HAVENETSCAPE
+#define FEATURE_NETSCAPE
+#endif
+#ifdef HAVEGMAP
+#define FEATURE_GMAP
+#define FEATURE_MINTSIZE
+#endif
+#ifdef HAVEDOT
+#define FEATURE_DOT
+#endif
+#ifdef GNU
+#define FEATURE_GNU
+#endif
+#ifdef HAVERUSAGE
+#define FEATURE_RUSAGE
+#endif
+/* */
+
+#ifdef FEATURE_CS
+#include <ast.h>
+#else
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <ctype.h>
+
+#ifdef FEATURE_WIN32
+#include <windows.h>
+#include <commdlg.h>
+#endif
+#ifdef FEATURE_MS
+#include <malloc.h>
+#endif
+
+#define POS __FILE__, __LINE__
+
+#ifndef TRUE
+#define TRUE  1
+#define FALSE 0
+#endif
+
+#ifndef L_SUCCESS
+#define L_SUCCESS 1
+#define L_FAILURE 0
+#endif
+
+#define CHARSRC 0
+#define FILESRC 1
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef REALSTRCMP
+#define Strcmp(s1, s2) ( \
+    *(s1) == *(s2) ? ( \
+        (*s1) ? strcmp ((s1) + 1, (s2) + 1) : 0 \
+    ) : (*(s1) < *(s2) ? -1 : 1) \
+)
+#else
+#define Strcmp(s1, s2) strcmp ((s1), (s2))
+#endif
+
+    extern int warnflag;
+    extern char *leftypath, *leftyoptions, *shellpath;
+    extern jmp_buf exitljbuf;
+    extern int idlerunmode;
+    extern fd_set inputfds;
+
+    int init(char *);
+    void term(void);
+    char *buildpath(char *, int);
+    char *buildcommand(char *, char *, int, int, char *);
+    void warning(char *, int, char *, char *, ...);
+    void panic(char *, int, char *, char *, ...);
+    void panic2(char *, int, char *, char *, ...);
+#endif                         /* _COMMON_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/display.c b/cmd/lefty/display.c
new file mode 100644 (file)
index 0000000..c5bbdda
--- /dev/null
@@ -0,0 +1,184 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "mem.h"
+#include "code.h"
+#include "tbl.h"
+#include "str.h"
+#include "display.h"
+
+typedef struct dnode_t {
+    Ttype_t ttype;
+    Tobj ko, vo;
+    char *path;
+} dnode_t;
+
+typedef struct seennode_t {
+    Tobj vo;
+    char *path;
+} seennode_t;
+static seennode_t *seenp;
+static int seeni, seenn;
+#define SEENINCR 100
+#define SEENSIZE sizeof (seennode_t)
+
+static int indent, afternl;
+
+static void update(dnode_t *);
+static int cmp(const void *, const void *);
+static seennode_t *findseen(dnode_t *);
+static void add2seen(dnode_t *);
+static void pr(char *);
+
+void Dinit(void)
+{
+    seenp = Marrayalloc((long) SEENINCR * SEENSIZE);
+    seeni = 0;
+    seenn = SEENINCR;
+}
+
+void Dterm(void)
+{
+    Marrayfree(seenp);
+    seeni = seenn = 0;
+}
+
+void Dtrace(Tobj to, int offset)
+{
+    dnode_t dnode;
+    char *s;
+    int i;
+
+    indent = offset - 2;
+    afternl = TRUE;
+    if (Tgettype(to) != T_TABLE) {
+       pr((s = Ssfull(NULL, to))), free(s);
+       return;
+    }
+
+    seeni = 0;
+    dnode.vo = to;
+    dnode.path = "";
+    update(&dnode);
+    for (i = 0; i < seeni; i++)
+       free(seenp[i].path), seenp[i].path = NULL;
+}
+
+static void update(dnode_t * pnode)
+{
+    Tkvindex_t tkvi;
+    dnode_t *list, *cnode;
+    seennode_t *seennode;
+    char *s;
+    long i, n;
+
+    indent += 2;
+    n = ((Ttable_t *) pnode->vo)->n;
+    if (!(list = malloc(n * sizeof(dnode_t))))
+       panic(POS, "update", "list malloc failed");
+    for (cnode = &list[0], Tgetfirst(pnode->vo, &tkvi); tkvi.kvp;
+        cnode++, Tgetnext(&tkvi)) {
+       cnode->ko = tkvi.kvp->ko;
+       cnode->vo = tkvi.kvp->vo;
+       cnode->ttype = Tgettype(cnode->vo);
+    }
+    qsort((char *) list, n, sizeof(dnode_t), cmp);
+    for (i = 0, cnode = &list[0]; i < n; i++, cnode++) {
+       cnode->path = Spath(pnode->path, cnode->ko);
+       seennode = findseen(cnode);
+       if (seennode) {
+           pr((s = Sseen(cnode->ko, seennode->path))), free(s);
+       } else {
+           add2seen(cnode);
+           if (cnode->ttype == T_TABLE) {
+               pr((s = Stfull(cnode->ko))), free(s);
+               update(cnode);
+               pr("];");
+           } else {
+               pr((s = Ssfull(cnode->ko, cnode->vo))), free(s);
+           }
+       }
+    }
+    free(list);
+    indent -= 2;
+}
+
+static int cmp(const void *a, const void *b)
+{
+    Ttype_t atype, btype;
+    dnode_t *anode, *bnode;
+    double d1 = 0.0, d2 = 0.0;
+
+    anode = (dnode_t *) a, bnode = (dnode_t *) b;
+    atype = Tgettype(anode->ko), btype = Tgettype(bnode->ko);
+    if (atype != btype)
+       return (atype - btype);
+    if (atype == T_STRING)
+       return Strcmp(Tgetstring(anode->ko), Tgetstring(bnode->ko));
+    if (atype == T_INTEGER)
+       d1 = Tgetinteger(anode->ko), d2 = Tgetinteger(bnode->ko);
+    else if (atype == T_REAL)
+       d1 = Tgetreal(anode->ko), d2 = Tgetreal(bnode->ko);
+    if (d1 < d2)
+       return -1;
+    else if (d1 > d2)
+       return 1;
+    else
+       return 0;               /* but this should never happen since keys are unique */
+}
+
+static seennode_t *findseen(dnode_t * cnode)
+{
+    int i;
+
+    for (i = 0; i < seeni; i++)
+       if (seenp[i].vo == cnode->vo)
+           return &seenp[i];
+    return NULL;
+}
+
+static void add2seen(dnode_t * cnode)
+{
+    if (seeni >= seenn) {
+       seenp = Marraygrow(seenp, (long) (seenn + SEENINCR) * SEENSIZE);
+       seenn += SEENINCR;
+    }
+    seenp[seeni].vo = cnode->vo;
+    seenp[seeni++].path = cnode->path;
+}
+
+static void pr(char *s)
+{
+    char *s1;
+    int i;
+
+    for (s1 = s; *s1; s1++) {
+       if (afternl) {
+           for (i = 0; i < indent; i++)
+               putchar(' ');
+           afternl = FALSE;
+       }
+       if (*s1 == '\n')
+           afternl = TRUE;
+       putchar((*s1));         /* HACK: to keep proto happy */
+    }
+    putchar('\n');
+    afternl = TRUE;
+}
diff --git a/cmd/lefty/display.h b/cmd/lefty/display.h
new file mode 100644 (file)
index 0000000..3fd8ec3
--- /dev/null
@@ -0,0 +1,33 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _DISPLAY_H
+#define _DISPLAY_H
+    void Dinit(void);
+    void Dterm(void);
+    void Dtrace(Tobj, int);
+#endif                         /* _DISPLAY_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/exec.c b/cmd/lefty/exec.c
new file mode 100644 (file)
index 0000000..634965c
--- /dev/null
@@ -0,0 +1,1017 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "mem.h"
+#include "code.h"
+#include "tbl.h"
+#include "str.h"
+#include "exec.h"
+#include "internal.h"
+
+#define VOLATILE
+
+static lvar_t *lvarp;
+static int lvarn, llvari, flvari;
+#define LVARINCR 1000
+#define LVARSIZE sizeof (lvar_t)
+
+Tobj root, null;
+Tobj rtno;
+int Erun;
+int Eerrlevel, Estackdepth, Eshowbody, Eshowcalls, Eoktorun;
+
+#define PUSHJMP(op, np, b) op = (VOLATILE jmp_buf *) np, np = (jmp_buf *) &b
+#define POPJMP(op, np) np = (jmp_buf *) op
+
+/* longjmps for normal program execution */
+typedef enum {
+    PLJ_BREAK, PLJ_CONTINUE, PLJ_RETURN, PLJ_SIZE
+} PLJtype_t;
+static jmp_buf *pljbufp1, *pljbufp2;
+static PLJtype_t pljtype;
+
+/* longjmp for error handling */
+static jmp_buf *eljbufp;
+
+/* error levels and types */
+typedef enum {
+    ERR0, ERR1, ERR2, ERR3, ERR4, ERR5
+} errlevel_t;
+typedef enum {
+    ERRNOLHS, ERRNORHS, ERRNOSUCHFUNC, ERRBADARG, ERRARGMIS, ERRNOTATABLE,
+    ERRIFUNCERR, ERRRECRUN, ERRTABLECHANGED
+} errnum_t;
+static char *errnam[] = {
+    "no variable",
+    "no value",
+    "no such function",
+    "bad argument",
+    "argument number mismatch",
+    "not a table",
+    "internal function call error",
+    "recursive run attempt",
+    "table changed during a forin loop",
+};
+
+static int errdo;
+
+/* stack information */
+typedef struct sinfo_t {
+    Tobj co, fco;
+    int ci, fci;
+    int flvari, llvari;
+} sinfo_t;
+#define SINFOSIZE sizeof (sinfo_t)
+#define SINFOINCR 100
+static sinfo_t *sinfop;
+static int sinfoi, sinfon;
+
+typedef enum {
+    TNK_LI, TNK_O, TNK_S
+} tnktype_t;
+typedef struct tnk_t {
+    tnktype_t type;
+    union {
+       int li;
+       struct {
+           Tobj to, ko;
+       } tnko;
+       struct {
+           Ctype_t kt;
+           Tobj to, co;
+           int vi;
+       } tnks;
+    } u;
+} tnk_t;
+
+typedef struct Num_t {
+    Ctype_t type;
+    union {
+       long i;
+       double d;
+       Tobj no;
+    } u;
+} Num_t;
+
+static long rootm;
+static int running;
+
+static Tobj eeval(Tobj, int);
+static Tobj efcall(Tobj, int);
+static void ewhilest(Tobj, int);
+static void eforst(Tobj, int);
+static void eforinst(Tobj, int);
+
+static Tobj getval(Tobj, int);
+static int getvar(Tobj, int, tnk_t *);
+static void setvar(tnk_t, Tobj);
+static int boolop(Tobj);
+static int orderop(Tobj, Ctype_t, Tobj);
+static Tobj arithop(Num_t *, Ctype_t, Num_t *);
+static void err(int, int, Tobj, int);
+static void printbody(char *, int);
+
+void Einit(void)
+{
+    root = Ttable(100);
+    rootm = Mpushmark(root);
+    Tinss(root, "null", (null = Ttable(2)));
+    rtno = NULL;
+    pljbufp1 = pljbufp2 = NULL, pljtype = (PLJtype_t) 0;
+    eljbufp = NULL;
+    lvarp = Marrayalloc((long) LVARINCR * LVARSIZE);
+    lvarn = LVARINCR;
+    llvari = 0;
+    flvari = 0;
+    sinfop = Marrayalloc((long) SINFOINCR * SINFOSIZE);
+    sinfon = SINFOINCR;
+    sinfoi = 0;
+    Erun = FALSE;
+    running = 0;
+    Eoktorun = FALSE;
+}
+
+void Eterm(void)
+{
+    Marrayfree(sinfop), sinfop = NULL, sinfon = 0, sinfoi = 0;
+    Marrayfree(lvarp), lvarp = NULL, lvarn = 0, llvari = 0, flvari = 0;
+    rtno = NULL;
+    null = NULL;
+    Mpopmark(rootm);
+}
+
+Tobj Eunit(Tobj co)
+{
+    VOLATILE jmp_buf *oeljbufp;
+    VOLATILE int ownsinfoi;
+    VOLATILE long m;
+    VOLATILE Tobj lrtno;
+
+    jmp_buf eljbuf;
+
+#if 0
+    if (running && !Eoktorun) {
+       err(ERRRECRUN, ERR2, NULL, 0);
+       return NULL;
+    }
+#endif
+    Eoktorun = FALSE;
+
+    if (!co)
+       return NULL;
+
+    if (Tgettype(co) != T_CODE)
+       panic(POS, "Eunit", "argument type is not T_CODE");
+
+    m = Mpushmark(co);
+    PUSHJMP(oeljbufp, eljbufp, eljbuf);
+    ownsinfoi = sinfoi++;
+    if (sinfoi == sinfon) {
+       sinfop =
+           Marraygrow(sinfop, (long) (sinfon + SINFOINCR) * SINFOSIZE);
+       sinfon += SINFOINCR;
+    }
+    sinfop[ownsinfoi].co = co;
+    sinfop[ownsinfoi].ci = TCgetfp(co, 0);
+    sinfop[ownsinfoi].fco = NULL;
+    sinfop[ownsinfoi].flvari = flvari;
+    sinfop[ownsinfoi].llvari = llvari;
+    running++;
+    if (setjmp(*eljbufp))
+       lrtno = NULL;
+    else
+       lrtno = eeval(co, TCgetfp(co, 0));
+    running--;
+    rtno = NULL;
+    flvari = sinfop[ownsinfoi].flvari;
+    llvari = sinfop[ownsinfoi].llvari;
+    sinfoi = ownsinfoi;
+    POPJMP(oeljbufp, eljbufp);
+    Mpopmark(m);
+    Erun = TRUE;
+    return lrtno;
+}
+
+/* shortcut: this function executes a piece of code that corresponds to
+   <internal func name> = function () internal "<internal func name>";
+*/
+Tobj Efunction(Tobj co, char *ifnam)
+{
+    Tobj v1o;
+    int fi;
+
+    fi = TCgetnext(co, TCgetfp(co, TCgetfp(co, 0)));
+    v1o = Tcode(TCgetaddr(co, fi), fi,
+               (int) TCgetinteger(co, TCgetfp(co, fi)));
+    Tinss(root, ifnam, v1o);
+    return v1o;
+}
+
+static Tobj eeval(Tobj co, int ci)
+{
+    Tobj v1o, v2o, v3o;
+    Ttype_t ttype;
+    Ctype_t ctype;
+    tnk_t tnk;
+    Num_t lnum, rnum;
+    long m1 = 0, m2;
+    int i1, i2, res;
+
+  tailrec:
+    errdo = TRUE;
+    v1o = NULL;
+    ctype = TCgettype(co, ci);
+    switch (ctype) {
+    case C_ASSIGN:
+       i1 = TCgetfp(co, ci);
+       if ((v1o = eeval(co, TCgetnext(co, i1))) == NULL) {
+           err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+           return NULL;
+       }
+       m1 = Mpushmark(v1o);
+       res = getvar(co, i1, &tnk);
+       Mpopmark(m1);
+       if (res == -1) {
+           err(ERRNOLHS, ERR3, co, i1);
+           return NULL;
+       }
+       setvar(tnk, v1o);
+       return v1o;
+    case C_OR:
+    case C_AND:
+    case C_NOT:
+       i1 = TCgetfp(co, ci);
+       if ((v1o = eeval(co, i1)) == NULL)
+           err(ERRNORHS, ERR4, co, i1);
+       switch (ctype) {
+       case C_OR:
+           if (boolop(v1o) == TRUE)
+               return Ttrue;
+           if ((v1o = eeval(co, TCgetnext(co, i1))) == NULL)
+               err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+           return (boolop(v1o) == TRUE) ? Ttrue : Tfalse;
+       case C_AND:
+           if (boolop(v1o) == FALSE)
+               return Tfalse;
+           if ((v1o = eeval(co, TCgetnext(co, i1))) == NULL)
+               err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+           return (boolop(v1o) == FALSE) ? Tfalse : Ttrue;
+       case C_NOT:
+           return (boolop(v1o) == TRUE) ? Tfalse : Ttrue;
+       default:
+           break;
+       }
+       /* NOT REACHED */
+       return Tfalse;
+    case C_EQ:
+    case C_NE:
+    case C_LT:
+    case C_LE:
+    case C_GT:
+    case C_GE:
+       i1 = TCgetfp(co, ci);
+       if ((v1o = eeval(co, i1)) == NULL)
+           err(ERRNORHS, ERR4, co, i1);
+       else
+           m1 = Mpushmark(v1o);
+       if ((v2o = eeval(co, TCgetnext(co, i1))) == NULL)
+           err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+       if (v1o)
+           Mpopmark(m1);
+       return (orderop(v1o, ctype, v2o) == TRUE) ? Ttrue : Tfalse;
+    case C_PLUS:
+    case C_MINUS:
+    case C_MUL:
+    case C_DIV:
+    case C_MOD:
+    case C_UMINUS:
+       i1 = TCgetfp(co, ci);
+       if ((lnum.type = TCgettype(co, i1)) == C_INTEGER)
+           lnum.u.i = TCgetinteger(co, i1);
+       else if (lnum.type == C_REAL)
+           lnum.u.d = TCgetreal(co, i1);
+       else if ((lnum.u.no = eeval(co, i1)) == NULL) {
+           err(ERRNORHS, ERR4, co, i1);
+           return NULL;
+       }
+       if (ctype == C_UMINUS) {
+           if (!(v1o = arithop(&lnum, ctype, NULL)))
+               err(ERRNORHS, ERR4, co, ci);
+           return v1o;
+       }
+       if (lnum.type != C_INTEGER && lnum.type != C_REAL)
+           m1 = Mpushmark(lnum.u.no);
+       i1 = TCgetnext(co, i1);
+       if ((rnum.type = TCgettype(co, i1)) == C_INTEGER)
+           rnum.u.i = TCgetinteger(co, i1);
+       else if (rnum.type == C_REAL)
+           rnum.u.d = TCgetreal(co, i1);
+       else if ((rnum.u.no = eeval(co, i1)) == NULL)
+           err(ERRNORHS, ERR4, co, i1);
+       if (lnum.type != C_INTEGER && lnum.type != C_REAL)
+           Mpopmark(m1);
+       if (!(v1o = arithop(&lnum, ctype, &rnum)))
+           err(ERRNORHS, ERR4, co, ci);
+       return v1o;
+    case C_PEXPR:
+       ci = TCgetfp(co, ci);
+       goto tailrec;
+    case C_FCALL:
+       return efcall(co, ci);
+    case C_INTEGER:
+       return Tinteger(TCgetinteger(co, ci));
+    case C_REAL:
+       return Treal(TCgetreal(co, ci));
+    case C_STRING:
+       return Tstring(TCgetstring(co, ci));
+    case C_GVAR:
+    case C_LVAR:
+    case C_PVAR:
+       return getval(co, ci);
+    case C_FUNCTION:
+       return Tcode(TCgetaddr(co, ci), ci,
+                    (int) TCgetinteger(co, TCgetfp(co, ci)));
+    case C_TCONS:
+       v1o = Ttable(0);
+       m1 = Mpushmark(v1o);
+       for (i1 = TCgetfp(co, ci); i1 != C_NULL;
+            i1 = TCgetnext(co, TCgetnext(co, i1))) {
+           if (!(v3o = eeval(co, TCgetnext(co, i1)))) {
+               err(ERRNORHS, ERR4, co, TCgetnext(co, i1));
+               continue;
+           }
+           m2 = Mpushmark(v3o);
+           if (!(v2o = eeval(co, i1))) {
+               err(ERRNOLHS, ERR3, co, i1);
+               Mpopmark(m2);
+               continue;
+           }
+           ttype = Tgettype(v2o);
+           if (ttype == T_INTEGER || ttype == T_REAL || ttype == T_STRING)
+               Tinso(v1o, v2o, v3o);
+           else
+               err(ERRNOLHS, ERR1, co, i1);
+       }
+       Mpopmark(m1);
+       return v1o;
+    case C_STMT:
+       for (i1 = TCgetfp(co, ci); i1 != C_NULL;)
+           if ((i2 = TCgetnext(co, i1)) != C_NULL) {
+               eeval(co, i1);
+               i1 = i2;
+           } else {
+               ci = i1;
+               goto tailrec;
+           }
+       /* NOT REACHED */
+       break;
+    case C_IF:
+       i1 = TCgetfp(co, ci);
+       if (!(v1o = eeval(co, i1)))
+           err(ERRNORHS, ERR5, co, i1);
+       if (boolop(v1o) == TRUE) {
+           ci = TCgetnext(co, i1);
+           goto tailrec;
+       } else if ((ci = TCgetnext(co, TCgetnext(co, i1))) != C_NULL)
+           goto tailrec;
+       break;
+    case C_WHILE:
+       ewhilest(co, ci);
+       break;
+    case C_FOR:
+       eforst(co, ci);
+       break;
+    case C_FORIN:
+       eforinst(co, ci);
+       break;
+    case C_BREAK:
+       pljtype = PLJ_BREAK;
+       longjmp(*pljbufp1, 1);
+       /* NOT REACHED */
+       break;
+    case C_CONTINUE:
+       pljtype = PLJ_CONTINUE;
+       longjmp(*pljbufp1, 1);
+       /* NOT REACHED */
+       break;
+    case C_RETURN:
+       if ((i1 = TCgetfp(co, ci)) != C_NULL)
+           rtno = eeval(co, i1);
+       pljtype = PLJ_RETURN;
+       longjmp(*pljbufp2, 1);
+       /* NOT REACHED */
+       break;
+    default:
+       panic(POS, "eeval", "unknown program token type %d", ctype);
+    }
+    return v1o;
+}
+
+static Tobj efcall(Tobj co, int ci)
+{
+    VOLATILE jmp_buf *opljbufp1, *opljbufp2;
+    VOLATILE long m;
+    VOLATILE int bi, ownsinfoi, li, ln;
+
+    jmp_buf pljbuf;
+    Tobj fdo, vo, lrtno;
+    int i, fci, ai, di, di1, fid;
+
+    ownsinfoi = sinfoi++;
+    if (sinfoi == sinfon) {
+       sinfop =
+           Marraygrow(sinfop, (long) (sinfon + SINFOINCR) * SINFOSIZE);
+       sinfon += SINFOINCR;
+    }
+    sinfop[ownsinfoi].co = co;
+    sinfop[ownsinfoi].ci = ci;
+    sinfop[ownsinfoi].fco = NULL;
+    sinfop[ownsinfoi].flvari = flvari;
+    sinfop[ownsinfoi].llvari = llvari;
+    fci = TCgetfp(co, ci);
+    if (!(fdo = getval(co, fci)) || Tgettype(fdo) != T_CODE) {
+       err(ERRNOSUCHFUNC, ERR2, co, fci);
+       sinfoi = ownsinfoi;
+       return NULL;
+    }
+
+    m = Mpushmark((Tobj) fdo);
+    ai = TCgetfp(co, TCgetnext(co, fci));
+    ln = (int) TCgetinteger(fdo, (li = TCgetnext(fdo, TCgetfp(fdo, 0))));
+    di = TCgetnext(fdo, li);
+    bi = TCgetnext(fdo, di);
+    if (bi != C_NULL && TCgettype(fdo, bi) == C_INTERNAL) {
+       for (i = 0; ai != C_NULL; ai = TCgetnext(co, ai), i++) {
+           if (!(vo = eeval(co, ai))) {
+               err(ERRBADARG, ERR2, co, ai);
+               Mpopmark(m);
+               llvari = sinfop[ownsinfoi].llvari;
+               sinfoi = ownsinfoi;
+               return NULL;
+           }
+           if (llvari + 1 > lvarn) {
+               lvarp = Marraygrow(lvarp, (long) (llvari + 1) * LVARSIZE);
+               lvarn = llvari + 1;
+           }
+           lvarp[llvari].m = Mpushmark((lvarp[llvari].o = vo));
+           llvari++;
+       }
+       fid = (int) TCgetinteger(fdo, TCgetfp(fdo, bi));
+       if (Ifuncs[fid].min > i || Ifuncs[fid].max < i) {
+           err(ERRARGMIS, ERR2, co, ci);
+           Mpopmark(m);
+           llvari = sinfop[ownsinfoi].llvari;
+           sinfoi = ownsinfoi;
+           return NULL;
+       }
+       flvari = sinfop[ownsinfoi].llvari;
+       sinfop[ownsinfoi].fco = fdo;
+       sinfop[ownsinfoi].fci = bi;
+       if (fid < 0 || fid >= Ifuncn)
+           panic(POS, "efcall", "no such internal function: %d", fid);
+       rtno = Ttrue;
+       if ((*Ifuncs[fid].func) (i, &lvarp[flvari]) == L_FAILURE) {
+           rtno = NULL;
+           err(ERRIFUNCERR, ERR2, co, ci);
+       }
+    } else {
+       if (llvari + ln > lvarn) {
+           lvarp = Marraygrow(lvarp, (long) (llvari + ln) * LVARSIZE);
+           lvarn = llvari + ln;
+       }
+       di1 = TCgetfp(fdo, di);
+       for (i = 0; i < ln && di1 != C_NULL && ai != C_NULL;
+            i++, ai = TCgetnext(co, ai)) {
+           if (!(vo = eeval(co, ai))) {
+               err(ERRBADARG, ERR2, co, ai);
+               Mpopmark(m);
+               llvari = sinfop[ownsinfoi].llvari;
+               sinfoi = ownsinfoi;
+               return NULL;
+           }
+           lvarp[llvari].m = Mpushmark((lvarp[llvari].o = vo));
+           llvari++;
+           di1 = TCgetnext(fdo, di1);
+       }
+       if (di1 != C_NULL || ai != C_NULL) {
+           err(ERRARGMIS, ERR2, co, ci);
+           Mpopmark(m);
+           llvari = sinfop[ownsinfoi].llvari;
+           sinfoi = ownsinfoi;
+           return NULL;
+       }
+       for (; i < ln; i++, llvari++)
+           lvarp[llvari].m = Mpushmark((lvarp[llvari].o = NULL));
+       flvari = sinfop[ownsinfoi].llvari;
+       PUSHJMP(opljbufp2, pljbufp2, pljbuf);
+       opljbufp1 = (VOLATILE jmp_buf *) pljbufp1;
+       if (setjmp(*pljbufp2)) {
+           ;
+       } else {
+           sinfop[ownsinfoi].fco = fdo;
+           for (; bi != C_NULL; bi = TCgetnext(fdo, bi)) {
+               sinfop[ownsinfoi].fci = bi;
+               if (TCgettype(fdo, bi) != C_DECL)
+                   eeval((Tobj) fdo, bi);
+           }
+       }
+       POPJMP(opljbufp2, pljbufp2);
+       pljbufp1 = (jmp_buf *) opljbufp1;
+    }
+    flvari = sinfop[ownsinfoi].flvari;
+    llvari = sinfop[ownsinfoi].llvari;
+    sinfoi = ownsinfoi;
+    Mpopmark(m);
+    lrtno = rtno, rtno = NULL;
+    errdo = TRUE;
+    return lrtno;
+}
+
+static void ewhilest(Tobj co, int ci)
+{
+    VOLATILE jmp_buf *opljbufp;
+    VOLATILE jmp_buf pljbuf;
+    VOLATILE Tobj c1o;
+    VOLATILE int ei, si;
+
+    Tobj v1o;
+
+    c1o = (Tobj) co;           /* protect argument from longjmp */
+    ei = TCgetfp(c1o, ci);
+    si = TCgetnext(c1o, ei);
+    PUSHJMP(opljbufp, pljbufp1, pljbuf);
+    for (;;) {
+       if (!(v1o = eeval((Tobj) c1o, ei)))
+           err(ERRNORHS, ERR5, c1o, ei);
+       if (boolop(v1o) == FALSE)
+           break;
+       if (setjmp(*pljbufp1)) {
+           if (pljtype == PLJ_CONTINUE)
+               continue;
+           else if (pljtype == PLJ_BREAK)
+               break;
+       }
+       eeval((Tobj) c1o, si);
+    }
+    POPJMP(opljbufp, pljbufp1);
+}
+
+static void eforst(Tobj co, int ci)
+{
+    VOLATILE jmp_buf *opljbufp;
+    VOLATILE jmp_buf pljbuf;
+    VOLATILE Tobj c1o;
+    VOLATILE int ei1, ei2, ei3, si, eisnop1, eisnop2, eisnop3;
+
+    Tobj v1o;
+
+    c1o = (Tobj) co;           /* protect argument from longjmp */
+    ei1 = TCgetfp(c1o, ci);
+    ei2 = TCgetnext(c1o, ei1);
+    ei3 = TCgetnext(c1o, ei2);
+    si = TCgetnext(c1o, ei3);
+    eisnop1 = (TCgettype(c1o, ei1) == C_NOP);
+    eisnop2 = (TCgettype(c1o, ei2) == C_NOP);
+    eisnop3 = (TCgettype(c1o, ei3) == C_NOP);
+    PUSHJMP(opljbufp, pljbufp1, pljbuf);
+    if (!eisnop1)
+       eeval((Tobj) c1o, ei1);
+    for (;;) {
+       if (!eisnop2) {
+           if (!(v1o = eeval((Tobj) c1o, ei2)))
+               err(ERRNORHS, ERR5, c1o, ei2);
+           if (boolop(v1o) == FALSE)
+               break;
+       }
+       if (setjmp(*pljbufp1) != 0) {
+           if (pljtype == PLJ_CONTINUE);
+           else if (pljtype == PLJ_BREAK)
+               break;
+       } else {
+           eeval((Tobj) c1o, si);
+       }
+       if (!eisnop3)
+           eeval((Tobj) c1o, ei3);
+    }
+    POPJMP(opljbufp, pljbufp1);
+}
+
+static void eforinst(Tobj co, int ci)
+{
+    VOLATILE jmp_buf *opljbufp;
+    VOLATILE jmp_buf pljbuf;
+    VOLATILE Tobj tblo, c1o;
+    VOLATILE Tkvindex_t tkvi;
+    VOLATILE tnk_t tnk;
+    VOLATILE long km = 0, t, tm;
+    VOLATILE int ei1, ei2, si;
+
+    c1o = (Tobj) co;           /* protect argument from longjmp */
+    ei1 = TCgetfp(c1o, ci);
+    ei2 = TCgetnext(c1o, ei1);
+    si = TCgetnext(c1o, ei2);
+    if (getvar((Tobj) c1o, ei1, (tnk_t *) & tnk) == -1) {
+       err(ERRNOLHS, ERR3, c1o, ei1);
+       return;
+    }
+    if (tnk.type == TNK_O)
+       km = Mpushmark(tnk.u.tnko.ko);
+    if (!(tblo = (Tobj) eeval((Tobj) c1o, ei2))) {
+       if (tnk.type == TNK_O)
+           Mpopmark(km);
+       err(ERRNORHS, ERR4, c1o, ei2);
+       return;
+    }
+    if (Tgettype(tblo) != T_TABLE) {
+       err(ERRNOTATABLE, ERR1, c1o, ei2);
+       return;
+    }
+    tm = Mpushmark(tblo);
+    PUSHJMP(opljbufp, pljbufp1, pljbuf);
+    t = Tgettime(tblo);
+    for (Tgetfirst((Tobj) tblo, (Tkvindex_t *) & tkvi); tkvi.kvp;
+        Tgetnext((Tkvindex_t *) & tkvi)) {
+       setvar(tnk, tkvi.kvp->ko);
+       if (setjmp(*pljbufp1) != 0) {
+           if (pljtype == PLJ_CONTINUE)
+               continue;
+           else if (pljtype == PLJ_BREAK)
+               break;
+       }
+       eeval((Tobj) c1o, si);
+       if (t != Tgettime(tblo)) {
+           err(ERRTABLECHANGED, ERR1, c1o, ei2);
+           break;
+       }
+    }
+    POPJMP(opljbufp, pljbufp1);
+    if (tnk.type == TNK_O)
+       Mpopmark(km);
+    Mpopmark(tm);
+}
+
+static Tobj getval(Tobj co, int ci)
+{
+    Tobj cvo = NULL, cko = NULL, cto = NULL;
+    Ctype_t ct = (Ctype_t) 0, vt = (Ctype_t) 0;
+    int vi, ni, nn = 0;
+
+    if ((ct = TCgettype(co, ci)) == C_LVAR) {
+       nn = (int) TCgetinteger(co, (ni = TCgetnext(co, TCgetfp(co, ci))));
+       cto = cvo = lvarp[flvari + nn].o;
+       if (!cto)
+           return NULL;
+       vi = TCgetnext(co, ni);
+    } else if (ct == C_GVAR) {
+       cto = root;
+       vi = TCgetfp(co, ci);
+    } else if (ct == C_PVAR)
+       return TCgetobject(co, ci);
+    else
+       return NULL;
+
+    while (vi != C_NULL) {
+       if (Tgettype(cto) != T_TABLE)
+           return NULL;
+       if ((vt = TCgettype(co, vi)) == C_STRING) {
+           if (!(cvo = Tfinds(cto, TCgetstring(co, vi))))
+               return NULL;
+       } else if (vt == C_INTEGER) {
+           if (!(cvo = Tfindi(cto, TCgetinteger(co, vi))))
+               return NULL;
+       } else if (vt == C_REAL) {
+           if (!(cvo = Tfindr(cto, TCgetreal(co, vi))))
+               return NULL;
+       } else {
+           if (!(cko = eeval(co, vi)) || !(cvo = Tfindo(cto, cko)))
+               return NULL;
+       }
+       cto = cvo;
+       vi = TCgetnext(co, vi);
+    }
+    return cvo;
+}
+
+static int getvar(Tobj co, int ci, tnk_t * tnkp)
+{
+    Tobj cvo = NULL, cko = NULL, cto = NULL;
+    Ctype_t ct = (Ctype_t) 0, vt = (Ctype_t) 0;
+    long m;
+    int vi, ovi, nn = 0, ni;
+
+    if ((ct = TCgettype(co, ci)) == C_LVAR) {
+       nn = (int) TCgetinteger(co, (ni = TCgetnext(co, TCgetfp(co, ci))));
+       cvo = cto = lvarp[flvari + nn].o;
+       vi = TCgetnext(co, ni);
+       if (vi != C_NULL && (!cvo || Tgettype(cvo) != T_TABLE))
+           Mresetmark(lvarp[flvari + nn].m,
+                      (lvarp[flvari + nn].o = cvo = cto = Ttable(0)));
+    } else if (ct == C_GVAR) { /* else it's a global variable */
+       cvo = root;
+       vi = TCgetfp(co, ci);
+    } else {
+       return -1;
+    }
+
+    ovi = -1;
+    while (vi != C_NULL) {
+       cto = cvo;
+       if ((vt = TCgettype(co, vi)) == C_STRING) {
+           cvo = Tfinds(cto, TCgetstring(co, vi));
+       } else if (vt == C_INTEGER) {
+           cvo = Tfindi(cto, TCgetinteger(co, vi));
+       } else if (vt == C_REAL) {
+           cvo = Tfindr(cto, TCgetreal(co, vi));
+       } else {
+           if (!(cko = eeval(co, vi)) || !(T_ISSTRING(cko) ||
+                                           T_ISNUMBER(cko)))
+               return -1;
+           cvo = Tfindo(cto, cko);
+       }
+       ovi = vi, vi = TCgetnext(co, vi);
+       if (vi != C_NULL && (!cvo || Tgettype(cvo) != T_TABLE)) {
+           if (vt == C_STRING)
+               Tinss(cto, TCgetstring(co, ovi), (cvo = Ttable(0)));
+           else if (vt == C_INTEGER)
+               Tinsi(cto, TCgetinteger(co, ovi), (cvo = Ttable(0)));
+           else if (vt == C_REAL)
+               Tinsr(cto, TCgetreal(co, ovi), (cvo = Ttable(0)));
+           else
+               m = Mpushmark(cko), Tinso(cto, cko, (cvo = Ttable(0))),
+                   Mpopmark(m);
+       }
+    }
+    if (ct == C_LVAR && ovi == -1) {
+       tnkp->type = TNK_LI;
+       tnkp->u.li = nn;
+    } else {
+       switch (vt) {
+       case C_STRING:
+       case C_INTEGER:
+       case C_REAL:
+           tnkp->type = TNK_S;
+           tnkp->u.tnks.kt = vt;
+           tnkp->u.tnks.to = cto;
+           tnkp->u.tnks.co = co;
+           tnkp->u.tnks.vi = ovi;
+           break;
+       default:
+           tnkp->type = TNK_O;
+           tnkp->u.tnko.to = cto;
+           tnkp->u.tnko.ko = cko;
+           break;
+       }
+    }
+    return 0;
+}
+
+static void setvar(tnk_t tnk, Tobj vo)
+{
+    switch (tnk.type) {
+    case TNK_LI:
+       Mresetmark(lvarp[flvari + tnk.u.li].m,
+                  (lvarp[flvari + tnk.u.li].o = vo));
+       break;
+    case TNK_O:
+       Tinso(tnk.u.tnko.to, tnk.u.tnko.ko, vo);
+       break;
+    default:
+       switch (tnk.u.tnks.kt) {
+       case C_STRING:
+           Tinss(tnk.u.tnks.to,
+                 TCgetstring(tnk.u.tnks.co, tnk.u.tnks.vi), vo);
+           break;
+       case C_INTEGER:
+           Tinsi(tnk.u.tnks.to,
+                 TCgetinteger(tnk.u.tnks.co, tnk.u.tnks.vi), vo);
+           break;
+       case C_REAL:
+           Tinsr(tnk.u.tnks.to, TCgetreal(tnk.u.tnks.co, tnk.u.tnks.vi),
+                 vo);
+           break;
+       default:
+           break;
+       }
+       break;
+    }
+}
+
+static int boolop(Tobj vo)
+{
+    long i;
+    double d;
+
+    if (!vo)
+       return FALSE;
+
+    switch (Tgettype(vo)) {
+    case T_INTEGER:
+       i = Tgetinteger(vo);
+       return (i == 0) ? FALSE : TRUE;
+    case T_REAL:
+       d = Tgetreal(vo);
+       return (d == 0.0) ? FALSE : TRUE;
+    case T_TABLE:
+       if (vo == null)
+           return FALSE;
+       return TRUE;
+    default:
+       return TRUE;
+    }
+}
+
+static int orderop(Tobj v1o, Ctype_t op, Tobj v2o)
+{
+    Ctype_t t1, t2;
+    long i1, i2;
+    int r;
+    double d1, d2;
+
+    if (!v1o || !v2o) {
+       if ((v1o || v2o) && op == C_NE)
+           return TRUE;
+       return FALSE;
+    }
+    t1 = (Ctype_t) Tgettype(v1o), t2 = (Ctype_t) Tgettype(v2o);
+    if (t1 == T_STRING && t2 == T_STRING) {
+       r = Strcmp(Tgetstring(v1o), Tgetstring(v2o));
+    } else if (t1 == T_INTEGER && t2 == T_INTEGER) {
+       i1 = Tgetinteger(v1o), i2 = Tgetinteger(v2o);
+       r = (i1 == i2) ? 0 : ((i1 < i2) ? -1 : 1);
+    } else if (t1 == T_INTEGER && t2 == T_REAL) {
+       i1 = Tgetinteger(v1o), d2 = Tgetreal(v2o);
+       r = (i1 == d2) ? 0 : ((i1 < d2) ? -1 : 1);
+    } else if (t1 == T_REAL && t2 == T_INTEGER) {
+       d1 = Tgetreal(v1o), i2 = Tgetinteger(v2o);
+       r = (d1 == i2) ? 0 : ((d1 < i2) ? -1 : 1);
+    } else if (t1 == T_REAL && t2 == T_REAL) {
+       d1 = Tgetreal(v1o), d2 = Tgetreal(v2o);
+       r = (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1);
+    } else if (t1 == t2) {
+       if (op != C_EQ && op != C_NE)
+           return FALSE;
+       r = (v1o == v2o) ? 0 : 1;
+    } else {
+       return FALSE;
+    }
+    switch (op) {
+    case C_EQ:
+       return (r == 0) ? TRUE : FALSE;
+    case C_NE:
+       return (r != 0) ? TRUE : FALSE;
+    case C_LT:
+       return (r < 0) ? TRUE : FALSE;
+    case C_LE:
+       return (r <= 0) ? TRUE : FALSE;
+    case C_GT:
+       return (r > 0) ? TRUE : FALSE;
+    case C_GE:
+       return (r >= 0) ? TRUE : FALSE;
+    default:
+       break;
+    }
+    panic(POS, "orderop", "bad op code");
+    return FALSE;              /* NOT REACHED */
+}
+
+static Tobj arithop(Num_t * lnum, Ctype_t op, Num_t * rnum)
+{
+    double d1, d2, d3 = 0.0;
+
+    if (!rnum && op != C_UMINUS)
+       return NULL;
+    if (lnum->type == C_INTEGER)
+       d1 = lnum->u.i;
+    else if (lnum->type == C_REAL)
+       d1 = lnum->u.d;
+    else if (!lnum->u.no)
+       return NULL;
+    else if (Tgettype(lnum->u.no) == T_INTEGER)
+       d1 = Tgetinteger(lnum->u.no);
+    else if (Tgettype(lnum->u.no) == T_REAL)
+       d1 = Tgetreal(lnum->u.no);
+    else
+       return NULL;
+    if (op == C_UMINUS) {
+       d3 = -d1;
+       goto result;
+    }
+    if (rnum->type == C_INTEGER)
+       d2 = rnum->u.i;
+    else if (rnum->type == C_REAL)
+       d2 = rnum->u.d;
+    else if (!rnum->u.no)
+       return NULL;
+    else if (Tgettype(rnum->u.no) == T_INTEGER)
+       d2 = Tgetinteger(rnum->u.no);
+    else if (Tgettype(rnum->u.no) == T_REAL)
+       d2 = Tgetreal(rnum->u.no);
+    else
+       return NULL;
+    switch (op) {
+    case C_PLUS:
+       d3 = d1 + d2;
+       break;
+    case C_MINUS:
+       d3 = d1 - d2;
+       break;
+    case C_MUL:
+       d3 = d1 * d2;
+       break;
+    case C_DIV:
+       d3 = d1 / d2;
+       break;
+    case C_MOD:
+       d3 = (long) d1 % (long) d2;
+       break;
+    default:
+       break;
+    }
+  result:
+    if (d3 == (double) (long) d3)
+       return Tinteger((long) d3);
+    return Treal(d3);
+}
+
+static void err(int errnum, int level, Tobj co, int ci)
+{
+    char *s;
+    int si, i;
+
+    if (level > Eerrlevel || !errdo)
+       return;
+    s = "";
+    fprintf(stderr, "runtime error: %s\n", errnam[errnum]);
+    if (!co)
+       return;
+    if (Estackdepth < 1)
+       return;
+    if (!sinfop[(si = sinfoi - 1)].fco && si > 0)
+       si--;
+    if (Eshowbody > 0) {
+       if (co == sinfop[si].fco)
+           s = Scfull(co, 0, ci);
+       else if (co == sinfop[si].co)
+           s = Scfull(co, TCgetfp(co, 0), ci);
+       printbody(s, Eshowbody), free(s);
+       if (Estackdepth == 1) {
+           fprintf(stderr, "\n");
+           errdo = FALSE;
+       }
+       for (i = si; i >= 0; i--) {
+           if (sinfop[i].fco) {
+               s = Scfull(sinfop[i].fco, 0, sinfop[i].fci);
+               printbody(s, Eshowbody), free(s);
+           }
+       }
+       s = Scfull(sinfop[0].co, TCgetfp(sinfop[0].co, 0), sinfop[0].ci);
+       printbody(s, Eshowbody), free(s);
+    }
+    fprintf(stderr, "\n");
+    errdo = FALSE;
+}
+
+static void printbody(char *s, int mode)
+{
+    char *s1, *s2;
+    char c;
+
+    if (mode == 2) {
+       fprintf(stderr, "%s\n", s);
+       return;
+    }
+    c = '\000';
+    for (s1 = s; *s1; s1++)
+       if (*s1 == '>' && *(s1 + 1) && *(s1 + 1) == '>')
+           break;
+    if (!*s1)
+       return;
+    for (; s1 != s; s1--)
+       if (*(s1 - 1) == '\n')
+           break;
+    for (s2 = s1; *s2; s2++)
+       if (*s2 == '\n')
+           break;
+    if (*s2)
+       c = *s2, *s2 = '\000';
+    fprintf(stderr, "%s\n", s1);
+    if (c)
+       *s2 = c;
+}
diff --git a/cmd/lefty/exec.h b/cmd/lefty/exec.h
new file mode 100644 (file)
index 0000000..609200e
--- /dev/null
@@ -0,0 +1,41 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _EXEC_H
+#define _EXEC_H
+    typedef struct Tonm_t lvar_t;
+
+    extern Tobj root, null;
+    extern Tobj rtno;
+    extern int Erun;
+    extern int Eerrlevel, Estackdepth, Eshowbody, Eshowcalls, Eoktorun;
+
+    void Einit(void);
+    void Eterm(void);
+    Tobj Eunit(Tobj);
+    Tobj Efunction(Tobj, char *);
+#endif                         /* _EXEC_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/g.c b/cmd/lefty/g.c
new file mode 100644 (file)
index 0000000..7a93c7a
--- /dev/null
@@ -0,0 +1,993 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "gcommon.h"
+#include "mem.h"
+
+Gwidget_t *Gwidgets;
+int Gwidgetn;
+
+Gbitmap_t *Gbitmaps;
+int Gbitmapn;
+#define BITMAPINCR 10
+#define BITMAPSIZE sizeof (Gbitmap_t)
+
+char *Gdefaultfont;
+int Gneedredraw;
+int Gbuttonsdown;
+int Gerrflag;
+int Gerrno;
+
+char *texts;
+int textn;
+
+static long wsizes[G_WTYPESIZE];
+static Gtextline_t tlarray[1000];
+
+Gwattrmap_t Gwattrmap[] = {
+    {G_ATTRORIGIN, G_ATTRTYPEPOINT, "origin",},
+    {G_ATTRSIZE, G_ATTRTYPESIZE, "size",},
+    {G_ATTRBORDERWIDTH, G_ATTRTYPEINT, "borderwidth",},
+    {G_ATTRNAME, G_ATTRTYPETEXT, "name",},
+    {G_ATTRTEXT, G_ATTRTYPETEXT, "text",},
+    {G_ATTRAPPENDTEXT, G_ATTRTYPETEXT, "appendtext",},
+    {G_ATTRSELECTION, G_ATTRTYPETEXT, "selection",},
+    {G_ATTRCURSOR, G_ATTRTYPETEXT, "cursor",},
+    {G_ATTRMODE, G_ATTRTYPETEXT, "mode",},
+    {G_ATTRLAYOUT, G_ATTRTYPETEXT, "layout",},
+    {G_ATTRZORDER, G_ATTRTYPETEXT, "zorder",},
+    {G_ATTRCOLOR, G_ATTRTYPECOLOR, "color",},
+    {G_ATTRVIEWPORT, G_ATTRTYPESIZE, "viewport",},
+    {G_ATTRWINDOW, G_ATTRTYPERECT, "window",},
+    {G_ATTRWINDOWID, G_ATTRTYPETEXT, "windowid",},
+    {G_ATTRCHILDCENTER, G_ATTRTYPEPOINT, "childcenter",},
+    {G_ATTRNEWLINECB, G_ATTRTYPEFUNC, "newlinecb",},
+    {G_ATTRRESIZECB, G_ATTRTYPEFUNC, "resizecb",},
+    {G_ATTRBUTTONCB, G_ATTRTYPEFUNC, "buttoncb",},
+    {G_ATTREVENTCB, G_ATTRTYPEFUNC, "eventcb",},
+    {G_ATTRUSERDATA, G_ATTRTYPEFUNC, "userdata",},
+    {-1, (Gwattrtype_t) - 1, NULL,},
+};
+
+static int arrayattr[] = {
+    G_ATTRSIZE,
+    G_ATTRBORDERWIDTH,
+    G_ATTRMODE,
+    G_ATTRLAYOUT,
+    G_ATTRCOLOR,
+    G_ATTRWINDOWID,
+    G_ATTRRESIZECB,
+    G_ATTRUSERDATA,
+    -1
+};
+
+static int buttonattr[] = {
+    G_ATTRSIZE,
+    G_ATTRBORDERWIDTH,
+    G_ATTRTEXT,
+    G_ATTRCOLOR,
+    G_ATTRWINDOWID,
+    G_ATTRBUTTONCB,
+    G_ATTRUSERDATA,
+    -1
+};
+
+static int canvasattr[] = {
+    G_ATTRSIZE,
+    G_ATTRBORDERWIDTH,
+#ifdef FEATURE_GMAP
+    G_ATTRMODE,
+#endif
+    G_ATTRCURSOR,
+    G_ATTRCOLOR,
+    G_ATTRVIEWPORT,
+    G_ATTRWINDOW,
+    G_ATTRWINDOWID,
+    G_ATTREVENTCB,
+    G_ATTRUSERDATA,
+    -1
+};
+
+static int labelattr[] = {
+    G_ATTRSIZE,
+    G_ATTRBORDERWIDTH,
+    G_ATTRTEXT,
+    G_ATTRCOLOR,
+    G_ATTRWINDOWID,
+    G_ATTREVENTCB,
+    G_ATTRUSERDATA,
+    -1
+};
+
+static int menuattr[] = {
+    G_ATTRUSERDATA,
+    -1
+};
+
+static int pcanvasattr[] = {
+    G_ATTRORIGIN,
+    G_ATTRSIZE,
+    G_ATTRNAME,
+    G_ATTRMODE,
+    G_ATTRCOLOR,
+    G_ATTRWINDOW,
+    G_ATTRWINDOWID,
+    G_ATTREVENTCB,
+    G_ATTRUSERDATA,
+    -1
+};
+
+static int queryattr[] = {
+    G_ATTRMODE,
+    G_ATTRUSERDATA,
+    -1
+};
+
+static int scrollattr[] = {
+    G_ATTRSIZE,
+    G_ATTRBORDERWIDTH,
+    G_ATTRCHILDCENTER,
+    G_ATTRMODE,
+    G_ATTRCOLOR,
+    G_ATTRWINDOWID,
+    G_ATTRUSERDATA,
+    -1,
+};
+
+static int textattr[] = {
+    G_ATTRSIZE,
+    G_ATTRBORDERWIDTH,
+    G_ATTRTEXT,
+    G_ATTRAPPENDTEXT,
+    G_ATTRSELECTION,
+    G_ATTRMODE,
+    G_ATTRCOLOR,
+    G_ATTRWINDOWID,
+    G_ATTRNEWLINECB,
+    G_ATTRUSERDATA,
+    -1
+};
+
+static int viewattr[] = {
+    G_ATTRORIGIN,
+    G_ATTRSIZE,
+    G_ATTRNAME,
+    G_ATTRCOLOR,
+    G_ATTRZORDER,
+    G_ATTRWINDOWID,
+    G_ATTREVENTCB,
+    G_ATTRUSERDATA,
+    -1
+};
+
+Gwlist_t Gwlist[] = {
+    {G_ARRAYWIDGET, "array", &arrayattr[0],},
+    {G_BUTTONWIDGET, "button", &buttonattr[0],},
+    {G_CANVASWIDGET, "canvas", &canvasattr[0],},
+    {G_LABELWIDGET, "label", &labelattr[0],},
+    {G_MENUWIDGET, "menu", &menuattr[0],},
+    {G_PCANVASWIDGET, "ps", &pcanvasattr[0],},
+    {G_QUERYWIDGET, "query", &queryattr[0],},
+    {G_SCROLLWIDGET, "scroll", &scrollattr[0],},
+    {G_TEXTWIDGET, "text", &textattr[0],},
+    {G_VIEWWIDGET, "view", &viewattr[0],},
+    {-1, NULL, NULL,},
+};
+
+static char *errmsg[] = {
+    /* Gerrno starts at 1      */ "no error",
+    /* G_ERRBADATTRID          */ "bad attribute id %d",
+    /* G_ERRBADATTRVALUE       */ "bad attribute value %s",
+    /* G_ERRBADCOLORINDEX      */ "bad color index %d",
+    /* G_ERRBADPARENTWIDGETID  */ "bad parent widget id %d",
+    /* G_ERRBADWIDGETID        */ "bad widget id %d",
+    /* G_ERRBADWIDGETTYPE      */ "bad widget type %d",
+    /* G_ERRCANNOTCREATEWIDGET */ "cannot create widget",
+    /* G_ERRCANNOTGETATTR      */ "cannot get attribute %s",
+    /* G_ERRCANNOTOPENFILE     */ "cannot open file %s",
+    /* G_ERRCANNOTSETATTR1     */
+       "cannot set attribute %s in createwidget",
+    /* G_ERRCANNOTSETATTR2     */
+       "cannot set attribute %s in setwidgetattr",
+    /* G_ERRINITFAILED         */ "initialization failed",
+    /* G_ERRNOCHILDWIDGET      */ "no child widget",
+    /* G_ERRNOPARENTWIDGET     */ "no parent widget",
+    /* G_ERRNOSUCHCURSOR       */ "no such cursor %s",
+    /* G_ERRNOTACANVAS         */ "widget %d is not a canvas",
+    /* G_ERRNOTIMPLEMENTED     */ "not implemented",
+    /* G_ERRNOTSUPPORTED       */ "not supported"
+       /* G_ERRBADBITMAPID        */ "bad bitmap id %d",
+    /* G_ERRCANNOTCREATEBITMAP */ "cannot create bitmap",
+    /* G_ERRNOBITMAP           */ "no bitmap",
+    /* G_ERRCANNOTREADBITMAP   */ "cannot read bitmap",
+};
+
+static int unpackstring(char *);
+
+int Ginit(void)
+{
+    int wi, bi;
+
+    wsizes[G_ARRAYWIDGET] = AWSIZE;
+    wsizes[G_BUTTONWIDGET] = BWSIZE;
+    wsizes[G_CANVASWIDGET] = CWSIZE;
+    wsizes[G_LABELWIDGET] = LWSIZE;
+    wsizes[G_MENUWIDGET] = MWSIZE;
+    wsizes[G_PCANVASWIDGET] = PWSIZE;
+    wsizes[G_QUERYWIDGET] = QWSIZE;
+    wsizes[G_SCROLLWIDGET] = SWSIZE;
+    wsizes[G_TEXTWIDGET] = TWSIZE;
+    wsizes[G_VIEWWIDGET] = VWSIZE;
+    Gwidgets = Marrayalloc((long) WIDGETINCR * WIDGETSIZE);
+    Gwidgetn = WIDGETINCR;
+    for (wi = 0; wi < Gwidgetn; wi++)
+       Gwidgets[wi].inuse = FALSE;
+    Gbitmapn = BITMAPINCR;
+    Gbitmaps = Marrayalloc((long) Gbitmapn * BITMAPSIZE);
+    for (bi = 0; bi < Gbitmapn; bi++)
+       Gbitmaps[bi].inuse = FALSE;
+    Gneedredraw = FALSE;
+    Gbuttonsdown = 0;
+    Gdefaultfont = "";
+    Gerrflag = FALSE;
+    textn = 100;
+    texts = Marrayalloc((long) textn);
+    texts[0] = '\0';
+    Ginitgraphics();
+    return 0;
+}
+
+int Gterm(void)
+{
+    int wi;
+
+    for (wi = 0; wi < Gwidgetn; wi++)
+       if (Gwidgets[wi].inuse)
+           Gdestroywidget(wi);
+    Gtermgraphics();
+    Marrayfree(texts), texts = NULL, textn = 0;
+    Marrayfree(Gwidgets), Gwidgets = NULL, Gwidgetn = 0;
+    Marrayfree(Gbitmaps), Gbitmaps = NULL, Gbitmapn = 0;
+    return 0;
+}
+
+int Gcreatewidget(int pwi, int type, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *parent, *widget;
+    int rtn;
+
+    if ((pwi != -1) && (pwi < 0 || pwi > Gwidgetn || !Gwidgets[pwi].inuse)) {
+       Gerr(POS, G_ERRBADPARENTWIDGETID, pwi);
+       return -1;
+    }
+    if (type < 0 || type >= G_WTYPESIZE) {
+       Gerr(POS, G_ERRBADWIDGETTYPE, type);
+       return -1;
+    }
+    widget = newwidget(type);
+    widget->inuse = TRUE;
+    widget->pwi = pwi;
+    parent = (pwi == -1) ? NULL : &Gwidgets[pwi];
+    rtn = -1;
+    switch (type) {
+    case G_ARRAYWIDGET:
+       rtn = GAcreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_BUTTONWIDGET:
+       rtn = GBcreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_CANVASWIDGET:
+       rtn = GCcreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_LABELWIDGET:
+       rtn = GLcreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_MENUWIDGET:
+       rtn = GMcreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_PCANVASWIDGET:
+       rtn = GPcreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_QUERYWIDGET:
+       rtn = GQcreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_SCROLLWIDGET:
+       rtn = GScreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_TEXTWIDGET:
+       rtn = GTcreatewidget(parent, widget, attrn, attrp);
+       break;
+    case G_VIEWWIDGET:
+       rtn = GVcreatewidget(parent, widget, attrn, attrp);
+       break;
+    }
+    if (rtn == -1) {
+       widget->inuse = FALSE;
+       return -1;
+    }
+    return widget - &Gwidgets[0];
+}
+
+int Gsetwidgetattr(int wi, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_ARRAYWIDGET:
+       return GAsetwidgetattr(widget, attrn, attrp);
+    case G_BUTTONWIDGET:
+       return GBsetwidgetattr(widget, attrn, attrp);
+    case G_CANVASWIDGET:
+       return GCsetwidgetattr(widget, attrn, attrp);
+    case G_LABELWIDGET:
+       return GLsetwidgetattr(widget, attrn, attrp);
+    case G_MENUWIDGET:
+       return GMsetwidgetattr(widget, attrn, attrp);
+    case G_PCANVASWIDGET:
+       return GPsetwidgetattr(widget, attrn, attrp);
+    case G_QUERYWIDGET:
+       return GQsetwidgetattr(widget, attrn, attrp);
+    case G_SCROLLWIDGET:
+       return GSsetwidgetattr(widget, attrn, attrp);
+    case G_TEXTWIDGET:
+       return GTsetwidgetattr(widget, attrn, attrp);
+    case G_VIEWWIDGET:
+       return GVsetwidgetattr(widget, attrn, attrp);
+    }
+    return -1;
+}
+
+int Ggetwidgetattr(int wi, int attrn, Gwattr_t * attrp)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_ARRAYWIDGET:
+       return GAgetwidgetattr(widget, attrn, attrp);
+    case G_BUTTONWIDGET:
+       return GBgetwidgetattr(widget, attrn, attrp);
+    case G_CANVASWIDGET:
+       return GCgetwidgetattr(widget, attrn, attrp);
+    case G_LABELWIDGET:
+       return GLgetwidgetattr(widget, attrn, attrp);
+    case G_MENUWIDGET:
+       return GMgetwidgetattr(widget, attrn, attrp);
+    case G_PCANVASWIDGET:
+       return GPgetwidgetattr(widget, attrn, attrp);
+    case G_QUERYWIDGET:
+       return GQgetwidgetattr(widget, attrn, attrp);
+    case G_SCROLLWIDGET:
+       return GSgetwidgetattr(widget, attrn, attrp);
+    case G_TEXTWIDGET:
+       return GTgetwidgetattr(widget, attrn, attrp);
+    case G_VIEWWIDGET:
+       return GVgetwidgetattr(widget, attrn, attrp);
+    }
+    return -1;
+}
+
+int Gdestroywidget(int wi)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_ARRAYWIDGET:
+       GAdestroywidget(widget);
+       break;
+    case G_BUTTONWIDGET:
+       GBdestroywidget(widget);
+       break;
+    case G_CANVASWIDGET:
+       GCdestroywidget(widget);
+       break;
+    case G_LABELWIDGET:
+       GLdestroywidget(widget);
+       break;
+    case G_MENUWIDGET:
+       GMdestroywidget(widget);
+       break;
+    case G_PCANVASWIDGET:
+       GPdestroywidget(widget);
+       break;
+    case G_QUERYWIDGET:
+       GQdestroywidget(widget);
+       break;
+    case G_SCROLLWIDGET:
+       GSdestroywidget(widget);
+       break;
+    case G_TEXTWIDGET:
+       GTdestroywidget(widget);
+       break;
+    case G_VIEWWIDGET:
+       GVdestroywidget(widget);
+       break;
+    }
+    /* HACK: should do switch on type ... */
+    free(widget->u.c);
+    widget->inuse = FALSE;
+    return 0;
+}
+
+int Gqueryask(int wi, char *prompt, char *args,
+             char *responsep, int responsen)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    return GQqueryask(widget, prompt, args, responsep, responsen);
+}
+
+int Gmenuaddentries(int wi, int en, char **ep)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    return GMmenuaddentries(widget, en, ep);
+}
+
+int Gmenudisplay(int pwi, int wi)
+{
+    Gwidget_t *parent, *widget;
+
+    if (pwi < 0 || pwi > Gwidgetn || !Gwidgets[pwi].inuse) {
+       Gerr(POS, G_ERRBADPARENTWIDGETID, wi);
+       return -1;
+    }
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    parent = &Gwidgets[pwi];
+    widget = &Gwidgets[wi];
+    return GMmenudisplay(parent, widget);
+}
+
+/* functions for drawing on canvas and pcanvas widgets */
+
+int Gcanvasclear(int wi)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCcanvasclear(widget);
+    case G_PCANVASWIDGET:
+       return GPcanvasclear(widget);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Gsetgfxattr(int wi, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCsetgfxattr(widget, ap);
+    case G_PCANVASWIDGET:
+       return GPsetgfxattr(widget, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Ggetgfxattr(int wi, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCgetgfxattr(widget, ap);
+    case G_PCANVASWIDGET:
+       return GPgetgfxattr(widget, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Garrow(int wi, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCarrow(widget, gp1, gp2, ap);
+    case G_PCANVASWIDGET:
+       return GParrow(widget, gp1, gp2, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Gline(int wi, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCline(widget, gp1, gp2, ap);
+    case G_PCANVASWIDGET:
+       return GPline(widget, gp1, gp2, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Gbox(int wi, Grect_t gr, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCbox(widget, gr, ap);
+    case G_PCANVASWIDGET:
+       return GPbox(widget, gr, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Gpolygon(int wi, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCpolygon(widget, gpn, gpp, ap);
+    case G_PCANVASWIDGET:
+       return GPpolygon(widget, gpn, gpp, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Gsplinegon(int wi, int gpn, Gpoint_t * gpp, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCsplinegon(widget, gpn, gpp, ap);
+    case G_PCANVASWIDGET:
+       return GPsplinegon(widget, gpn, gpp, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Garc(int wi, Gpoint_t gc, Gsize_t gs, double ang1, double ang2,
+        Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCarc(widget, gc, gs, ang1, ang2, ap);
+    case G_PCANVASWIDGET:
+       return GParc(widget, gc, gs, ang1, ang2, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Gtext(int wi, char *string, Gpoint_t go, char *fn, double fs,
+         char *justs, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+    char js[2];
+    int n;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    if (string[0] == '\000')
+       return 0;
+    n = unpackstring(string);
+    if (!justs[0] || !justs[1])
+       js[0] = 'c', js[1] = 'c';
+    else {
+       js[0] = justs[0], js[1] = justs[1];
+       if (js[0] != 'l' && js[0] != 'c' && js[0] != 'r')
+           js[0] = 'c';
+       if (js[1] != 'd' && js[1] != 'c' && js[1] != 'u')
+           js[1] = 'c';
+    }
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCtext(widget, &tlarray[0], n, go, fn, fs, &js[0], ap);
+    case G_PCANVASWIDGET:
+       return GPtext(widget, &tlarray[0], n, go, fn, fs, &js[0], ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Ggettextsize(int wi, char *string, char *fn, double fs, Gsize_t * gsp)
+{
+    Gwidget_t *widget;
+    int n;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    if (widget->type != G_CANVASWIDGET) {
+       Gerr(POS, G_ERRNOTACANVAS, wi);
+       return -1;
+    }
+    if (string[0] == '\000') {
+       gsp->x = gsp->y = 0.0;
+       return 0;
+    }
+    n = unpackstring(string);
+    return GCgettextsize(widget, &tlarray[0], n, fn, fs, gsp);
+}
+
+int Gcreatebitmap(int wi, Gsize_t s)
+{
+    Gwidget_t *widget;
+    Gbitmap_t *bitmap;
+    int rtn;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADPARENTWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    if (widget->type != G_CANVASWIDGET && widget->type != G_PCANVASWIDGET) {
+       Gerr(POS, G_ERRNOTACANVAS, wi);
+       return -1;
+    }
+    bitmap = newbitmap();
+    bitmap->inuse = TRUE;
+    bitmap->canvas = wi;
+    rtn = -1;
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       rtn = GCcreatebitmap(widget, bitmap, s);
+       break;
+    case G_PCANVASWIDGET:
+       rtn = GPcreatebitmap(widget, bitmap, s);
+       break;
+    }
+    if (rtn == -1) {
+       bitmap->inuse = FALSE;
+       return -1;
+    }
+    return bitmap - &Gbitmaps[0];
+}
+
+int Gdestroybitmap(int bi)
+{
+    Gbitmap_t *bitmap;
+
+    if (bi < 0 || bi > Gbitmapn || !Gbitmaps[bi].inuse) {
+       Gerr(POS, G_ERRBADBITMAPID, bi);
+       return -1;
+    }
+    bitmap = &Gbitmaps[bi];
+    switch (bitmap->ctype) {
+    case G_CANVASWIDGET:
+       GCdestroybitmap(bitmap);
+       break;
+    case G_PCANVASWIDGET:
+       GPdestroybitmap(bitmap);
+       break;
+    }
+    bitmap->inuse = FALSE;
+    return 0;
+}
+
+int Greadbitmap(int wi, FILE * fp)
+{
+    Gwidget_t *widget;
+    Gbitmap_t *bitmap;
+    int rtn;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADPARENTWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    if (widget->type != G_CANVASWIDGET && widget->type != G_PCANVASWIDGET) {
+       Gerr(POS, G_ERRNOTACANVAS, wi);
+       return -1;
+    }
+    bitmap = newbitmap();
+    bitmap->inuse = TRUE;
+    bitmap->canvas = wi;
+    rtn = -1;
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       rtn = GCreadbitmap(widget, bitmap, fp);
+       break;
+    case G_PCANVASWIDGET:
+       rtn = GPreadbitmap(widget, bitmap, fp);
+       break;
+    }
+    if (rtn == -1) {
+       bitmap->inuse = FALSE;
+       return -1;
+    }
+    return bitmap - &Gbitmaps[0];
+}
+
+int Gwritebitmap(FILE * fp, int bi)
+{
+    Gbitmap_t *bitmap;
+    int rtn;
+
+    if (bi < 0 || bi > Gbitmapn || !Gbitmaps[bi].inuse) {
+       Gerr(POS, G_ERRBADBITMAPID, bi);
+       return -1;
+    }
+    bitmap = &Gbitmaps[bi];
+    rtn = -1;
+    switch (bitmap->ctype) {
+    case G_CANVASWIDGET:
+       rtn = GCwritebitmap(bitmap, fp);
+       break;
+    case G_PCANVASWIDGET:
+       rtn = GPwritebitmap(bitmap, fp);
+       break;
+    }
+    if (rtn == -1)
+       return -1;
+    return 0;
+}
+
+int Gbitblt(int wi, Gpoint_t gp, Grect_t gr, int bi,
+           char *mode, Ggattr_t * ap)
+{
+    Gwidget_t *widget;
+    Gbitmap_t *bitmap;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    if (bi < 0 || bi > Gbitmapn || !Gbitmaps[bi].inuse) {
+       Gerr(POS, G_ERRBADBITMAPID, bi);
+       return -1;
+    }
+    bitmap = &Gbitmaps[bi];
+    switch (widget->type) {
+    case G_CANVASWIDGET:
+       return GCbitblt(widget, gp, gr, bitmap, mode, ap);
+    case G_PCANVASWIDGET:
+       return GPbitblt(widget, gp, gr, bitmap, mode, ap);
+    }
+    Gerr(POS, G_ERRNOTACANVAS, wi);
+    return -1;
+}
+
+int Ggetmousecoords(int wi, Gpoint_t * gsp, int *count)
+{
+    Gwidget_t *widget;
+
+    if (wi < 0 || wi > Gwidgetn || !Gwidgets[wi].inuse) {
+       Gerr(POS, G_ERRBADWIDGETID, wi);
+       return -1;
+    }
+    widget = &Gwidgets[wi];
+    if (widget->type != G_CANVASWIDGET) {
+       Gerr(POS, G_ERRNOTACANVAS, wi);
+       return -1;
+    }
+    return GCgetmousecoords(widget, gsp, count);
+}
+
+Gwidget_t *newwidget(int type)
+{
+    Gwidget_t *new;
+    int wi;
+
+    for (wi = 0; wi < Gwidgetn; wi++)
+       if (!Gwidgets[wi].inuse)
+           goto found;
+
+    Gwidgets = Marraygrow(Gwidgets,
+                         (long) (Gwidgetn + WIDGETINCR) * WIDGETSIZE);
+    for (wi = Gwidgetn; wi < Gwidgetn + WIDGETINCR; wi++)
+       Gwidgets[wi].inuse = FALSE;
+    wi = Gwidgetn, Gwidgetn += WIDGETINCR;
+
+  found:
+    new = &Gwidgets[wi];
+    new->type = type;
+    new->w = 0;
+    new->udata = 0;
+    /* HACK: should do a switch on type, but ... */
+    if (!(new->u.c = (Gcw_t *) malloc(wsizes[type])))
+       panic(POS, "newwidget", "cannot allocate data");
+    return new;
+}
+
+Gwidget_t *findwidget(unsigned long w, int type)
+{
+    int wi;
+
+    if (type == G_WTYPESIZE) {
+       for (wi = 0; wi < Gwidgetn; wi++)
+           if (Gwidgets[wi].inuse && (unsigned long) Gwidgets[wi].w == w)
+               return &Gwidgets[wi];
+    } else {
+       for (wi = 0; wi < Gwidgetn; wi++)
+           if (Gwidgets[wi].inuse && Gwidgets[wi].type == type &&
+               (unsigned long) Gwidgets[wi].w == w)
+               return &Gwidgets[wi];
+    }
+    return NULL;
+}
+
+Gbitmap_t *newbitmap(void)
+{
+    Gbitmap_t *new;
+    int bi;
+
+    for (bi = 0; bi < Gbitmapn; bi++)
+       if (!Gbitmaps[bi].inuse)
+           goto found;
+
+    Gbitmaps = Marraygrow(Gbitmaps,
+                         (long) (Gbitmapn + BITMAPINCR) * BITMAPSIZE);
+    for (bi = Gbitmapn; bi < Gbitmapn + BITMAPINCR; bi++)
+       Gbitmaps[bi].inuse = FALSE;
+    bi = Gbitmapn, Gbitmapn += BITMAPINCR;
+
+  found:
+    new = &Gbitmaps[bi];
+    return new;
+}
+
+void Gerr(char *file, int line, int errnum, ...)
+{
+    va_list args;
+
+#ifdef FEATURE_WIN32
+    char buf[256];
+
+    Gerrno = errnum;
+    if (!warnflag)
+       return;
+
+    va_start(args, errnum);
+    vsprintf(buf, errmsg[errnum], args);
+    Gnocallbacks = TRUE;
+    MessageBox((HWND) NULL, buf, "Lefty Warning", MB_APPLMODAL);
+    Gnocallbacks = FALSE;
+    va_end(args);
+#else
+    Gerrno = errnum;
+    if (!Gerrflag)
+       return;
+
+    va_start(args, errnum);
+    fprintf(stderr, "warning: (file %s, line %d) ", file, line);
+    vfprintf(stderr, errmsg[errnum], args);
+    fprintf(stderr, "\n");
+    va_end(args);
+#endif
+}
+
+static int unpackstring(char *s)
+{
+    char *p1, *p2;
+    int n;
+
+    if ((n = strlen(s) + 1) > textn)
+       textn = n, texts = Marraygrow(texts, (long) textn);
+    strcpy(texts, s);
+    n = 0;
+    p1 = p2 = texts;
+    tlarray[0].p = p1;
+    while (*p1) {
+       if (p1[0] == '\\') {
+           if (p1[1] == 'n' || p1[1] == 'l' || p1[1] == 'r') {
+               tlarray[n].n = p1 - tlarray[n].p;
+               tlarray[n].j = p1[1];
+               tlarray[++n].p = (p1 += 2);
+           } else {
+               for (p2 = p1 + 1; *p2; p2++)
+                   p2[-1] = p2[0];
+               p2[-1] = 0;
+               p1++;
+           }
+       } else
+           p1++;
+    }
+    if ((tlarray[n].n = p1 - tlarray[n].p) > 0)
+       tlarray[n++].j = 'n';
+    return n;
+}
diff --git a/cmd/lefty/g.h b/cmd/lefty/g.h
new file mode 100644 (file)
index 0000000..e7981a1
--- /dev/null
@@ -0,0 +1,609 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _G_H
+#define _G_H
+#ifdef FEATURE_X11
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xmu/CurUtil.h>
+#ifdef FEATURE_GMAP
+#ifdef FEATURE_MESAGL
+#include <GL/GLwDrawA.h>
+#else
+#include <X11/GLw/GLwDrawA.h>
+#endif
+#include <Performer/pf.h>
+#endif
+#endif
+
+#ifdef FEATURE_GTK
+#include <gtk/gtk.h>
+#ifdef FEATURE_GMAP
+#ifdef FEATURE_MESAGL
+#include <GL/GLwDrawA.h>
+#endif
+#include <Performer/pf.h>
+#endif
+#endif
+
+
+/* general coordinate structures */
+
+    typedef struct Gxy_t {
+       double x, y;
+    } Gxy_t;
+    typedef Gxy_t Gpoint_t;
+    typedef Gxy_t Gsize_t;
+    typedef struct Grect_t {
+       Gxy_t o, c;
+    } Grect_t;
+
+/* textline structure */
+    typedef struct Gtextline_t {
+       char *p;
+       int n, j;
+       int w, h;
+    } Gtextline_t;
+
+/* Color structure */
+
+    typedef struct Gcolor_t {
+       int index;
+       int r, g, b;
+    } Gcolor_t;
+#define G_MAXCOLORS 256
+
+/* event structures */
+
+/* event consumption modes */
+    typedef enum {
+       G_ONEEVENT, G_MANYEVENTS
+    } Geventmode_t;
+
+/* event types and main structure */
+    typedef enum {
+       G_MOUSE, G_KEYBD
+    } Getype_t;
+#define G_DOWN 0
+#define G_UP   1
+#define G_MOVE 2
+#define G_LEFT   0
+#define G_MIDDLE 1
+#define G_RIGHT  2
+    typedef struct Gevent_t {
+       Getype_t type;
+       int wi;
+       int code;
+       int data;
+       Gpoint_t p;
+    } Gevent_t;
+
+/* Widgets */
+
+/* minimum widget sizes */
+#define MINVWSIZE 100
+#define MINTWSIZE 40
+#define MINBWSIZE 40
+#define MINLWSIZE 25
+#define MINAWSIZE 25
+#define MINSWSIZE 40
+#define MINCWSIZE 100
+#define MINPWSIZE 100
+
+/* gfx attributes for the [p]canvas widget */
+
+/* drawing styles */
+#define G_SOLID       0
+#define G_DASHED      1
+#define G_DOTTED      2
+#define G_LONGDASHED  3
+#define G_SHORTDASHED 4
+
+/* drawing modes */
+#if defined(FEATURE_X11) || defined(FEATURE_GTK)
+#ifdef FEATURE_GTK
+#define G_SRC GDK_COPY
+#define G_XOR GDK_XOR
+#else
+#define G_SRC GXcopy
+#define G_XOR GXxor
+#endif
+#else
+#ifdef FEATURE_WIN32
+#define G_SRC R2_COPYPEN
+#define G_XOR R2_NOT
+#else
+#define G_SRC 0
+#define G_XOR 1
+#endif
+#endif
+
+/* gfx attributes and the attribute structure */
+    typedef enum {
+       G_GATTRCOLOR = 1, G_GATTRWIDTH = 2, G_GATTRMODE = 4,
+       G_GATTRFILL = 8, G_GATTRSTYLE = 16
+    } Ggattrflags_t;
+    typedef struct Ggattr_t {
+       Ggattrflags_t flags;
+       int color;
+       int width;
+       int mode;
+       int fill;
+       int style;
+    } Ggattr_t;
+
+/* widget attributes structures */
+
+    typedef enum {
+       G_ATTRTYPEINT, G_ATTRTYPELONG,
+       G_ATTRTYPEFLOAT, G_ATTRTYPEDOUBLE,
+       G_ATTRTYPETEXT,
+       G_ATTRTYPEPOINT, G_ATTRTYPESIZE, G_ATTRTYPERECT,
+       G_ATTRTYPECOLOR,
+       G_ATTRTYPEFUNC,
+       G_ATTRTYPEULONG
+    } Gwattrtype_t;
+    typedef struct Gwattrmap_t {
+       int id;
+       Gwattrtype_t type;
+       char *name;
+    } Gwattrmap_t;
+    typedef struct Gwattr_t {
+       int id;
+       union {
+           int i;
+           long l;
+           float f;
+           double d;
+           char *t;
+           Gpoint_t p;
+           Gsize_t s;
+           Grect_t r;
+           Gcolor_t c;
+           void *func;
+           unsigned long u;
+       } u;
+    } Gwattr_t;
+    typedef struct Gwlist_t {
+       int wid;
+       char *wname;
+       int *attrid;
+    } Gwlist_t;
+
+/* attribute ids */
+#define G_ATTRORIGIN       0
+#define G_ATTRSIZE         1
+#define G_ATTRBORDERWIDTH  2
+#define G_ATTRNAME         3
+#define G_ATTRTEXT         4
+#define G_ATTRAPPENDTEXT   5
+#define G_ATTRSELECTION    6
+#define G_ATTRCURSOR       7
+#define G_ATTRMODE         8
+#define G_ATTRLAYOUT       9
+#define G_ATTRZORDER      10
+#define G_ATTRCOLOR       11
+#define G_ATTRVIEWPORT    12
+#define G_ATTRWINDOW      13
+#define G_ATTRWINDOWID    14
+#define G_ATTRCHILDCENTER 15
+#define G_ATTRNEWLINECB   16
+#define G_ATTRRESIZECB    17
+#define G_ATTRBUTTONCB    18
+#define G_ATTREVENTCB     19
+#define G_ATTRUSERDATA    20
+
+/* array widget structs */
+    typedef struct Gawcarray_t {
+#if defined(FEATURE_X11) || defined(FEATURE_GTK)
+#ifdef FEATURE_GTK
+       GtkWidget *w;
+#else
+       Widget w;
+#endif
+#else
+#ifdef FEATURE_WIN32
+       HWND w;
+#else
+       int w;
+#endif
+#endif
+       int flag;
+       int ox, oy, sx, sy, bs;
+    } Gawcarray_t;
+#define AWCARRAYINCR 10
+#define AWCARRAYSIZE sizeof (Gawcarray_t)
+
+#define G_AWHARRAY 1
+#define G_AWVARRAY 2
+    typedef struct Gawdata_t {
+       int type;
+       int sx, sy;
+       Gawcarray_t *carray;
+       int cj, cn;
+#ifdef FEATURE_WIN32
+       int batchmode, working;
+#endif
+    } Gawdata_t;
+
+/* query widget macros */
+#define G_QWSTRING 1           /* string query */
+#define G_QWFILE   2           /* file query */
+#define G_QWCHOICE 3           /* choose 1 query */
+#define G_QWMODES 3            /* total number of modes */
+
+/* widget callbacks */
+    typedef void (*Gtwnewlinecb) (int, char *);
+    typedef void (*Gbuttoncb) (int, void *);
+    typedef void (*Glabelcb) (Gevent_t *);
+    typedef void (*Gcanvascb) (Gevent_t *);
+    typedef void (*Gawordercb) (void *, Gawdata_t *);
+    typedef void (*Gawcoordscb) (int, Gawdata_t *);
+    typedef void (*Gviewcb) (Gevent_t *);
+
+#define G_ARRAYWIDGET    0
+#define G_BUTTONWIDGET   1
+#define G_CANVASWIDGET   2
+#define G_LABELWIDGET    3
+#define G_MENUWIDGET     4
+#define G_PCANVASWIDGET  5
+#define G_QUERYWIDGET    6
+#define G_SCROLLWIDGET   7
+#define G_TEXTWIDGET     8
+#define G_VIEWWIDGET     9
+#define G_WTYPESIZE     10
+
+/* predefined widgets */
+
+/* --- array --- */
+    typedef struct Gaw_t {
+       Gawcoordscb func;
+       int mode;
+#ifdef FEATURE_WIN32
+       Gawdata_t data;
+#endif
+    } Gaw_t;
+#define AWSIZE sizeof (Gaw_t)
+
+/* --- button --- */
+    typedef struct Gbw_t {
+       Gbuttoncb func;
+    } Gbw_t;
+#define BWSIZE sizeof (Gbw_t)
+
+/* --- canvas --- */
+    typedef struct Gcw_t {
+       int needredraw;
+       int buttonsdown;
+       char bstate[3];
+       struct Gcwcolor_t {
+           int inuse;
+#if defined(FEATURE_X11) || defined(FEATURE_GTK)
+#ifdef FEATURE_GTK
+           GdkColor color;
+#else
+           XColor color;
+#endif
+#else
+#ifdef FEATURE_WIN32
+           PALETTEENTRY color;
+#endif
+#endif
+       } colors[G_MAXCOLORS];
+       char allocedcolor[2];
+       Ggattr_t gattr, defgattr;
+       Grect_t wrect;
+       Gsize_t vsize;
+       Grect_t clip;
+       Gcanvascb func;
+#if defined(FEATURE_X11) || defined(FEATURE_GTK)
+#ifdef FEATURE_GTK
+       GtkWindow window;
+       GdkColormap *cmap;
+       GdkGC *gc;
+       GdkPixmap grays[17];
+       GdkFont *font;
+#else
+       Window window;
+       Colormap cmap;
+       GC gc;
+       Pixmap grays[17];
+       XFontStruct *font;
+#endif
+#else
+#ifdef FEATURE_WIN32
+       HPALETTE cmap;
+       HDC gc;
+       HBRUSH grays[17];
+       HFONT font;
+       int ncolor;
+#endif
+#endif
+#ifdef FEATURE_GMAP
+       int gmapmode;
+#endif
+    } Gcw_t;
+#define CWSIZE sizeof (Gcw_t)
+
+/* --- label --- */
+    typedef struct Glw_t {
+       Glabelcb func;
+    } Glw_t;
+#define LWSIZE sizeof (Glw_t)
+
+/* --- menu --- */
+    typedef struct Gmw_t {
+#ifdef FEATURE_GTK
+       glong count;
+#else
+       long count;
+#endif
+    } Gmw_t;
+#define MWSIZE sizeof (Gmw_t)
+
+/* --- postscript --- */
+    typedef struct Gpw_t {
+#if defined(FEATURE_X11) || defined(FEATURE_GTK)
+       FILE *fp;
+       struct Gpwcolor_t {
+           int inuse;
+           int r, g, b;
+           double nr, ng, nb;
+       } colors[G_MAXCOLORS];
+       Ggattr_t gattr, defgattr;
+       Grect_t wrect;
+       Gsize_t vsize;
+#else
+#ifdef FEATURE_WIN32
+       struct Gpwcolor_t {
+           int inuse;
+           PALETTEENTRY color;
+       } colors[G_MAXCOLORS];
+       Ggattr_t gattr, defgattr;
+       Grect_t wrect;
+       Gsize_t vsize;
+       HPALETTE cmap;
+       HDC gc;
+       HBRUSH grays[17];
+       HFONT font;
+       int ncolor, mode;
+#else
+       int dummy;
+#endif
+#endif
+    } Gpw_t;
+#define PWSIZE sizeof (Gpw_t)
+
+/* --- query --- */
+    typedef struct Gqw_t {
+#if defined(FEATURE_X11) || defined(FEATURE_GTK)
+#ifdef FEATURE_GTK
+       GtkWidget *w;
+#else
+       Widget w;
+#endif
+#else
+#ifdef FEATURE_WIN32
+       HWND w;
+#endif
+#endif
+       int mode;
+       int state, button;
+    } Gqw_t;
+#define QWSIZE sizeof (Gqw_t)
+
+/* --- scroll --- */
+    typedef struct Gsw_t {
+       int dummy;
+    } Gsw_t;
+#define SWSIZE sizeof (Gsw_t)
+
+/* --- text --- */
+    typedef struct Gtw_t {
+       Gtwnewlinecb func;
+    } Gtw_t;
+#define TWSIZE sizeof (Gtw_t)
+
+/* --- view --- */
+    typedef struct Gvw_t {
+       Gviewcb func;
+       int closing;
+    } Gvw_t;
+#define VWSIZE sizeof (Gvw_t)
+
+/* the main widget structure */
+    typedef struct Gwidget_t {
+       int type;
+       int inuse;
+       int pwi;
+#if defined(FEATURE_X11) || defined(FEATURE_GTK)
+#ifdef FEATURE_GTK
+       GtkWidget *w;
+#else
+       Widget w;
+#endif
+#else
+#ifdef FEATURE_WIN32
+       HWND w;
+#else
+       int w;
+#endif
+#endif
+       union {
+           Gaw_t *a;
+           Gbw_t *b;
+           Gcw_t *c;
+           Glw_t *l;
+           Gmw_t *m;
+           Gpw_t *p;
+           Gqw_t *q;
+           Gsw_t *s;
+           Gtw_t *t;
+           Gvw_t *v;
+       } u;
+       unsigned long udata;
+    } Gwidget_t;
+#define WIDGETINCR 20
+#define WIDGETSIZE sizeof (Gwidget_t)
+
+/* bitmap data structure */
+    typedef struct Gbitmap_t {
+       int inuse;
+       int canvas;
+       int ctype;              /* type of canvas, eg. G_CANVASWIDGET */
+       Gsize_t size;
+       Gsize_t scale;
+       union {
+           struct {
+#if defined(FEATURE_X11) || defined(FEATURE_GTK)
+#ifdef FEATURE_GTK
+               GdkPixmap orig, scaled;
+#else
+               Pixmap orig, scaled;
+#endif
+#else
+#ifdef FEATURE_WIN32
+               HBITMAP orig, scaled;
+#else
+               int dummy;
+#endif
+#endif
+           } bmap;
+           unsigned char *bits;
+       } u;
+    } Gbitmap_t;
+
+    extern Gbitmap_t *Gbitmaps;
+    extern int Gbitmapn;
+
+/* global array of widgets */
+    extern Gwidget_t *Gwidgets;
+    extern int Gwidgetn;
+
+    extern Gwlist_t Gwlist[];
+    extern Gwattrmap_t Gwattrmap[];
+
+    extern char *Gdefaultfont;
+    extern int Gneedredraw;
+    extern int Gbuttonsdown;
+    extern int Gerrflag;
+    extern char *Gpscanvasname;
+
+    extern int Gxfd;
+
+#ifdef FEATURE_GTK
+    static int Gnocallbacks;
+#else
+#ifdef FEATURE_WIN32
+    extern int Gnocallbacks;
+#endif
+#endif
+
+/* functions returning an int
+   return -1 if there's an error and
+   also set the Gerrno variable
+
+   the rendering functions may return +1
+   if the graphical object is completely hidden
+*/
+    int Ginit(void);
+    int Gterm(void);
+    int Gcreatewidget(int, int, int, Gwattr_t *);
+    int Gsetwidgetattr(int, int, Gwattr_t *);
+    int Ggetwidgetattr(int, int, Gwattr_t *);
+    int Gdestroywidget(int);
+    int Gqueryask(int, char *, char *, char *, int);
+    int Gmenuaddentries(int, int, char **);
+    int Gmenudisplay(int, int);
+    int Gsync(void);
+    int Gresetbstate(int);
+    int Gcanvasclear(int);
+    int Gsetgfxattr(int, Ggattr_t *);
+    int Ggetgfxattr(int, Ggattr_t *);
+    int Garrow(int, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int Gline(int, Gpoint_t, Gpoint_t, Ggattr_t *);
+    int Gbox(int, Grect_t, Ggattr_t *);
+    int Gpolygon(int, int, Gpoint_t *, Ggattr_t *);
+    int Gsplinegon(int, int, Gpoint_t *, Ggattr_t *);
+    int Garc(int, Gpoint_t, Gsize_t, double, double, Ggattr_t *);
+    int Gtext(int, char *, Gpoint_t, char *, double, char *, Ggattr_t *);
+    int Ggettextsize(int, char *, char *, double, Gsize_t *);
+    int Gcreatebitmap(int, Gsize_t);
+    int Gdestroybitmap(int);
+    int Greadbitmap(int, FILE *);
+    int Gwritebitmap(FILE *, int);
+    int Gbitblt(int, Gpoint_t, Grect_t, int, char *, Ggattr_t *);
+    int Ggetmousecoords(int, Gpoint_t *, int *);
+
+    int Gprocessevents(int, Geventmode_t);
+
+    int Gaworder(Gwidget_t *, void *, Gawordercb);
+    int Gawsetmode(Gwidget_t *, int);
+    int Gawgetmode(Gwidget_t *);
+    void Gawdefcoordscb(int, Gawdata_t *);
+
+    Gwidget_t *newwidget(int);
+    Gwidget_t *findwidget(unsigned long, int);
+    Gbitmap_t *newbitmap(void);
+    void Gerr(char *, int, int, ...);
+
+/* error messages */
+#define G_ERRBADATTRID           1
+#define G_ERRBADATTRVALUE        2
+#define G_ERRBADCOLORINDEX       3
+#define G_ERRBADPARENTWIDGETID   4
+#define G_ERRBADWIDGETID         5
+#define G_ERRBADWIDGETTYPE       6
+#define G_ERRCANNOTCREATEWIDGET  7
+#define G_ERRCANNOTGETATTR       8
+#define G_ERRCANNOTOPENFILE      9
+#define G_ERRCANNOTSETATTR1     10
+#define G_ERRCANNOTSETATTR2     11
+#define G_ERRINITFAILED         12
+#define G_ERRNOCHILDWIDGET      13
+#define G_ERRNOPARENTWIDGET     14
+#define G_ERRNOSUCHCURSOR       15
+#define G_ERRNOTACANVAS         16
+#define G_ERRNOTIMPLEMENTED     17
+#define G_ERRNOTSUPPORTED       18
+#define G_ERRBADBITMAPID        19
+#define G_ERRCANNOTCREATEBITMAP 20
+#define G_ERRNOBITMAP           21
+#define G_ERRCANNOTREADBITMAP   22
+
+    extern int Gerrno;
+#endif                         /* _G_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/gfxview.c b/cmd/lefty/gfxview.c
new file mode 100644 (file)
index 0000000..6a16b4c
--- /dev/null
@@ -0,0 +1,1780 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "mem.h"
+#include "io.h"
+#include "code.h"
+#include "tbl.h"
+#include "parse.h"
+#include "exec.h"
+#include "gfxview.h"
+
+#define max(a, b) (((a) >= (b)) ? (a) : (b))
+#define min(a, b) (((a) >= (b)) ? (b) : (a))
+
+#define getwintnset(to) \
+    if (getint (to, &wattrp[wattri].u.i) != -1) wattri++;
+#define getwpointnset(to) \
+    if (getxy (to, &wattrp[wattri].u.p) != -1) wattri++;
+#define getwsizenset(to) \
+    if (getxy (to, &wattrp[wattri].u.s) != -1) wattri++;
+#define getwrectnset(to) \
+    if (getrect (to, &wattrp[wattri].u.r) != -1) wattri++;
+#define getwstrnset(to) \
+    if (getstr (to, &wattrp[wattri].u.t) != -1) wattri++;
+#define getwcolornset(ko, vo) \
+    if (getcolor (ko, vo, &wattrp[wattri].u.c) != -1) wattri++;
+
+#define getaintnset(to, n, flag) \
+    if (to) { \
+        getint (to, &n), dp->flags |= flag; \
+    }
+#define getastrnset(to, s, flag) \
+    if (to) \
+        getstr (to, &s), dp->flags |= flag;
+
+typedef struct colorname_t {
+    char *name;
+    unsigned char r, g, b;
+} colorname_t;
+
+#include "colors.txt"
+
+typedef struct gfxrect_t {
+    struct gfxrect_t *next;
+    Tobj ko;
+    Grect_t r;
+} gfxrect_t;
+typedef struct gfxmenu_t {
+    struct gfxmenu_t *next;
+    Tobj ko;
+    long time;
+    int mi;
+} gfxmenu_t;
+#define LISTSIZE 100
+typedef struct gfxnode_t {
+    int inuse;
+    int wi;
+    Tobj plvo, pmvo, prvo, pkvo[256];
+    Gpoint_t plp, pmp, prp, pkp[256];
+    char ls, ms, rs, ks[256];
+    struct gfxrect_t *rect[LISTSIZE];
+    struct gfxmenu_t *menu[LISTSIZE];
+} gfxnode_t;
+#define GFXNODEINCR 20
+#define GFXNODESIZE sizeof (gfxnode_t)
+static gfxnode_t *gfxnodes;
+static int gfxnoden;
+
+#define ISAWIDGET(wi) (wi >= 0 && wi < Gwidgetn && Gwidgets[wi].inuse)
+#define ISALABEL(wi) (Gwidgets[wi].type == G_LABELWIDGET)
+#define ISACANVAS(wi) (Gwidgets[wi].type == G_CANVASWIDGET)
+#define ISACANVAS2(wi) (Gwidgets[wi].type == G_CANVASWIDGET || \
+        Gwidgets[wi].type == G_PCANVASWIDGET)
+#define NODEID(wi) Gwidgets[wi].udata
+#define ISABITMAP(bi) (bi >= 0 && bi < Gbitmapn && Gbitmaps[bi].inuse)
+
+static Gpoint_t *gpp = NULL;
+static long gpn = 0;
+#define GPINCR 100
+#define GPSIZE sizeof (Gpoint_t)
+
+static Gwattr_t *wattrp = NULL;
+static int wattrn = 0;
+static int wattri = 0;
+#define WATTRINCR 10
+#define WATTRSIZE sizeof (Gwattr_t)
+
+static Tobj rootwo, rootbo;
+
+static void nodeinit(int);
+static void nodeterm(int);
+static void rectinit(int);
+static void rectterm(int);
+static void rectinsert(int, Tobj, Grect_t);
+static void rectmerge(int, Tobj, Grect_t);
+static Tobj rectfind(int, Gpoint_t);
+static void rectdelete(int, Tobj);
+static void rectprune(int);
+static void menuinsert(int, Tobj, long, int);
+static int menufind(int, Tobj, long);
+static void menuprune(int);
+static int getwattr(Tobj, int *);
+static int getgattr(Tobj, Ggattr_t *);
+static int getrect(Tobj, Grect_t *);
+static int getxy(Tobj, Gxy_t *);
+static int getint(Tobj, int *);
+static int getdouble(Tobj, double *);
+static int getstr(Tobj, char **);
+static int getcolor(Tobj, Tobj, Gcolor_t *);
+
+void GFXinit(void)
+{
+    int ni;
+
+    Tinss(root, "widgets", (rootwo = Ttable(100)));
+    Tinss(root, "bitmaps", (rootbo = Ttable(100)));
+    gfxnodes = Marrayalloc((long) GFXNODEINCR * GFXNODESIZE);
+    gfxnoden = GFXNODEINCR;
+    for (ni = 0; ni < gfxnoden; ni++)
+       gfxnodes[ni].inuse = FALSE;
+    ni = 0;
+    gpp = Marrayalloc((long) GPINCR * GPSIZE);
+    gpn = GPINCR;
+    wattrp = Marrayalloc((long) WATTRINCR * WATTRSIZE);
+    wattrn = WATTRINCR, wattri = 0;
+}
+
+void GFXterm(void)
+{
+    int ni;
+
+    Marrayfree(wattrp), wattrp = NULL, wattrn = 0;
+    Marrayfree(gpp), gpp = NULL, gpn = 0;
+    for (ni = 0; ni < gfxnoden; ni++) {
+       if (gfxnodes[ni].inuse) {
+           Gdestroywidget(gfxnodes[ni].wi);
+           nodeterm(ni);
+           gfxnodes[ni].inuse = FALSE;
+       }
+    }
+    Marrayfree(gfxnodes), gfxnodes = NULL, gfxnoden = 0;
+    Tdels(root, "bitmaps");
+    Tdels(root, "widgets");
+}
+
+/* callback for mem.c module - removes dead objects from gfxnodes */
+void GFXprune(void)
+{
+    gfxnode_t *np;
+    int ni, ki;
+
+    for (ni = 0; ni < gfxnoden; ni++) {
+       np = &gfxnodes[ni];
+       if (np->inuse) {
+           rectprune(ni), menuprune(ni);
+           if (np->plvo && !M_AREAOF(np->plvo))
+               np->plvo = 0;
+           if (np->pmvo && !M_AREAOF(np->pmvo))
+               np->pmvo = 0;
+           if (np->prvo && !M_AREAOF(np->prvo))
+               np->prvo = 0;
+           for (ki = 0; ki < 256; ki++)
+               if (np->pkvo[ki] && !M_AREAOF(np->pkvo[ki]))
+                   np->pkvo[ki] = 0;
+       }
+    }
+}
+
+/* callback for g.c module - calls the appropriate lefty function */
+void GFXlabelcb(Gevent_t * evp)
+{
+    Tobj fo, to, co, wo;
+    char *fn;
+    char s[2];
+    long fm;
+    fn = 0;
+
+    wo = Tfindi(rootwo, evp->wi);
+    switch (evp->type) {
+    case G_MOUSE:
+       switch (evp->data) {
+       case G_LEFT:
+           fn = (evp->code == G_UP) ? "leftup" : "leftdown";
+           break;
+       case G_MIDDLE:
+           fn = (evp->code == G_UP) ? "middleup" : "middledown";
+           break;
+       case G_RIGHT:
+           fn = (evp->code == G_UP) ? "rightup" : "rightdown";
+           break;
+       }
+       break;
+    case G_KEYBD:
+       fn = (evp->code == G_UP) ? "keyup" : "keydown";
+       break;
+    }
+    if (!wo || !(fo = Tfinds(wo, fn)) || Tgettype(fo) != T_CODE)
+       if (!(fo = Tfinds(root, fn)) || Tgettype(fo) != T_CODE)
+           return;
+    fm = Mpushmark(fo);
+    to = Ttable(4);
+    Mpushmark(to);
+    Tinss(to, "widget", Tinteger(evp->wi));
+    if (evp->type == G_KEYBD)
+       s[0] = evp->data, s[1] = '\000', Tinss(to, "key", Tstring(s));
+    if ((co = Pfcall(fo, to)))
+       Eunit(co);
+    Mpopmark(fm);
+}
+
+/* callback for g.c module - calls the appropriate lefty function */
+void GFXviewcb(Gevent_t * evp)
+{
+    Tobj fo, to, co, wo;
+    char *fn;
+    long fm;
+
+    wo = Tfindi(rootwo, evp->wi);
+    fn = "closeview";
+    if (!wo || !(fo = Tfinds(wo, fn)) || Tgettype(fo) != T_CODE)
+       if (!(fo = Tfinds(root, fn)) || Tgettype(fo) != T_CODE)
+           exit(0);
+    fm = Mpushmark(fo);
+    to = Ttable(2);
+    Mpushmark(to);
+    Tinss(to, "widget", Tinteger(evp->wi));
+    if ((co = Pfcall(fo, to)))
+       Eunit(co);
+    Mpopmark(fm);
+}
+
+/* callback for g.c module - calls the appropriate lefty function */
+void GFXevent(Gevent_t * evp)
+{
+    Tobj vo, pvo = NULL, fo, to, po, co, wo;
+    gfxnode_t *np;
+    Gpoint_t pp = { 0, 0 };
+    char *fn;
+    char s[2];
+    long fm;
+    int ni;
+    fn = 0;
+
+    ni = Gwidgets[evp->wi].udata;
+    np = &gfxnodes[ni];
+    wo = Tfindi(rootwo, evp->wi);
+    if (!(vo = rectfind(ni, evp->p)))
+       vo = null;
+    switch (evp->type) {
+    case G_MOUSE:
+       switch (evp->data) {
+       case G_LEFT:
+           if (evp->code == G_UP)
+               fn = "leftup", pvo = np->plvo, pp = np->plp, np->ls = 0;
+           else
+               fn = "leftdown", np->plvo = vo, np->plp = evp->p, np->ls =
+                   1;
+           break;
+       case G_MIDDLE:
+           if (evp->code == G_UP)
+               fn = "middleup", pvo = np->pmvo, pp = np->pmp, np->ms = 0;
+           else
+               fn = "middledown", np->pmvo = vo, np->pmp =
+                   evp->p, np->ms = 1;
+           break;
+       case G_RIGHT:
+           if (evp->code == G_UP)
+               fn = "rightup", pvo = np->prvo, pp = np->prp, np->rs = 0;
+           else
+               fn = "rightdown", np->prvo = vo, np->prp = evp->p, np->rs =
+                   1;
+           break;
+       }
+       break;
+    case G_KEYBD:
+       if (evp->code == G_UP) {
+           fn = "keyup";
+           pvo = np->pkvo[evp->data];
+           pp = np->pkp[evp->data];
+           np->ks[evp->data] = 0;
+       } else {
+           fn = "keydown";
+           np->pkvo[evp->data] = vo;
+           np->pkp[evp->data] = evp->p;
+           np->ks[evp->data] = 1;
+       }
+       break;
+    }
+    if (!wo || !(fo = Tfinds(wo, fn)) || Tgettype(fo) != T_CODE)
+       if (!(fo = Tfinds(root, fn)) || Tgettype(fo) != T_CODE)
+           return;
+
+    fm = Mpushmark(fo);
+    to = Ttable(4);
+    Mpushmark(to);
+    Tinss(to, "widget", Tinteger(evp->wi));
+    Tinss(to, "obj", vo);
+    Tinss(to, "pos", (po = Ttable(2)));
+    Tinss(po, "x", Treal(evp->p.x));
+    Tinss(po, "y", Treal(evp->p.y));
+    if (evp->code == G_UP) {
+       Tinss(to, "pobj", pvo);
+       Tinss(to, "ppos", (po = Ttable(2)));
+       Tinss(po, "x", Treal(pp.x));
+       Tinss(po, "y", Treal(pp.y));
+    }
+    if (evp->type == G_KEYBD)
+       s[0] = evp->data, s[1] = '\000', Tinss(to, "key", Tstring(s));
+    if ((co = Pfcall(fo, to)))
+       Eunit(co);
+    Mpopmark(fm);
+}
+
+/* called directly from lefty.c, when any button is held down */
+void GFXmove(void)
+{
+    gfxnode_t *np;
+    char *fn[3];
+    Tobj vo[3], fo, to, po, co, wo;
+    Gpoint_t cp, pp[3];
+    long fm;
+    int count, i, ni;
+
+    for (ni = 0; ni < gfxnoden; ni++) {
+       np = &gfxnodes[ni];
+       if (!np->inuse || !ISACANVAS(np->wi) ||
+           !Gwidgets[np->wi].u.c->buttonsdown)
+           continue;
+       wo = Tfindi(rootwo, np->wi);
+       Ggetmousecoords(np->wi, &cp, &count);
+       if (!count) {
+           Gresetbstate(np->wi);
+           continue;
+       }
+       if (np->ls)
+           fn[0] = "leftmove", pp[0] = np->plp, vo[0] = np->plvo;
+       else
+           fn[0] = NULL;
+       if (np->ms)
+           fn[1] = "middlemove", pp[1] = np->pmp, vo[1] = np->pmvo;
+       else
+           fn[1] = NULL;
+       if (np->rs)
+           fn[2] = "rightmove", pp[2] = np->prp, vo[2] = np->prvo;
+       else
+           fn[2] = NULL;
+       for (i = 0; i < 3; i++) {
+           if (!fn[i])
+               continue;
+           if (!wo || !(fo = Tfinds(wo, fn[i])) || Tgettype(fo) != T_CODE)
+               fo = Tfinds(root, fn[i]);
+           if (fo && Tgettype(fo) == T_CODE) {
+               fm = Mpushmark(fo);
+               to = Ttable(4);
+               Mpushmark(to);
+               Tinss(to, "widget", Tinteger(np->wi));
+               Tinss(to, "obj", vo[i]);
+               Tinss(to, "pos", (po = Ttable(2)));
+               Tinss(po, "x", Treal(cp.x));
+               Tinss(po, "y", Treal(cp.y));
+               Tinss(to, "ppos", (po = Ttable(2)));
+               Tinss(po, "x", Treal(pp[i].x));
+               Tinss(po, "y", Treal(pp[i].y));
+               if ((co = Pfcall(fo, to)))
+                   Eunit(co);
+               Mpopmark(fm);
+           }
+       }
+    }
+}
+
+/* called directly from lefty.c, when any canvas needs to be redrawn */
+void GFXredraw(void)
+{
+    gfxnode_t *np;
+    Tobj wo, fo, co, to;
+    long fm;
+    int ni;
+
+    for (ni = 0; ni < gfxnoden; ni++) {
+       np = &gfxnodes[ni];
+       if (!np->inuse || !ISACANVAS(np->wi) ||
+           !Gwidgets[np->wi].u.c->needredraw)
+           continue;
+       Gwidgets[np->wi].u.c->needredraw = FALSE;
+       wo = Tfindi(rootwo, np->wi);
+       if (!wo || !(fo = Tfinds(wo, "redraw")) || Tgettype(fo) != T_CODE)
+           if (!(fo = Tfinds(root, "redraw")) || Tgettype(fo) != T_CODE)
+               return;
+
+       fm = Mpushmark(fo);
+       to = Ttable(4);
+       Mpushmark(to);
+       Tinss(to, "widget", Tinteger(np->wi));
+       if ((co = Pfcall(fo, to)))
+           Eunit(co);
+       Mpopmark(fm);
+    }
+}
+
+void GFXtextcb(int wi, char *s)
+{
+    Tobj wo, fo, co, to;
+    long fm;
+
+    if (!(wo = Tfindi(rootwo, wi)))
+       return;
+
+    if (!(fo = Tfinds(wo, "oneline")) || Tgettype(fo) != T_CODE)
+       return;
+
+    fm = Mpushmark(fo);
+    to = Ttable(4);
+    Mpushmark(to);
+    Tinss(to, "widget", Tinteger(wi));
+    Tinss(to, "text", Tstring(s));
+    if ((co = Pfcall(fo, to)))
+       Eunit(co);
+    Mpopmark(fm);
+}
+
+void GFXbuttoncb(int wi, void *data)
+{
+    Tobj wo, fo, co, to;
+    long fm;
+
+    if (!(wo = Tfindi(rootwo, wi)))
+       return;
+
+    if (!(fo = Tfinds(wo, "pressed")) || Tgettype(fo) != T_CODE)
+       return;
+
+    fm = Mpushmark(fo);
+    to = Ttable(4);
+    Mpushmark(to);
+    Tinss(to, "widget", Tinteger(wi));
+    if ((co = Pfcall(fo, to)))
+       Eunit(co);
+    Mpopmark(fm);
+}
+
+void GFXarrayresizecb(int wi, Gawdata_t * dp)
+{
+    Tobj wo, fo, co, so, to, lrtno = NULL, sxo, syo;
+    Tkvindex_t tkvi;
+    Gawcarray_t *cp;
+    int sx, sy, csx, csy, ci;
+    long fm;
+
+    if (!(wo = Tfindi(rootwo, wi)))
+       return;
+
+    if (!(fo = Tfinds(wo, "resize")) || Tgettype(fo) != T_CODE) {
+       Gawdefcoordscb(wi, dp);
+       return;
+    }
+
+    fm = Mpushmark(fo);
+    to = Ttable(4);
+    Mpushmark(to);
+    Tinss(to, "widget", Tinteger(wi));
+    Tinss(to, "size", (so = Ttable(2)));
+    Tinss(so, "x", Treal((double) dp->sx));
+    Tinss(so, "y", Treal((double) dp->sy));
+    if ((co = Pfcall(fo, to)))
+       lrtno = Eunit(co);
+    Mpopmark(fm);
+    if (!lrtno) {
+       Gawdefcoordscb(wi, dp);
+       return;
+    }
+    for (Tgetfirst(lrtno, &tkvi); tkvi.kvp; Tgetnext(&tkvi)) {
+       if (!T_ISNUMBER(tkvi.kvp->ko) || !T_ISTABLE((so = tkvi.kvp->vo)))
+           continue;
+       wi = Tgetnumber(tkvi.kvp->ko);
+       if (!ISAWIDGET(wi))
+           continue;
+       for (ci = 0; ci < dp->cj; ci++) {
+           cp = &dp->carray[ci];
+           if (!cp->flag || cp->w != Gwidgets[wi].w)
+               continue;
+           if ((sxo = Tfinds(so, "x")) && T_ISNUMBER(sxo))
+               cp->sx = Tgetnumber(sxo);
+           if ((syo = Tfinds(so, "y")) && T_ISNUMBER(syo))
+               cp->sy = Tgetnumber(syo);
+           break;
+       }
+    }
+    sx = dp->sx, sy = dp->sy;
+    csx = csy = 0;
+    for (ci = 0; ci < dp->cj; ci++) {
+       cp = &dp->carray[ci];
+       if (!cp->flag)
+           continue;
+       cp->ox = csx, cp->oy = csy;
+       if (dp->type == G_AWVARRAY)
+           cp->sx = sx - 2 * cp->bs, csy += cp->sy + 2 * cp->bs;
+       else
+           cp->sy = sy - 2 * cp->bs, csx += cp->sx + 2 * cp->bs;
+    }
+    if (dp->type == G_AWVARRAY)
+       dp->sy = csy;
+    else
+       dp->sx = csx;
+}
+
+/* callback for when there is input on some file descriptor */
+void GFXmonitorfile(int fd)
+{
+    Tobj fo, to, co;
+    long fm, tm;
+
+    if (!(fo = Tfinds(root, "monitorfile")) || Tgettype(fo) != T_CODE)
+       return;
+
+    fm = Mpushmark(fo);
+    to = Ttable(4);
+    tm = Mpushmark(to);
+    Tinss(to, "fd", Tinteger(fd));
+    if ((co = Pfcall(fo, to)))
+       Eunit(co);
+    Mpopmark(tm);
+    Mpopmark(fm);
+}
+
+/* callback for when there is no X event and no file input */
+void GFXidle(void)
+{
+    Tobj fo, to, co;
+    long fm, tm;
+
+    if (!(fo = Tfinds(root, "idle")) || Tgettype(fo) != T_CODE)
+       return;
+
+    fm = Mpushmark(fo);
+    to = Ttable(4);
+    tm = Mpushmark(to);
+    if ((co = Pfcall(fo, to)))
+       Eunit(co);
+    Mpopmark(tm);
+    Mpopmark(fm);
+}
+
+/* LEFTY builtin */
+int GFXcreatewidget(int argc, lvar_t * argv)
+{
+    Tobj pwo, cwo, cho;
+    long rtnm;
+    int type, pwi, wi, ni;
+
+    type = -1;
+    if (getint(argv[0].o, &pwi) == -1 || getwattr(argv[1].o, &type) == -1)
+       return L_FAILURE;
+    if (type != G_VIEWWIDGET && type != G_PCANVASWIDGET && !ISAWIDGET(pwi))
+       return L_FAILURE;
+    if (type == G_CANVASWIDGET || type == G_LABELWIDGET) {
+       for (ni = 0; ni < gfxnoden; ni++)
+           if (!gfxnodes[ni].inuse)
+               break;
+       if (ni == gfxnoden) {
+           gfxnodes = Marraygrow(gfxnodes,
+                                 (long) (ni + GFXNODEINCR) * GFXNODESIZE);
+           for (ni = gfxnoden; ni < gfxnoden + GFXNODEINCR; ni++)
+               gfxnodes[ni].inuse = FALSE;
+           ni = gfxnoden, gfxnoden += GFXNODEINCR;
+       }
+       nodeinit(ni);
+       if (wattri >= wattrn) {
+           wattrp = Marraygrow(wattrp,
+                               (long) (wattrn + WATTRINCR) * WATTRSIZE);
+           wattrn += WATTRINCR;
+       }
+       switch (type) {
+       case G_CANVASWIDGET:
+           wattrp[wattri].id = G_ATTRUSERDATA;
+           wattrp[wattri].u.u = ni, wattri++;
+           break;
+       case G_LABELWIDGET:
+           wattrp[wattri].id = G_ATTRUSERDATA;
+           wattrp[wattri].u.u = ni, wattri++;
+           break;
+       }
+       wi = gfxnodes[ni].wi = Gcreatewidget(pwi, type, wattri, wattrp);
+       gfxnodes[ni].inuse = TRUE;
+       goto done;
+    }
+    wi = Gcreatewidget(pwi, type, wattri, wattrp);
+
+  done:
+    Tinsi(rootwo, wi, (cwo = Ttable(4)));
+    rtno = Tinteger(wi);
+    rtnm = Mpushmark(rtno);
+    if (pwi != -1) {
+       Tinss(cwo, "parent", Tinteger(pwi));
+       if ((pwo = Tfindi(rootwo, pwi))) {
+           if (!(cho = Tfinds(pwo, "children")))
+               Tinss(pwo, "children", (cho = Ttable(2)));
+           Tinsi(cho, wi, Tinteger(pwi));
+       }
+    }
+    Mpopmark(rtnm);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXsetwidgetattr(int argc, lvar_t * argv)
+{
+    int wi, type, rtn;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi))
+       return L_FAILURE;
+    type = Gwidgets[wi].type;
+    if ((rtn = getwattr(argv[1].o, &type)) == -1)
+       return L_FAILURE;
+    Gsetwidgetattr(wi, wattri, wattrp);
+    rtno = Tinteger(rtn);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXgetwidgetattr(int argc, lvar_t * argv)
+{
+    Tkvindex_t tkvi;
+    Tobj po, so, ro, co, co2;
+    Gwattrmap_t *mapp;
+    int *ap;
+    int li, ai, type, color, wattri2, wi;
+    long rtnm;
+
+    wattri = 0;
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) ||
+       !T_ISTABLE(argv[1].o))
+       return L_FAILURE;
+    type = Gwidgets[wi].type;
+    for (ap = NULL, li = 0; Gwlist[li].wname; li++) {
+       if (type == Gwlist[li].wid) {
+           ap = Gwlist[li].attrid;
+           break;
+       }
+    }
+    if (!ap)
+       return L_FAILURE;
+    for (Tgetfirst(argv[1].o, &tkvi); tkvi.kvp; Tgetnext(&tkvi)) {
+       if (!T_ISSTRING(tkvi.kvp->vo))
+           continue;
+       for (ai = 0; ap[ai] != -1; ai++) {
+           if (Strcmp(Gwattrmap[ap[ai]].name,
+                      Tgetstring(tkvi.kvp->vo)) == 0) {
+               if (wattri >= wattrn) {
+                   wattrp = Marraygrow(wattrp,
+                                       (long) (wattrn +
+                                               WATTRINCR) * WATTRSIZE);
+                   wattrn += WATTRINCR;
+               }
+               if (ap[ai] == G_ATTRCOLOR) {
+                   for (color = 0; color < G_MAXCOLORS; color++) {
+                       if (wattri >= wattrn) {
+                           wattrp = Marraygrow(wattrp,
+                                               (long) (wattrn +
+                                                       WATTRINCR) *
+                                               WATTRSIZE);
+                           wattrn += WATTRINCR;
+                       }
+                       wattrp[wattri].u.c.index = color;
+                       wattrp[wattri++].id = ap[ai];
+                   }
+               } else
+                   wattrp[wattri++].id = ap[ai];
+               break;
+           }
+       }
+    }
+    if (Ggetwidgetattr(wi, wattri, wattrp) == -1)
+       return L_FAILURE;
+    rtno = Ttable(wattri);
+    rtnm = Mpushmark(rtno);
+    for (wattri2 = 0; wattri2 < wattri; wattri2++) {
+       mapp = &Gwattrmap[wattrp[wattri2].id];
+       switch (mapp->type) {
+       case G_ATTRTYPEPOINT:
+           Tinss(rtno, mapp->name, (po = Ttable(2)));
+           Tinss(po, "x", Treal(wattrp[wattri2].u.p.x));
+           Tinss(po, "y", Treal(wattrp[wattri2].u.p.y));
+           break;
+       case G_ATTRTYPESIZE:
+           Tinss(rtno, mapp->name, (so = Ttable(2)));
+           Tinss(so, "x", Treal(wattrp[wattri2].u.s.x));
+           Tinss(so, "y", Treal(wattrp[wattri2].u.s.y));
+           break;
+       case G_ATTRTYPERECT:
+           Tinss(rtno, mapp->name, (ro = Ttable(2)));
+           Tinsi(ro, 0, (po = Ttable(2)));
+           Tinss(po, "x", Treal(wattrp[wattri2].u.r.o.x));
+           Tinss(po, "y", Treal(wattrp[wattri2].u.r.o.y));
+           Tinsi(ro, 1, (po = Ttable(2)));
+           Tinss(po, "x", Treal(wattrp[wattri2].u.r.c.x));
+           Tinss(po, "y", Treal(wattrp[wattri2].u.r.c.y));
+           break;
+       case G_ATTRTYPETEXT:
+           Tinss(rtno, mapp->name, Tstring(wattrp[wattri2].u.t));
+           break;
+       case G_ATTRTYPEINT:
+           Tinss(rtno, mapp->name, Tinteger(wattrp[wattri2].u.i));
+           break;
+       case G_ATTRTYPECOLOR:
+           Tinss(rtno, mapp->name, (co = Ttable(G_MAXCOLORS)));
+           for (color = 0; color < G_MAXCOLORS; color++) {
+               Tinsi(co, color, (co2 = Ttable(3)));
+               Tinss(co2, "r", Treal(wattrp[wattri2 + color].u.c.r));
+               Tinss(co2, "g", Treal(wattrp[wattri2 + color].u.c.g));
+               Tinss(co2, "b", Treal(wattrp[wattri2 + color].u.c.g));
+           }
+           wattri2 += (G_MAXCOLORS - 1);
+           break;
+       default:
+           break;
+       }
+    }
+    Mpopmark(rtnm);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXdestroywidget(int argc, lvar_t * argv)
+{
+    Tkvindex_t tkvi;
+    Tobj wo, cho, pwio, pwo;
+    lvar_t argv2[1];
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi))
+       return L_FAILURE;
+
+    wo = Tfindi(rootwo, wi);
+    if ((cho = Tfinds(wo, "children"))) {
+       while (Tgettablen(cho) > 0) {
+           Tgetfirst(cho, &tkvi);
+           argv2[0].o = tkvi.kvp->ko;
+           GFXdestroywidget(1, argv2);
+       }
+    }
+    if ((pwio = Tfinds(wo, "parent"))) {
+       pwo = Tfindi(rootwo, Tgetinteger(pwio));
+       cho = Tfinds(pwo, "children");
+       Tdeli(cho, wi);
+    }
+    if (ISACANVAS(wi) || ISALABEL(wi)) {
+       nodeterm(NODEID(wi));
+       gfxnodes[NODEID(wi)].inuse = FALSE;
+    }
+    Gdestroywidget(wi);
+    Tdeli(rootwo, wi);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXclear(int argc, lvar_t * argv)
+{
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi))
+       return L_FAILURE;
+    Gcanvasclear(wi);
+    if (ISACANVAS(wi))
+       rectterm(NODEID(wi)), rectinit(NODEID(wi));
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXsetgfxattr(int argc, lvar_t * argv)
+{
+    Ggattr_t gattr;
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) ||
+       getgattr(argv[1].o, &gattr) == -1)
+       return L_FAILURE;
+    Gsetgfxattr(wi, &gattr);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXgetgfxattr(int argc, lvar_t * argv)
+{
+    Tkvindex_t tkvi;
+    Ggattr_t gattr;
+    long rtnm = 0;
+    int wi;
+    char *s = 0;
+
+    gattr.flags = (Ggattrflags_t) 0;
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) ||
+       !T_ISTABLE(argv[1].o))
+       return L_FAILURE;
+    for (Tgetfirst(argv[1].o, &tkvi); tkvi.kvp; Tgetnext(&tkvi)) {
+       if (!T_ISSTRING(tkvi.kvp->vo))
+           continue;
+       s = Tgetstring(tkvi.kvp->vo);
+       if (Strcmp(s, "color") == 0)
+           gattr.flags |= G_GATTRCOLOR;
+       else if (Strcmp(s, "width") == 0)
+           gattr.flags |= G_GATTRWIDTH;
+       else if (Strcmp(s, "mode") == 0)
+           gattr.flags |= G_GATTRMODE;
+       else if (Strcmp(s, "fill") == 0)
+           gattr.flags |= G_GATTRFILL;
+       else if (Strcmp(s, "style") == 0)
+           gattr.flags |= G_GATTRSTYLE;
+    }
+    if (Ggetgfxattr(wi, &gattr) == -1)
+       return L_FAILURE;
+    rtno = Ttable(wattri);
+    rtnm = Mpushmark(rtno);
+    if (gattr.flags & G_GATTRCOLOR) {
+       Tinss(rtno, "color", Tinteger(gattr.color));
+    } else if (gattr.flags & G_GATTRWIDTH) {
+       Tinss(rtno, "width", Tinteger(gattr.width));
+    } else if (gattr.flags & G_GATTRMODE) {
+       s = (gattr.mode == G_SRC) ? "src" : "xor";
+       Tinss(rtno, "mode", Tstring(s));
+    } else if (gattr.flags & G_GATTRFILL) {
+       s = (gattr.fill) ? "on" : "off";
+       Tinss(rtno, "fill", Tstring(s));
+    } else if (gattr.flags & G_GATTRSTYLE) {
+       switch (gattr.style) {
+       case G_SOLID:
+           s = "solid";
+           break;
+       case G_DASHED:
+           s = "dashed";
+           break;
+       case G_DOTTED:
+           s = "dotted";
+           break;
+       case G_LONGDASHED:
+           s = "longdashed";
+           break;
+       case G_SHORTDASHED:
+           s = "shortdashed";
+           break;
+       }
+       Tinss(rtno, "style", Tstring(s));
+    }
+    Mpopmark(rtnm);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXarrow(int argc, lvar_t * argv)
+{
+    Ggattr_t gattr;
+    Gpoint_t p0, p1;
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getxy(argv[2].o, &p0) == -1 || getxy(argv[3].o, &p1) == -1
+       || getgattr((argc == 5) ? argv[4].o : NULL, &gattr) == -1)
+       return L_FAILURE;
+    Garrow(wi, p0, p1, &gattr);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXline(int argc, lvar_t * argv)
+{
+    Ggattr_t gattr;
+    Gpoint_t p0, p1;
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getxy(argv[2].o, &p0) == -1 || getxy(argv[3].o, &p1) == -1
+       || getgattr((argc == 5) ? argv[4].o : NULL, &gattr) == -1)
+       return L_FAILURE;
+    Gline(wi, p0, p1, &gattr);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXbox(int argc, lvar_t * argv)
+{
+    Ggattr_t gattr;
+    Grect_t r;
+    int wi, rtn;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getrect(argv[2].o, &r) == -1
+       || getgattr((argc == 4) ? argv[3].o : NULL, &gattr) == -1)
+       return L_FAILURE;
+    rtn = Gbox(wi, r, &gattr);
+    if (rtn == 0 && argv[1].o != null && ISACANVAS(wi))
+       rectmerge(NODEID(wi), argv[1].o, r);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXpolygon(int argc, lvar_t * argv)
+{
+    Tobj po;
+    Ggattr_t gattr;
+    long i, pn;
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getgattr((argc == 4) ? argv[3].o : NULL, &gattr) == -1)
+       return L_FAILURE;
+    po = argv[2].o;
+    if ((pn = Tgettablen(po)) > gpn) {
+       gpp = Marraygrow(gpp, (long) pn * GPSIZE);
+       gpn = pn;
+    }
+    for (i = 0; i < pn; i++)
+       if (getxy(Tfindi(po, i), &gpp[i]) == -1)
+           return L_FAILURE;
+    Gpolygon(wi, pn, gpp, &gattr);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXsplinegon(int argc, lvar_t * argv)
+{
+    Tobj po;
+    Ggattr_t gattr;
+    long i, pn;
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getgattr((argc == 4) ? argv[3].o : NULL, &gattr) == -1)
+       return L_FAILURE;
+    po = argv[2].o;
+    if ((pn = Tgettablen(po)) > gpn) {
+       gpp = Marraygrow(gpp, (long) pn * GPSIZE);
+       gpn = pn;
+    }
+    for (i = 0; i < pn; i++)
+       if (getxy(Tfindi(po, i), &gpp[i]) == -1)
+           return L_FAILURE;
+    Gsplinegon(wi, pn, gpp, &gattr);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXarc(int argc, lvar_t * argv)
+{
+    Ggattr_t gattr;
+    Grect_t r;
+    Gpoint_t op;
+    Gsize_t sp;
+    int wi, rtn;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getxy(argv[2].o, &op) == -1 || getxy(argv[3].o, &sp) == -1
+       || getgattr((argc == 5) ? argv[4].o : NULL, &gattr) == -1)
+       return L_FAILURE;
+    rtn = Garc(wi, op, sp, (double) 0, (double) 360, &gattr);
+    if (rtn == 0 && argv[1].o != null && ISACANVAS(wi)) {
+       r.o.x = op.x - sp.x, r.o.y = op.y - sp.y;
+       r.c.x = op.x + sp.x, r.c.y = op.y + sp.y;
+       rectmerge(NODEID(wi), argv[1].o, r);
+    }
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXtext(int argc, lvar_t * argv)
+{
+    Ggattr_t gattr;
+    Gpoint_t p;
+    char *s, *fn, *justs;
+    double fs;
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getxy(argv[2].o, &p) == -1 || getstr(argv[3].o, &s) == -1
+       || getstr(argv[4].o, &fn) == -1 || getdouble(argv[5].o, &fs) == -1
+       || getstr(argv[6].o, &justs) == -1
+       || getgattr((argc == 8) ? argv[7].o : NULL, &gattr) == -1)
+       return L_FAILURE;
+    Gtext(wi, s, p, fn, fs, justs, &gattr);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXtextsize(int argc, lvar_t * argv)
+{
+    Gsize_t sp;
+    double fs;
+    char *s, *fn;
+    long m;
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS(wi) ||
+       getstr(argv[1].o, &s) == -1 || getstr(argv[2].o, &fn) == -1 ||
+       getdouble(argv[3].o, &fs) == -1)
+       return L_FAILURE;
+    if (Ggettextsize(wi, s, fn, fs, &sp) == -1)
+       return L_FAILURE;
+    m = Mpushmark((rtno = Ttable(2)));
+    Tinss(rtno, "x", Treal(sp.x)), Tinss(rtno, "y", Treal(sp.y));
+    Mpopmark(m);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXcreatebitmap(int argc, lvar_t * argv)
+{
+    Tobj bo, so;
+    Gsize_t s;
+    long rtnm;
+    int wi, bi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getxy(argv[1].o, &s) == -1)
+       return L_FAILURE;
+    if ((bi = Gcreatebitmap(wi, s)) == -1)
+       return L_FAILURE;
+    Tinsi(rootbo, bi, (bo = Ttable(4)));
+    rtno = Tinteger(bi);
+    rtnm = Mpushmark(rtno);
+    Tinss(bo, "canvas", Tinteger(bi));
+    Tinss(bo, "size", (so = Ttable(2)));
+    Tinss(so, "x", Tinteger((long) Gbitmaps[bi].size.x));
+    Tinss(so, "y", Tinteger((long) Gbitmaps[bi].size.y));
+    Mpopmark(rtnm);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXdestroybitmap(int argc, lvar_t * argv)
+{
+    int bi;
+
+    if (getint(argv[0].o, &bi) == -1 || !ISABITMAP(bi))
+       return L_FAILURE;
+    Gdestroybitmap(bi);
+    Tdeli(rootbo, bi);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXreadbitmap(int argc, lvar_t * argv)
+{
+    Tobj bo, so;
+    long rtnm;
+    int wi, bi, ioi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getint(argv[1].o, &ioi) == -1 || ioi < 0 || ioi > ion
+       || !iop[ioi].inuse)
+       return L_FAILURE;
+    if ((bi = Greadbitmap(wi, iop[ioi].ifp)) == -1)
+       return L_FAILURE;
+    Tinsi(rootbo, bi, (bo = Ttable(4)));
+    rtno = Tinteger(bi);
+    rtnm = Mpushmark(rtno);
+    Tinss(bo, "canvas", Tinteger(bi));
+    Tinss(bo, "size", (so = Ttable(2)));
+    Tinss(so, "x", Tinteger((long) Gbitmaps[bi].size.x));
+    Tinss(so, "y", Tinteger((long) Gbitmaps[bi].size.y));
+    Mpopmark(rtnm);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXwritebitmap(int argc, lvar_t * argv)
+{
+    int bi, ioi;
+
+    if (getint(argv[0].o, &ioi) == -1 || ioi < 0 || ioi > ion ||
+       !iop[ioi].inuse || getint(argv[1].o, &bi) == -1 || !ISABITMAP(bi))
+       return L_FAILURE;
+    Gwritebitmap(iop[ioi].ofp, bi);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXbitblt(int argc, lvar_t * argv)
+{
+    Ggattr_t gattr;
+    Grect_t r;
+    Gpoint_t p;
+    char *mode;
+    int wi, bi, rtn;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS2(wi)
+       || getxy(argv[2].o, &p) == -1 || getrect(argv[3].o, &r) == -1
+       || getint(argv[4].o, &bi) == -1 || getstr(argv[5].o, &mode) == -1
+       || getgattr((argc == 7) ? argv[6].o : NULL, &gattr) == -1)
+       return L_FAILURE;
+    rtn = Gbitblt(wi, p, r, bi, mode, &gattr);
+    if (rtn == 0 && argv[1].o != null && ISACANVAS(wi) &&
+       strcmp(mode, "b2c") == 0)
+       rectmerge(NODEID(wi), argv[1].o, r);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXclearpick(int argc, lvar_t * argv)
+{
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS(wi))
+       return L_FAILURE;
+    if (argv[1].o != null)
+       rectdelete(NODEID(wi), argv[1].o);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXsetpick(int argc, lvar_t * argv)
+{
+    Grect_t r;
+    int wi;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) || !ISACANVAS(wi) ||
+       getrect(argv[2].o, &r) == -1)
+       return L_FAILURE;
+    if (argv[1].o != null)
+       rectinsert(NODEID(wi), argv[1].o, r);
+    return L_SUCCESS;
+}
+
+/* LEFTY builtin */
+int GFXdisplaymenu(int argc, lvar_t * argv)
+{
+    Tobj mo, meo;
+    char buf[50];
+    char *entries[1];
+    int wi, mi, mei;
+    int rtn;
+
+    if (getint(argv[0].o, &wi) == -1 || !ISAWIDGET(wi) ||
+       !(ISACANVAS(wi) || ISALABEL(wi)))
+       return L_FAILURE;
+    mo = argv[1].o;
+    if ((mi = menufind(NODEID(wi), mo, Tgettime(mo))) != -1) {
+       if ((rtn = Gmenudisplay(wi, mi)) == -1)
+           rtno = NULL;
+       else
+           rtno = Tinteger(rtn);
+       return L_SUCCESS;
+    }
+    wattri = 0;
+    mi = Gcreatewidget(wi, G_MENUWIDGET, wattri, wattrp);
+    mei = 0;
+    while ((meo = Tfindi(mo, mei))) {
+       switch (Tgettype(meo)) {
+       case T_STRING:
+           entries[0] = Tgetstring(meo);
+           break;
+       case T_INTEGER:
+           sprintf(buf, "%d", (int) Tgetnumber(meo));
+           entries[0] = &buf[0];
+           break;
+       case T_REAL:
+           sprintf(buf, "%f", Tgetnumber(meo));
+           entries[0] = &buf[0];
+           break;
+       case T_CODE:
+       case T_TABLE:
+       case T_SIZE:
+           break;
+       }
+       Gmenuaddentries(mi, 1, &entries[0]);
+       mei++;
+    }
+    menuinsert(NODEID(wi), mo, Tgettime(mo), mi);
+    Ttime++;
+    if ((rtn = Gmenudisplay(wi, mi)) == -1)
+       rtno = NULL;
+    else
+       rtno = Tinteger(rtn);
+    return L_SUCCESS;
+}
+
+static int getwattr(Tobj ao, int *type)
+{
+    Tkvindex_t tkvi;
+    Tobj co;
+    int *ap;
+    char *ts;
+    int li, ai;
+
+    wattri = 0;
+    if (!ao && Tgettype(ao) != T_TABLE)
+       return -1;
+    if (*type == -1) {
+       if (getstr(Tfinds(ao, "type"), &ts) == -1)
+           return -1;
+       for (ap = NULL, li = 0; Gwlist[li].wname; li++) {
+           if (Strcmp(ts, Gwlist[li].wname) == 0) {
+               *type = Gwlist[li].wid;
+               ap = Gwlist[li].attrid;
+               break;
+           }
+       }
+       if (*type == -1)
+           return -1;
+       if (wattri >= wattrn) {
+           wattrp = Marraygrow(wattrp,
+                               (long) (wattrn + WATTRINCR) * WATTRSIZE);
+           wattrn += WATTRINCR;
+       }
+       switch (*type) {
+       case G_TEXTWIDGET:
+           wattrp[wattri].id = G_ATTRNEWLINECB;
+           wattrp[wattri].u.func = (void *) (GFXtextcb), wattri++;
+           break;
+       case G_ARRAYWIDGET:
+           wattrp[wattri].id = G_ATTRRESIZECB;
+           wattrp[wattri].u.func = (void *) (GFXarrayresizecb), wattri++;
+           break;
+       case G_BUTTONWIDGET:
+           wattrp[wattri].id = G_ATTRBUTTONCB;
+           wattrp[wattri].u.func = (void *) (GFXbuttoncb), wattri++;
+           break;
+       case G_LABELWIDGET:
+           wattrp[wattri].id = G_ATTREVENTCB;
+           wattrp[wattri].u.func = (void *) (GFXlabelcb), wattri++;
+           break;
+       case G_CANVASWIDGET:
+           wattrp[wattri].id = G_ATTREVENTCB;
+           wattrp[wattri].u.func = (void *) (GFXevent), wattri++;
+           break;
+       case G_VIEWWIDGET:
+           wattrp[wattri].id = G_ATTREVENTCB;
+           wattrp[wattri].u.func = (void *) (GFXviewcb), wattri++;
+           break;
+       }
+    } else {
+       for (ap = NULL, li = 0; Gwlist[li].wname; li++) {
+           if (*type == Gwlist[li].wid) {
+               ap = Gwlist[li].attrid;
+               break;
+           }
+       }
+    }
+    for (ai = 0; ap[ai] != -1; ai++) {
+       if (wattri >= wattrn) {
+           wattrp = Marraygrow(wattrp,
+                               (long) (wattrn + WATTRINCR) * WATTRSIZE);
+           wattrn += WATTRINCR;
+       }
+       wattrp[wattri].id = ap[ai];
+       switch (Gwattrmap[ap[ai]].type) {
+       case G_ATTRTYPEPOINT:
+           getwpointnset(Tfinds(ao, Gwattrmap[ap[ai]].name));
+           break;
+       case G_ATTRTYPESIZE:
+           getwsizenset(Tfinds(ao, Gwattrmap[ap[ai]].name));
+           break;
+       case G_ATTRTYPERECT:
+           getwrectnset(Tfinds(ao, Gwattrmap[ap[ai]].name));
+           break;
+       case G_ATTRTYPETEXT:
+           getwstrnset(Tfinds(ao, Gwattrmap[ap[ai]].name));
+           break;
+       case G_ATTRTYPEINT:
+           getwintnset(Tfinds(ao, Gwattrmap[ap[ai]].name));
+           break;
+       case G_ATTRTYPECOLOR:
+           if ((co = Tfinds(ao, Gwattrmap[ap[ai]].name)) &&
+               Tgettype(co) == T_TABLE) {
+               for (Tgetfirst(co, &tkvi); tkvi.kvp; Tgetnext(&tkvi)) {
+                   if (wattri >= wattrn) {
+                       wattrp =
+                           Marraygrow(wattrp,
+                                      (long) (wattrn +
+                                              WATTRINCR) * WATTRSIZE);
+                       wattrn += WATTRINCR;
+                   }
+                   wattrp[wattri].id = ap[ai];
+                   getwcolornset(tkvi.kvp->ko, tkvi.kvp->vo);
+               }
+           }
+           break;
+       default:
+           break;
+       }
+    }
+    return wattri;
+}
+
+static int getgattr(Tobj ao, Ggattr_t * dp)
+{
+    Tkvindex_t tkvi;
+    char *s, *s2;
+
+    dp->flags = (Ggattrflags_t) 0;
+    if (!ao)
+       return 0;
+    if (Tgettype(ao) != T_TABLE)
+       return -1;
+    for (Tgetfirst(ao, &tkvi); tkvi.kvp; Tgetnext(&tkvi)) {
+       if (Tgettype(tkvi.kvp->ko) != T_STRING)
+           continue;
+       s = Tgetstring(tkvi.kvp->ko);
+       if (Strcmp(s, "color") == 0) {
+           getaintnset(tkvi.kvp->vo, dp->color, G_GATTRCOLOR);
+       } else if (Strcmp(s, "width") == 0) {
+           getaintnset(tkvi.kvp->vo, dp->width, G_GATTRWIDTH);
+       } else if (Strcmp(s, "mode") == 0) {
+           getastrnset(tkvi.kvp->vo, s2, G_GATTRMODE);
+           if (dp->flags & G_GATTRMODE) {
+               if (Strcmp(s2, "src") == 0)
+                   dp->mode = G_SRC;
+               else if (Strcmp(s2, "xor") == 0)
+                   dp->mode = G_XOR;
+               else
+                   dp->flags &= ~G_GATTRMODE;
+           }
+       } else if (Strcmp(s, "fill") == 0) {
+           getastrnset(tkvi.kvp->vo, s2, G_GATTRFILL);
+           if (dp->flags & G_GATTRFILL) {
+               if (Strcmp(s2, "on") == 0)
+                   dp->fill = 1;
+               else if (Strcmp(s2, "off") == 0)
+                   dp->fill = 0;
+               else
+                   dp->flags &= ~G_GATTRFILL;
+           }
+       } else if (Strcmp(s, "style") == 0) {
+           getastrnset(tkvi.kvp->vo, s2, G_GATTRSTYLE);
+           if (dp->flags & G_GATTRSTYLE) {
+               if (Strcmp(s2, "solid") == 0)
+                   dp->style = G_SOLID;
+               else if (Strcmp(s2, "dashed") == 0)
+                   dp->style = G_DASHED;
+               else if (Strcmp(s2, "dotted") == 0)
+                   dp->style = G_DOTTED;
+               else if (Strcmp(s2, "longdashed") == 0)
+                   dp->style = G_LONGDASHED;
+               else if (Strcmp(s2, "shortdashed") == 0)
+                   dp->style = G_SHORTDASHED;
+               else
+                   dp->flags &= ~G_GATTRSTYLE;
+           }
+       }
+    }
+    return 0;
+}
+
+static int getrect(Tobj ro, Grect_t * rp)
+{
+    if (ro && Tgettype(ro) == T_TABLE)
+       if (getxy(Tfindi(ro, 0), &rp->o) != -1 &&
+           getxy(Tfindi(ro, 1), &rp->c) != -1)
+           return 0;
+    return -1;
+}
+
+static int getxy(Tobj po, Gxy_t * xyp)
+{
+    Tobj xo, yo;
+
+    if (po && Tgettype(po) == T_TABLE) {
+       xo = Tfinds(po, "x"), yo = Tfinds(po, "y");
+       if (xo && T_ISNUMBER(xo) && yo && T_ISNUMBER(yo)) {
+           xyp->x = Tgetnumber(xo), xyp->y = Tgetnumber(yo);
+           return 0;
+       }
+    }
+    return -1;
+}
+
+static int getint(Tobj to, int *ip)
+{
+    if (to && T_ISNUMBER(to)) {
+       *ip = Tgetnumber(to);
+       return 0;
+    }
+    return -1;
+}
+
+static int getdouble(Tobj to, double *dp)
+{
+    if (to && T_ISNUMBER(to)) {
+       *dp = Tgetnumber(to);
+       return 0;
+    }
+    return -1;
+}
+
+static int getstr(Tobj so, char **s)
+{
+    if (so && T_ISSTRING(so)) {
+       *s = Tgetstring(so);
+       return 0;
+    }
+    return -1;
+}
+
+/* scan a string for 3 floating point numbers separated by
+ * white-space or commas.
+ */
+static int scanhsv(char *strp, double *h, double *s, double *v)
+{
+    char *endp;
+
+    *h = strtod(strp, &endp);
+    if (endp == strp)
+       return 0;
+    strp = endp;
+    while ((*strp == ',') || isspace(*strp))
+       strp++;
+    *s = strtod(strp, &endp);
+    if (endp == strp)
+       return 0;
+    strp = endp;
+    while ((*strp == ',') || isspace(*strp))
+       strp++;
+    *v = strtod(strp, &endp);
+    if (endp == strp)
+       return 0;
+    return 1;
+}
+
+static void hsv2rgb(double h, double s, double v, Gcolor_t * cp)
+{
+    double r, g, b;
+    double f, p, q, t;
+    int i;
+
+    /* clip to reasonable values */
+    h = max(min(h, 1.0), 0.0);
+    s = max(min(s, 1.0), 0.0);
+    v = max(min(v, 1.0), 0.0);
+
+    r = g = b = v;
+    if (s != 0.0) {
+       if (h == 1.0)
+           h = 0.0;
+       h = h * 6.0;
+       i = (int) h;
+       f = h - (double) i;
+       p = v * (1 - s);
+       q = v * (1 - (s * f));
+       t = v * (1 - (s * (1 - f)));
+       switch (i) {
+       case 0:
+           r = v;
+           g = t;
+           b = p;
+           break;
+       case 1:
+           r = q;
+           g = v;
+           b = p;
+           break;
+       case 2:
+           r = p;
+           g = v;
+           b = t;
+           break;
+       case 3:
+           r = p;
+           g = q;
+           b = v;
+           break;
+       case 4:
+           r = t;
+           g = p;
+           b = v;
+           break;
+       case 5:
+           r = v;
+           g = p;
+           b = q;
+           break;
+       }
+    }
+    cp->r = (int) (255.0 * r);
+    cp->g = (int) (255.0 * g);
+    cp->b = (int) (255.0 * b);
+}
+
+static int getcolor(Tobj ko, Tobj co, Gcolor_t * cp)
+{
+    Tobj ro, go, bo, ho, so, vo;
+    char *s, *s1, *s2;
+    double hc, sc, vc;
+    int cni;
+
+    if (ko && T_ISNUMBER(ko))
+       cp->index = Tgetnumber(ko);
+    else
+       return -1;
+    if (!co || !(T_ISSTRING(co) || T_ISTABLE(co)))
+       return -1;
+    if (T_ISSTRING(co)) {
+       unsigned int r, g, b, a;
+
+       s = Tgetstring(co);
+       while (*s == ' ')
+           s++;                /* skip over any leading spaces */
+       if ((*s == '#')
+           && (sscanf(s, "#%2x%2x%2x%2x", &r, &g, &b, &a) >= 3)) {
+           cp->r = r;
+           cp->g = g;
+           cp->b = b;
+           return 0;
+       }
+       if ((isdigit(*s) || (*s == '.')) && scanhsv(s, &hc, &sc, &vc)) {
+           hsv2rgb(hc, sc, vc, cp);
+           return 0;
+       }
+       for (cni = 0; cni < sizeof(colornames) / sizeof(colorname_t);
+            cni++) {
+           for (s1 = colornames[cni].name, s2 = s; *s1 && *s2; s1++, s2++) {
+               if (*s2 == ' ' || *s2 == '_')
+                   s2++;
+               else if (*s1 != *s2)
+                   break;
+           }
+           if (*s1 != *s2)
+               continue;
+           cp->r = colornames[cni].r;
+           cp->g = colornames[cni].g;
+           cp->b = colornames[cni].b;
+           return 0;
+       }
+       return -1;
+    } else {
+       ro = Tfinds(co, "r");
+       go = Tfinds(co, "g");
+       bo = Tfinds(co, "b");
+       if (ro && T_ISNUMBER(ro) && go && T_ISNUMBER(go) &&
+           bo && T_ISNUMBER(bo)) {
+           cp->r = Tgetnumber(ro);
+           cp->g = Tgetnumber(go);
+           cp->b = Tgetnumber(bo);
+           return 0;
+       } else {
+           ho = Tfinds(co, "h");
+           so = Tfinds(co, "s");
+           vo = Tfinds(co, "v");
+           if (ho && T_ISNUMBER(ho) && so && T_ISNUMBER(so) &&
+               vo && T_ISNUMBER(vo)) {
+               hc = Tgetnumber(ho);
+               sc = Tgetnumber(so);
+               vc = Tgetnumber(vo);
+               hsv2rgb(hc, sc, vc, cp);
+               return 0;
+           } else
+               return -1;
+       }
+    }
+    /* return -1; */
+}
+
+static void nodeinit(int ni)
+{
+    int li, ki;
+
+    for (li = 0; li < LISTSIZE; li++)
+       gfxnodes[ni].rect[li] = NULL, gfxnodes[ni].menu[li] = NULL;
+    gfxnodes[ni].ls = gfxnodes[ni].ms = gfxnodes[ni].rs = 0;
+    for (ki = 0; ki < 256; ki++)
+       gfxnodes[ni].ks[ki] = 0;
+    gfxnodes[ni].plvo = gfxnodes[ni].pmvo = gfxnodes[ni].prvo = 0;
+    for (ki = 0; ki < 256; ki++)
+       gfxnodes[ni].pkvo[ki] = 0;
+}
+
+static void nodeterm(int ni)
+{
+    gfxrect_t **rp;
+    gfxrect_t *crp, *nrp;
+    gfxmenu_t **mp;
+    gfxmenu_t *cmp, *nmp;
+    int li;
+
+    for (li = 0; li < LISTSIZE; li++) {
+       rp = &gfxnodes[ni].rect[li];
+       for (crp = *rp; crp; crp = nrp)
+           nrp = crp->next, free(crp);
+       *rp = NULL;
+       mp = &gfxnodes[ni].menu[li];
+       for (cmp = *mp; cmp; cmp = nmp)
+           nmp = cmp->next, free(cmp);
+       *mp = NULL;
+    }
+}
+
+static void rectinit(int ni)
+{
+    int li;
+
+    for (li = 0; li < LISTSIZE; li++)
+       gfxnodes[ni].rect[li] = NULL;
+}
+
+static void rectterm(int ni)
+{
+    gfxrect_t **rp;
+    gfxrect_t *crp, *nrp;
+    int li;
+
+    for (li = 0; li < LISTSIZE; li++) {
+       rp = &gfxnodes[ni].rect[li];
+       for (crp = *rp; crp; crp = nrp)
+           nrp = crp->next, free(crp);
+       *rp = NULL;
+    }
+}
+
+static void rectinsert(int ni, Tobj ko, Grect_t r)
+{
+    gfxrect_t **rp;
+    gfxrect_t *crp;
+
+    rp = &gfxnodes[ni].rect[(unsigned long) ko % LISTSIZE];
+    for (crp = *rp; crp; crp = crp->next)
+       if (crp->ko == ko) {
+           crp->r.o.x = min(r.o.x, r.c.x);
+           crp->r.o.y = min(r.o.y, r.c.y);
+           crp->r.c.x = max(r.o.x, r.c.x);
+           crp->r.c.y = max(r.o.y, r.c.y);
+           return;
+       }
+    if (!(crp = malloc(sizeof(gfxrect_t))))
+       panic(POS, "rectinsert", "rect malloc failed");
+
+    crp->ko = ko;
+    crp->r.o.x = min(r.o.x, r.c.x);
+    crp->r.o.y = min(r.o.y, r.c.y);
+    crp->r.c.x = max(r.o.x, r.c.x);
+    crp->r.c.y = max(r.o.y, r.c.y);
+    crp->next = *rp;
+    *rp = crp;
+}
+
+static void rectmerge(int ni, Tobj ko, Grect_t r)
+{
+    gfxrect_t **rp;
+    gfxrect_t *crp;
+
+    rp = &gfxnodes[ni].rect[(unsigned long) ko % LISTSIZE];
+    for (crp = *rp; crp; crp = crp->next)
+       if (crp->ko == ko) {
+           crp->r.o.x = min(crp->r.o.x, min(r.o.x, r.c.x));
+           crp->r.o.y = min(crp->r.o.y, min(r.o.y, r.c.y));
+           crp->r.c.x = max(crp->r.c.x, max(r.o.x, r.c.x));
+           crp->r.c.y = max(crp->r.c.y, max(r.o.y, r.c.y));
+           return;
+       }
+    if (!(crp = malloc(sizeof(gfxrect_t))))
+       panic(POS, "rectmerge", "rect malloc failed");
+
+    crp->ko = ko;
+    crp->r.o.x = min(r.o.x, r.c.x);
+    crp->r.o.y = min(r.o.y, r.c.y);
+    crp->r.c.x = max(r.o.x, r.c.x);
+    crp->r.c.y = max(r.o.y, r.c.y);
+    crp->next = *rp;
+    *rp = crp;
+}
+
+static Tobj rectfind(int ni, Gpoint_t p)
+{
+    gfxrect_t **rp;
+    gfxrect_t *crp;
+    int li;
+
+    for (li = 0; li < LISTSIZE; li++) {
+       rp = &gfxnodes[ni].rect[li];
+       for (crp = *rp; crp; crp = crp->next)
+           if (crp->r.o.x <= p.x && crp->r.c.x >= p.x &&
+               crp->r.o.y <= p.y && crp->r.c.y >= p.y)
+               return crp->ko;
+    }
+    return NULL;
+}
+
+static void rectdelete(int ni, Tobj ko)
+{
+    gfxrect_t **rp;
+    gfxrect_t *crp, *prp;
+
+    rp = &gfxnodes[ni].rect[(unsigned long) ko % LISTSIZE];
+    for (crp = *rp, prp = NULL; crp; prp = crp, crp = crp->next)
+       if (crp->ko == ko) {
+           if (crp == *rp)
+               *rp = crp->next;
+           else
+               prp->next = crp->next;
+           free(crp);
+           return;
+       }
+}
+
+static void rectprune(int ni)
+{
+    gfxrect_t **rp;
+    gfxrect_t *crp, *prp;
+    int li;
+
+    for (li = 0; li < LISTSIZE; li++) {
+       rp = &gfxnodes[ni].rect[li];
+       for (crp = *rp, prp = NULL; crp;) {
+           if (!M_AREAOF(crp->ko)) {
+               if (crp == *rp)
+                   *rp = crp->next, free(crp), crp = *rp;
+               else
+                   prp->next = crp->next, free(crp), crp = prp->next;
+           } else
+               prp = crp, crp = crp->next;
+       }
+    }
+}
+
+static void menuinsert(int ni, Tobj ko, long time, int mi)
+{
+    gfxmenu_t **mp;
+    gfxmenu_t *cmp;
+
+    mp = &gfxnodes[ni].menu[(unsigned long) ko % LISTSIZE];
+    for (cmp = *mp; cmp; cmp = cmp->next)
+       if (cmp->ko == ko) {
+           cmp->time = time, cmp->mi = mi;
+           return;
+       }
+    if (!(cmp = malloc(sizeof(gfxmenu_t))))
+       panic(POS, "menuinsert", "menu malloc failed");
+
+    cmp->ko = ko;
+    cmp->time = time;
+    cmp->mi = mi;
+    cmp->next = *mp;
+    *mp = cmp;
+}
+
+static int menufind(int ni, Tobj ko, long time)
+{
+    gfxmenu_t **mp;
+    gfxmenu_t *cmp;
+
+    mp = &gfxnodes[ni].menu[(unsigned long) ko % LISTSIZE];
+    for (cmp = *mp; cmp; cmp = cmp->next)
+       if (cmp->ko == ko && cmp->time == time)
+           return cmp->mi;
+    return -1;
+}
+
+static void menuprune(int ni)
+{
+    gfxmenu_t **mp;
+    gfxmenu_t *cmp, *pmp;
+    int li;
+
+    for (li = 0; li < LISTSIZE; li++) {
+       mp = &gfxnodes[ni].menu[li];
+       for (cmp = *mp, pmp = NULL; cmp;) {
+           if (!M_AREAOF(cmp->ko)) {
+               if (cmp == *mp)
+                   *mp = cmp->next, free(cmp), cmp = *mp;
+               else
+                   pmp->next = cmp->next, free(cmp), cmp = pmp->next;
+           } else
+               pmp = cmp, cmp = cmp->next;
+       }
+    }
+}
diff --git a/cmd/lefty/gfxview.h b/cmd/lefty/gfxview.h
new file mode 100644 (file)
index 0000000..685e971
--- /dev/null
@@ -0,0 +1,66 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _GFXVIEW_H
+#define _GFXVIEW_H
+    void GFXinit(void);
+    void GFXterm(void);
+    void GFXprune(void);
+    void GFXlabelcb(Gevent_t *);
+    void GFXviewcb(Gevent_t *);
+    void GFXevent(Gevent_t *);
+    void GFXmove(void);
+    void GFXredraw(void);
+    void GFXtextcb(int, char *);
+    void GFXbuttoncb(int, void *);
+    void GFXmonitorfile(int);
+    void GFXidle(void);
+
+    int GFXcreatewidget(int, lvar_t *);
+    int GFXsetwidgetattr(int, lvar_t *);
+    int GFXgetwidgetattr(int, lvar_t *);
+    int GFXdestroywidget(int, lvar_t *);
+    int GFXclear(int, lvar_t *);
+    int GFXsetgfxattr(int, lvar_t *);
+    int GFXgetgfxattr(int, lvar_t *);
+    int GFXarrow(int, lvar_t *);
+    int GFXline(int, lvar_t *);
+    int GFXbox(int, lvar_t *);
+    int GFXpolygon(int, lvar_t *);
+    int GFXsplinegon(int, lvar_t *);
+    int GFXarc(int, lvar_t *);
+    int GFXtext(int, lvar_t *);
+    int GFXtextsize(int, lvar_t *);
+    int GFXcreatebitmap(int, lvar_t *);
+    int GFXdestroybitmap(int, lvar_t *);
+    int GFXreadbitmap(int, lvar_t *);
+    int GFXwritebitmap(int, lvar_t *);
+    int GFXbitblt(int, lvar_t *);
+    int GFXclearpick(int, lvar_t *);
+    int GFXsetpick(int, lvar_t *);
+    int GFXdisplaymenu(int, lvar_t *);
+#endif                         /* _GFXVIEW_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/internal.c b/cmd/lefty/internal.c
new file mode 100644 (file)
index 0000000..a831da2
--- /dev/null
@@ -0,0 +1,841 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#include "common.h"
+#include "g.h"
+#include "mem.h"
+#include "io.h"
+#include "code.h"
+#include "tbl.h"
+#include "parse.h"
+#include "exec.h"
+#include "display.h"
+#include "txtview.h"
+#include "gfxview.h"
+#ifdef FEATURE_DOT
+#include "dot2l.h"
+#endif
+#ifdef FEATURE_GMAP
+#include "gmap2l.h"
+#endif
+#include "internal.h"
+#ifndef FEATURE_MS
+#include <sys/time.h>
+#ifdef HAVE_UNISTD_H
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+#endif
+
+int Idump(int, Tonm_t *);
+int Icopy(int, Tonm_t *);
+int Iremove(int, Tonm_t *);
+int Itablesize(int, Tonm_t *);
+int Iopenio(int, Tonm_t *);
+int Icloseio(int, Tonm_t *);
+int Ireadline(int, Tonm_t *);
+int Iread(int, Tonm_t *);
+int Iwriteline(int, Tonm_t *);
+int Iatan(int, Tonm_t *);
+int Itan(int, Tonm_t *);
+int Icos(int, Tonm_t *);
+int Isin(int, Tonm_t *);
+int Isqrt(int, Tonm_t *);
+int Irandom(int, Tonm_t *);
+int Intos(int, Tonm_t *);
+int Iston(int, Tonm_t *);
+int Isplit(int, Tonm_t *);
+int Iconcat(int, Tonm_t *);
+int Iquote(int, Tonm_t *);
+int Itoint(int, Tonm_t *);
+int Istrlen(int, Tonm_t *);
+int Iload(int, Tonm_t *);
+int Irun(int, Tonm_t *);
+int Imonitor(int, Tonm_t *);
+int Iidlerun(int, Tonm_t *);
+int Itime(int, Tonm_t *);
+int Isleep(int, Tonm_t *);
+int Iecho(int, Tonm_t *);
+int Igetenv(int, Tonm_t *);
+int Iputenv(int, Tonm_t *);
+int Isystem(int, Tonm_t *);
+int Iexit(int, Tonm_t *);
+#ifdef FEATURE_DOT
+int Iparsegraphlabel(int, Tonm_t *);
+int Ireadgraph(int, Tonm_t *);
+int Iwritegraph(int, Tonm_t *);
+#endif
+#ifdef FEATURE_CS
+int C2Lreadcsmessage(int, Tonm_t *);
+#endif
+
+#define MAXN 10000
+
+Ifunc_t Ifuncs[] = {
+    {"createwidget", GFXcreatewidget, 2, 2},
+    {"setwidgetattr", GFXsetwidgetattr, 2, 2},
+    {"getwidgetattr", GFXgetwidgetattr, 2, 2},
+    {"destroywidget", GFXdestroywidget, 1, 1},
+    {"clear", GFXclear, 1, 1},
+    {"setgfxattr", GFXsetgfxattr, 2, 2},
+    {"getgfxattr", GFXgetgfxattr, 2, 2},
+    {"arrow", GFXarrow, 4, 5},
+    {"line", GFXline, 4, 5},
+    {"box", GFXbox, 3, 4},
+    {"polygon", GFXpolygon, 3, 4},
+    {"splinegon", GFXsplinegon, 3, 4},
+    {"arc", GFXarc, 4, 5},
+    {"text", GFXtext, 7, 8},
+    {"textsize", GFXtextsize, 4, 4},
+    {"createbitmap", GFXcreatebitmap, 2, 2},
+    {"destroybitmap", GFXdestroybitmap, 1, 1},
+    {"readbitmap", GFXreadbitmap, 2, 2},
+    {"writebitmap", GFXwritebitmap, 2, 2},
+    {"bitblt", GFXbitblt, 6, 7},
+    {"clearpick", GFXclearpick, 2, 2},
+    {"setpick", GFXsetpick, 3, 3},
+    {"displaymenu", GFXdisplaymenu, 2, 2},
+    {"txtview", TXTmode, 1, 1},
+    {"ask", TXTask, 1, 3},
+    {"dump", Idump, 0, MAXN},
+    {"copy", Icopy, 1, 1},
+    {"remove", Iremove, 1, 2},
+    {"tablesize", Itablesize, 1, 1},
+    {"openio", Iopenio, 3, 4},
+    {"closeio", Icloseio, 1, 2},
+    {"readline", Ireadline, 1, 1},
+    {"read", Iread, 1, 1},
+    {"writeline", Iwriteline, 2, 2},
+    {"atan", Iatan, 2, 2},
+    {"tan", Itan, 1, 1},
+    {"cos", Icos, 1, 1},
+    {"sin", Isin, 1, 1},
+    {"sqrt", Isqrt, 1, 1},
+    {"random", Irandom, 1, 1},
+    {"ntos", Intos, 1, 1},
+    {"ston", Iston, 1, 1},
+    {"split", Isplit, 2, 2},
+    {"concat", Iconcat, 1, MAXN},
+    {"quote", Iquote, 1, 3},
+    {"toint", Itoint, 1, 1},
+    {"strlen", Istrlen, 1, 1},
+    {"load", Iload, 1, 1},
+    {"run", Irun, 1, 1},
+    {"monitor", Imonitor, 2, 2},
+    {"idlerun", Iidlerun, 1, 1},
+    {"time", Itime, 0, 0},
+    {"sleep", Isleep, 1, 1},
+    {"echo", Iecho, 1, MAXN},
+    {"getenv", Igetenv, 1, 1},
+    {"putenv", Iputenv, 2, 2},
+    {"system", Isystem, 1, MAXN},
+    {"exit", Iexit, 0, 0},
+#ifdef FEATURE_DOT
+    {"parsegraphlabel", Iparsegraphlabel, 2, 2},
+    {"readgraph", Ireadgraph, 1, 2},
+    {"writegraph", Iwritegraph, 3, 3},
+#endif
+#ifdef FEATURE_CS
+    {"readcsmessage", C2Lreadcsmessage, 1, 1},
+#endif
+#ifdef FEATURE_GMAP
+    {"createwindow", G2Lcreatewindow, 2, 2},
+    {"destroywindow", G2Ldestroywindow, 1, 1},
+    {"setwindowattr", G2Lsetwindowattr, 2, 2},
+    {"getwindowattr", G2Lgetwindowattr, 2, 2},
+    {"createchannel", G2Lcreatechannel, 2, 2},
+    {"destroychannel", G2Ldestroychannel, 1, 1},
+    {"setchannelattr", G2Lsetchannelattr, 2, 2},
+    {"getchannelattr", G2Lgetchannelattr, 2, 2},
+    {"getchannelcoord", G2Lgetchannelcoord, 2, 2},
+    {"loadgeometry", G2Lloadgeometry, 1, 1},
+    {"unloadgeometry", G2Lunloadgeometry, 1, 1},
+    {"setgeometryattr", G2Lsetgeometryattr, 2, 2},
+    {"getgeometryattr", G2Lgetgeometryattr, 2, 2},
+    {"getgeometryitems", G2Lgetgeometryitems, 3, 3},
+    {"insertgeom2chan", G2Linsertgeom2chan, 2, 2},
+    {"removegeom2chan", G2Lremovegeom2chan, 1, 1},
+    {"loadvalue", G2Lloadvalue, 1, 1},
+    {"unloadvalue", G2Lunloadvalue, 1, 1},
+    {"setvalueattr", G2Lsetvalueattr, 2, 2},
+    {"getvalueattr", G2Lgetvalueattr, 2, 2},
+    {"getvalueitems", G2Lgetvalueitems, 2, 2},
+    {"insertval2geom", G2Linsertval2geom, 3, 3},
+    {"removeval2geom", G2Lremoveval2geom, 1, 1},
+    {"setval2geomattr", G2Lsetval2geomattr, 2, 2},
+    {"getval2geomattr", G2Lgetval2geomattr, 2, 2},
+    {"updatewindows", G2Lupdatewindows, 0, 0},
+#endif
+    {0, 0, 0, 0}
+};
+int Ifuncn;
+
+static char *bufp;
+static int bufn;
+#define BUFINCR 10240
+#define BUFSIZE sizeof (char)
+static void growbufp(int);
+
+void Iinit(void)
+{
+    int i;
+
+    if (!(bufp = malloc(BUFINCR * BUFSIZE)))
+       panic(POS, "Iinit", "buf malloc failed");
+    bufn = BUFINCR;
+    for (i = 0; Ifuncs[i].name; i++)
+       Efunction(Pfunction(Ifuncs[i].name, i), Ifuncs[i].name);
+    Ifuncn = sizeof(Ifuncs) / sizeof(Ifunc_t) - 1;
+}
+
+void Iterm(void)
+{
+    int i;
+
+    for (i = 0; i < Ifuncn; i++)
+       Tdels(root, Ifuncs[i].name);
+    Ifuncn = 0;
+    free(bufp), bufp = NULL, bufn = 0;
+}
+
+int Igetfunc(char *name)
+{
+    int i = 0;
+
+    while (Ifuncs[i].name && Strcmp(Ifuncs[i].name, name) != 0)
+       i++;
+    return (Ifuncs[i].name) ? i : -1;
+}
+
+/* display.c functions */
+
+int Idump(int argc, lvar_t * argv)
+{
+    int i;
+
+    if (argc == 0)
+       Dtrace(root, 0);
+    else
+       for (i = 0; i < argc; i++)
+           Dtrace(argv[i].o, 0);
+    return L_SUCCESS;
+}
+
+/* tbl.c functions */
+
+int Icopy(int argc, lvar_t * argv)
+{
+    rtno = Tcopy(argv[0].o);
+    return L_SUCCESS;
+}
+
+int Iremove(int argc, lvar_t * argv)
+{
+    Tobj tblo, keyo;
+
+    if (argc == 2)
+       tblo = argv[1].o, keyo = argv[0].o;
+    else
+       tblo = root, keyo = argv[0].o;
+    if (T_ISTABLE(tblo) && (T_ISNUMBER(keyo) || T_ISSTRING(keyo)))
+       Tdelo(tblo, keyo);
+    return L_SUCCESS;
+}
+
+int Itablesize(int argc, lvar_t * argv)
+{
+    Tobj vo;
+
+    if (Tgettype((vo = argv[0].o)) != T_TABLE)
+       return L_FAILURE;
+    rtno = Tinteger(((Ttable_t *) vo)->n);
+    return L_SUCCESS;
+}
+
+/* file.c functions */
+
+int Iopenio(int argc, lvar_t * argv)
+{
+    int rtn;
+
+    if (argc == 3)
+       rtn = IOopen(Tgetstring(argv[0].o),
+                    Tgetstring(argv[1].o), Tgetstring(argv[2].o), NULL);
+    else
+       rtn = IOopen(Tgetstring(argv[0].o),
+                    Tgetstring(argv[1].o), Tgetstring(argv[2].o),
+                    Tgetstring(argv[3].o));
+    rtno = NULL;
+    if (rtn == -1)
+       return L_SUCCESS;
+    rtno = Tinteger(rtn);
+    return L_SUCCESS;
+}
+
+int Icloseio(int argc, lvar_t * argv)
+{
+    if (argc == 1)
+       IOclose((int) Tgetnumber(argv[0].o), NULL);
+    else
+       IOclose((int) Tgetnumber(argv[0].o), Tgetstring(argv[1].o));
+    return L_SUCCESS;
+}
+
+int Ireadline(int argc, lvar_t * argv)
+{
+    char *s;
+    int m, n;
+
+    s = bufp, n = bufn;
+    while ((m = IOreadline((int) Tgetnumber(argv[0].o), s, n)) != -1) {
+       if (m < n - 1)
+           break;
+       m += (s - bufp);
+       growbufp(bufn + BUFINCR);
+       s = bufp + m, n = bufn - m;
+    }
+    if (m != -1)
+       rtno = Tstring(bufp);
+    else
+       rtno = NULL;
+    return L_SUCCESS;
+}
+
+int Iread(int argc, lvar_t * argv)
+{
+    if (IOread((int) Tgetnumber(argv[0].o), bufp, bufn) > 0)
+       rtno = Tstring(bufp);
+    else
+       rtno = NULL;
+    return L_SUCCESS;
+}
+
+int Iwriteline(int argc, lvar_t * argv)
+{
+    IOwriteline((int) Tgetnumber(argv[0].o), Tgetstring(argv[1].o));
+    return L_SUCCESS;
+}
+
+/* math functions */
+
+int Iatan(int argc, lvar_t * argv)
+{
+    double x, y;
+
+    y = Tgetnumber(argv[0].o), x = Tgetnumber(argv[1].o);
+    rtno = Treal(180 * atan2(y, x) / M_PI);
+    return L_SUCCESS;
+}
+
+int Itan(int argc, lvar_t * argv)
+{
+    rtno = Treal(tan(M_PI * Tgetnumber(argv[0].o) / 180.0));
+    return L_SUCCESS;
+}
+
+int Icos(int argc, lvar_t * argv)
+{
+    rtno = Treal(cos(M_PI * Tgetnumber(argv[0].o) / 180.0));
+    return L_SUCCESS;
+}
+
+int Isin(int argc, lvar_t * argv)
+{
+    rtno = Treal(sin(M_PI * Tgetnumber(argv[0].o) / 180.0));
+    return L_SUCCESS;
+}
+
+int Isqrt(int argc, lvar_t * argv)
+{
+    rtno = Treal(sqrt(Tgetnumber(argv[0].o)));
+    return L_SUCCESS;
+}
+
+/* the gnu way is to test for a feature explicitly */
+#ifndef HAVE_LRAND48
+#define lrand48 rand
+#endif
+/* the othe way is to presume a feature based on the OS */
+#if defined(FEATURE_MS) || defined(__Mac_OSX__)
+#define lrand48 rand
+#endif
+
+int Irandom(int argc, lvar_t * argv)
+{
+    rtno = Treal((Tgetnumber(argv[0].o) *
+                 (lrand48() & 0xffff)) / (double) (0xffff));
+    return L_SUCCESS;
+}
+
+/* conversion functions */
+
+int Intos(int argc, lvar_t * argv)
+{
+    double d;
+
+    d = Tgetnumber(argv[0].o);
+    if ((long) d == d)
+       sprintf(bufp, "%ld", (long) d);
+    else
+       sprintf(bufp, "%f", d);
+    rtno = Tstring(bufp);
+    return L_SUCCESS;
+}
+
+int Iston(int argc, lvar_t * argv)
+{
+    rtno = Treal((double) atof(Tgetstring(argv[0].o)));
+    return L_SUCCESS;
+}
+
+int Isplit(int argc, lvar_t * argv)
+{
+    Tobj so, fo;
+    char *sp, *sp2, *s;
+    char fc, tc, qmode;
+    long rtnm, rtni;
+    int bufi;
+
+    if (Tgettype((so = argv[0].o)) != T_STRING ||
+       Tgettype((fo = argv[1].o)) != T_STRING)
+       return L_FAILURE;
+    sp = Tgetstring(so);
+    s = Tgetstring(fo);
+    if (s[0] == '\\' && s[1] == 'n')
+       fc = '\n';
+    else
+       fc = s[0];
+    rtno = Ttable(4);
+    rtnm = Mpushmark(rtno);
+    rtni = 0;
+    if (s[0] == 0) {
+       for (sp2 = sp; *sp2; sp2++) {
+           tc = *(sp2 + 1), *(sp2 + 1) = '\000';
+           Tinsi(rtno, rtni++, Tstring(sp2));
+           *(sp2 + 1) = tc;
+       }
+    } else if (fc == ' ' || fc == '    ') {
+       while (*sp == fc)
+           sp++;
+       while (*sp) {
+           bufi = 0;
+           qmode = 0;
+           for (sp2 = sp; *sp2; sp2++) {
+               if (bufi == bufn)
+                   growbufp(bufn + BUFINCR);
+               if (*sp2 == '"' || *sp2 == '\'') {
+                   if (qmode) {
+                       if (qmode == *sp2)
+                           qmode = 0;
+                       else
+                           bufp[bufi++] = *sp2;
+                   } else
+                       qmode = *sp2;
+               } else if (*sp2 == fc && !qmode)
+                   break;
+               else
+                   bufp[bufi++] = *sp2;
+           }
+           if (bufi == bufn)
+               growbufp(bufn + BUFINCR);
+           bufp[bufi] = 0;
+           Tinsi(rtno, rtni++, Tstring(bufp));
+           while (*sp2 == fc)
+               sp2++;
+           sp = sp2;
+       }
+    } else {
+       while (*sp) {
+           for (sp2 = sp; *sp2 && *sp2 != fc; sp2++);
+           tc = *sp2, *sp2 = '\000';
+           Tinsi(rtno, rtni++, Tstring(sp));
+           *sp2 = tc;
+           if (*sp2) {
+               sp2++;
+               if (!*sp2)
+                   Tinsi(rtno, rtni++, Tstring(""));
+           }
+           sp = sp2;
+       }
+    }
+    Mpopmark(rtnm);
+    return L_SUCCESS;
+}
+
+int Iconcat(int argc, lvar_t * argv)
+{
+    Tobj ao;
+    char buf2[50];
+    char *s;
+    int i, n, bufi;
+
+    for (bufi = 0, i = 0; i < argc; i++) {
+       ao = argv[i].o;
+       switch (Tgettype(argv[i].o)) {
+       case T_STRING:
+           if (bufi + (n = strlen(Tgetstring(ao)) + 1) > bufn)
+               growbufp(bufi + n);
+           for (s = Tgetstring(ao); *s; s++)
+               bufp[bufi++] = *s;
+           break;
+       case T_INTEGER:
+           if (bufi + 50 > bufn)
+               growbufp(bufi + 50);
+           sprintf(buf2, "%ld", Tgetinteger(ao));
+           for (s = buf2; *s; s++)
+               bufp[bufi++] = *s;
+           break;
+       case T_REAL:
+           if (bufi + 50 > bufn)
+               growbufp(bufi + 50);
+           sprintf(buf2, "%f", Tgetreal(ao));
+           for (s = buf2; *s; s++)
+               bufp[bufi++] = *s;
+           break;
+       case T_CODE:
+       case T_TABLE:
+       case T_SIZE:
+           break;
+       }
+    }
+    bufp[bufi] = '\000';
+    rtno = Tstring(bufp);
+    return L_SUCCESS;
+}
+
+int Iquote(int argc, lvar_t * argv)
+{
+    Tobj so, ao = NULL, qo = NULL;
+    char *s, *s1, *s2, *qs, *as;
+    char buf2[50];
+    int n, bufi;
+    s = 0;
+
+    if ((Tgettype((so = argv[0].o)) != T_STRING && !T_ISNUMBER(so)) ||
+       (argc > 1 && Tgettype((qo = argv[1].o)) != T_STRING) ||
+       (argc > 2 && Tgettype((ao = argv[2].o)) != T_STRING))
+       return L_FAILURE;
+    switch (Tgettype(so)) {
+    case T_STRING:
+       s = Tgetstring(so);
+       break;
+    case T_INTEGER:
+       sprintf(buf2, "%ld", Tgetinteger(so));
+       s = &buf2[0];
+       break;
+    case T_REAL:
+       sprintf(buf2, "%f", Tgetreal(so));
+       s = &buf2[0];
+       break;
+    case T_CODE:
+    case T_TABLE:
+    case T_SIZE:
+       break;
+    }
+    if (argc > 1)
+       qs = Tgetstring(qo);
+    else
+       qs = "'\"";
+    if (argc > 2)
+       as = Tgetstring(ao);
+    else
+       as = NULL;
+    bufi = 0;
+    if ((n = strlen(s) + 3) * 2 > bufn)
+       growbufp(n * 2);        /* the *2 is max for chars to quote */
+    if (as)
+       bufp[bufi++] = *as;
+    for (s1 = s; *s1; s1++) {
+       for (s2 = qs; *s2; s2++)
+           if (*s1 == *s2) {
+               bufp[bufi++] = '\\', bufp[bufi++] = *s1;
+               break;
+           }
+       if (!*s2) {
+           switch (*s1) {
+           case '\n':
+               bufp[bufi++] = '\\', bufp[bufi++] = 'n';
+               break;
+           case '\r':
+               bufp[bufi++] = '\\', bufp[bufi++] = 'r';
+               break;
+           default:
+               bufp[bufi++] = *s1;
+               break;
+           }
+       }
+    }
+    if (as)
+       bufp[bufi++] = *as;
+    bufp[bufi] = '\000';
+    rtno = Tstring(bufp);
+    return L_SUCCESS;
+}
+
+int Itoint(int argc, lvar_t * argv)
+{
+    rtno = Tinteger((long) Tgetnumber(argv[0].o));
+    return L_SUCCESS;
+}
+
+int Istrlen(int argc, lvar_t * argv)
+{
+    rtno = Tinteger(strlen(Tgetstring(argv[0].o)));
+    return L_SUCCESS;
+}
+
+/* script loading functions */
+
+int Iload(int argc, lvar_t * argv)
+{
+    Psrc_t src;
+    char *fn;
+    FILE *fp;
+    Tobj co;
+
+    if ((fn = Tgetstring(argv[0].o))) {
+       if (fn[0] == '-' && fn[1] == '\000')
+           fp = stdin;
+       else {
+           fp = NULL;
+           if ((fn = buildpath(fn, FALSE)))
+               fp = fopen(fn, "r");
+       }
+       if (fp) {
+           src.flag = FILESRC, src.s = NULL, src.fp = fp;
+           src.tok = -1, src.lnum = 1;
+           while ((co = Punit(&src)))
+               Eoktorun = TRUE, Eunit(co);
+           if (fp != stdin)
+               fclose(fp);
+       } else
+           return L_FAILURE;
+    }
+    return L_SUCCESS;
+}
+
+int Irun(int argc, lvar_t * argv)
+{
+    Psrc_t src;
+    char *s;
+    Tobj co;
+
+    if ((s = Tgetstring(argv[0].o))) {
+       src.flag = CHARSRC, src.s = s, src.fp = NULL;
+       src.tok = -1, src.lnum = 1;
+       while ((co = Punit(&src)))
+           Eoktorun = TRUE, Eunit(co);
+    }
+    return L_SUCCESS;
+}
+
+/* mode setting functions */
+
+int Imonitor(int argc, lvar_t * argv)
+{
+    Tobj mo, io;
+    char *ms;
+    int ioi;
+
+    if (Tgettype((mo = argv[0].o)) != T_STRING ||
+       (Tgettype((io = argv[1].o)) != T_INTEGER &&
+        Tgettype(io) != T_REAL))
+       return L_FAILURE;
+    ms = Tgetstring(mo), ioi = Tgetnumber(io);
+    if (ioi < 0 || ioi >= ion)
+       return L_FAILURE;
+    if (Strcmp(ms, "on") == 0)
+       IOmonitor(ioi, inputfds);
+    else if (Strcmp(ms, "off") == 0)
+       IOunmonitor(ioi, inputfds);
+    else
+       return L_FAILURE;
+    return L_SUCCESS;
+}
+
+int Iidlerun(int argc, lvar_t * argv)
+{
+    Tobj mo;
+    char *ms;
+    int mode;
+
+    if (Tgettype((mo = argv[0].o)) != T_STRING)
+       return L_SUCCESS;
+    ms = Tgetstring(mo);
+    if (Strcmp(ms, "on") == 0)
+       mode = 1;
+    else if (Strcmp(ms, "off") == 0)
+       mode = 0;
+    else
+       return L_FAILURE;
+    idlerunmode = mode;
+    return L_SUCCESS;
+}
+
+/* system functions */
+
+int Itime(int argc, lvar_t * argv)
+{
+#ifndef FEATURE_MS
+    struct timeval tz;
+
+    gettimeofday(&tz, NULL);
+    rtno = Treal(tz.tv_sec + tz.tv_usec / 1000000.0);
+#else
+    rtno = Treal(0);
+#endif
+    return L_SUCCESS;
+}
+
+int Isleep(int argc, lvar_t * argv)
+{
+#ifndef FEATURE_MS
+    struct timeval tz;
+    long l;
+
+    Gsync();
+    l = Tgetnumber(argv[0].o);
+    tz.tv_sec = l;
+    tz.tv_usec = (l - tz.tv_sec) * 1000000;
+    if (select(0, NULL, NULL, NULL, &tz) == -1)
+       return L_FAILURE;
+#endif
+    return L_SUCCESS;
+}
+
+int Iecho(int argc, lvar_t * argv)
+{
+    int i;
+
+    for (i = 0; i < argc; i++) {
+       switch (Tgettype(argv[i].o)) {
+       case T_STRING:
+           printf("%s", Tgetstring(argv[i].o));
+           break;
+       case T_INTEGER:
+           printf("%ld", Tgetinteger(argv[i].o));
+           break;
+       case T_REAL:
+           printf("%f", Tgetreal(argv[i].o));
+           break;
+       case T_TABLE:
+           printf("[\n"), Dtrace(argv[i].o, 4);
+           break;
+       case T_CODE:
+       case T_SIZE:
+           break;
+       }
+    }
+    printf("\n");
+    return L_SUCCESS;
+}
+
+int Igetenv(int argc, lvar_t * argv)
+{
+    char *s;
+
+    if (!T_ISSTRING(argv[0].o))
+       return L_FAILURE;
+    if (!(s = getenv(Tgetstring(argv[0].o))) || !*s)
+       return L_SUCCESS;
+    rtno = Tstring(s);
+    return L_SUCCESS;
+}
+
+int Iputenv(int argc, lvar_t * argv)
+{
+    if (!T_ISSTRING(argv[0].o) || !T_ISSTRING(argv[1].o))
+       return L_FAILURE;
+    bufp[0] = 0;
+    strcat(bufp, Tgetstring(argv[0].o));
+    strcat(bufp, "=");
+    strcat(bufp, Tgetstring(argv[1].o));
+    putenv(bufp);
+    return L_SUCCESS;
+}
+
+int Isystem(int argc, lvar_t * argv)
+{
+    int i;
+
+    bufp[0] = 0;
+    strcat(bufp, Tgetstring(argv[0].o));
+    for (i = 1; i < argc; i++)
+       strcat(bufp, " "), strcat(bufp, Tgetstring(argv[i].o));
+#ifndef FEATURE_MS
+    system(bufp);
+#else
+    {
+       UINT handle;
+       handle = WinExec(bufp, SW_SHOW);
+    }
+#endif
+    return L_SUCCESS;
+}
+
+int Iexit(int argc, lvar_t * argv)
+{
+    longjmp(exitljbuf, 1);
+    return L_SUCCESS;          /* NOT REACHED */
+}
+
+#ifdef FEATURE_DOT
+/* dot related functions */
+
+int Iparsegraphlabel(int argc, lvar_t * argv)
+{
+    rtno = D2Lparsegraphlabel(argv[0].o, argv[1].o);
+    return L_SUCCESS;
+}
+
+int Ireadgraph(int argc, lvar_t * argv)
+{
+    int ioi;
+
+    if ((ioi = Tgetnumber(argv[0].o)) < 0 || ioi >= ion)
+       return L_FAILURE;
+    if (argc == 2 && !T_ISTABLE(argv[1].o))
+       return L_FAILURE;
+    if (!(rtno = D2Lreadgraph(ioi, (argc == 2 ? argv[1].o : NULL))))
+       return L_FAILURE;
+    return L_SUCCESS;
+}
+
+int Iwritegraph(int argc, lvar_t * argv)
+{
+    int ioi;
+
+    if (!T_ISNUMBER(argv[0].o) || !T_ISTABLE(argv[1].o) ||
+       !T_ISINTEGER(argv[2].o))
+       return L_FAILURE;
+    if ((ioi = Tgetnumber(argv[0].o)) < 0 || ioi >= ion)
+       return L_FAILURE;
+    D2Lwritegraph(ioi, argv[1].o, Tgetinteger(argv[2].o));
+    return L_SUCCESS;
+}
+#endif
+
+static void growbufp(int newsize)
+{
+    if (!(bufp = realloc(bufp,
+                        ((newsize + BUFINCR -
+                          1) / BUFINCR) * BUFINCR * BUFSIZE)))
+       panic(POS, "growbufp", "buf realloc failed");
+    bufn = ((newsize + BUFINCR - 1) / BUFINCR) * BUFINCR;
+}
diff --git a/cmd/lefty/internal.h b/cmd/lefty/internal.h
new file mode 100644 (file)
index 0000000..9d3762b
--- /dev/null
@@ -0,0 +1,42 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Lefteris Koutsofios - AT&T Bell Laboratories */
+
+#ifndef _INTERNAL_H
+#define _INTERNAL_H
+    typedef struct Ifunc_t {
+       char *name;
+       int (*func) (int, Tonm_t *);
+       int min, max;
+    } Ifunc_t;
+
+    void Iinit(void);
+    void Iterm(void);
+    int Igetfunc(char *);
+
+    extern Ifunc_t Ifuncs[];
+    extern int Ifuncn;
+#endif                         /* _INTERNAL_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/lefty.pdf b/cmd/lefty/lefty.pdf
new file mode 100644 (file)
index 0000000..43d218a
Binary files /dev/null and b/cmd/lefty/lefty.pdf differ