]> 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)
13 files changed:
cmd/lefty/os/unix/.cvsignore [new file with mode: 0644]
cmd/lefty/os/unix/Makefile.am [new file with mode: 0644]
cmd/lefty/os/unix/io.c [new file with mode: 0644]
cmd/lefty/parse.c [new file with mode: 0644]
cmd/lefty/parse.h [new file with mode: 0644]
cmd/lefty/str.c [new file with mode: 0644]
cmd/lefty/str.h [new file with mode: 0644]
cmd/lefty/tbl.c [new file with mode: 0644]
cmd/lefty/tbl.h [new file with mode: 0644]
cmd/lefty/txtview.c [new file with mode: 0644]
cmd/lefty/txtview.h [new file with mode: 0644]
cmd/lefty/ws/.cvsignore [new file with mode: 0644]
cmd/lefty/ws/Makefile.am [new file with mode: 0644]

diff --git a/cmd/lefty/os/unix/.cvsignore b/cmd/lefty/os/unix/.cvsignore
new file mode 100644 (file)
index 0000000..9fb9857
--- /dev/null
@@ -0,0 +1,6 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
diff --git a/cmd/lefty/os/unix/Makefile.am b/cmd/lefty/os/unix/Makefile.am
new file mode 100644 (file)
index 0000000..a3b31cc
--- /dev/null
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir)/lefty
+
+if WITH_X
+noinst_LTLIBRARIES = libos.la
+endif
+
+libos_la_SOURCES = io.c
+
+EXTRA_DIST = $(libos_la_SOURCES)
diff --git a/cmd/lefty/os/unix/io.c b/cmd/lefty/os/unix/io.c
new file mode 100644 (file)
index 0000000..338460e
--- /dev/null
@@ -0,0 +1,439 @@
+/* $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 "io.h"
+#ifdef FEATURE_CS
+#include "cs2l.h"
+#endif
+#include "mem.h"
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+#ifndef HAVE_TERMIOS_H
+#include <termio.h>
+#else
+#include <termios.h>
+#endif
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef HAVEVFORK
+#define FORK vfork
+#else
+#define FORK fork
+#endif
+#include <netdb.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+io_t *iop;
+int ion;
+
+static char *shell;
+static char *shbname;
+
+static FILE *serverconnect(char *);
+static void ptyopen(char *, FILE **, FILE **, int *);
+static int findpty(int *);
+static void pipeopen(char *, FILE **, FILE **, int *);
+static void socketopen(char *, int, FILE **, FILE **, int *);
+
+static void sigchldhandler(int);
+
+void IOinit(void)
+{
+    struct stat statbuf;
+    int ioi;
+
+    if (!(shell = getenv("SHELL")))
+       shell = "/bin/sh";
+    if (shell[0] != '/' && shell[0] != '.') {
+       if (!(shell = buildpath(shell, TRUE)))
+           shell = "/bin/sh";
+       else
+           shell = strdup(shell);
+    }
+    shbname = shell + strlen(shell) - 1;
+    while (shbname >= shell && *shbname != '/')
+       shbname--;
+    if (*shbname == '/')
+       shbname++;
+    ion = IOINCR;
+    for (ioi = FD_SETSIZE - 1; ioi >= 0; ioi--) {
+       if (fstat(ioi, &statbuf) == 0) {
+           ion = (ioi / IOINCR + 1) * IOINCR;
+           break;
+       }
+    }
+    iop = Marrayalloc((long) ion * IOSIZE);
+    for (ioi = 0; ioi < ion; ioi++)
+       iop[ioi].inuse = FALSE;
+    for (ioi = 0; ioi < ion; ioi++) {
+       if (fstat(ioi, &statbuf) == 0) {
+           if ((iop[ioi].ifp = iop[ioi].ofp = fdopen(ioi,
+                                                     (ioi ==
+                                                      0 ? "r" : "w")))) {
+               iop[ioi].inuse = TRUE;
+               iop[ioi].type = IO_FILE;
+               iop[ioi].ismonitored = FALSE;
+               iop[ioi].pid = -1;
+               iop[ioi].buf = NULL;
+           }
+       }
+    }
+    signal(SIGCHLD, sigchldhandler);
+    signal(SIGPIPE, SIG_IGN);
+}
+
+void IOterm(void)
+{
+    int ioi;
+
+    for (ioi = 3; ioi < ion; ioi++)
+       if (iop[ioi].inuse)
+           IOclose(ioi, NULL);
+    Marrayfree(iop), iop = NULL, ion = 0;
+}
+
+int IOopen(char *kind, char *name, char *mode, char *fmt)
+{
+    io_t *p;
+    iotype_t type;
+    char *path, *command;
+    char hname[200];
+    int sfd;
+    socklen_t slen;
+    int i;
+    struct sockaddr_in sname;
+
+    if (Strcmp(kind, "file") == 0)
+       type = IO_FILE;
+    else if (Strcmp(kind, "pty") == 0)
+       type = IO_PTY;
+    else if (Strcmp(kind, "pipe") == 0)
+       type = IO_PIPE;
+    else if (Strcmp(kind, "socket") == 0)
+       type = IO_SOCKET;
+#ifdef FEATURE_CS
+    else if (Strcmp(kind, "cs") == 0)
+       type = IO_CS;
+#endif
+    else
+       return -1;
+    for (i = 0; i < ion; i++)
+       if (!iop[i].inuse)
+           break;
+    if (i == ion) {
+       iop = Marraygrow(iop, (long) (ion + IOINCR) * IOSIZE);
+       for (i = ion + IOINCR - 1; i >= ion; i--)
+           iop[i].inuse = FALSE;
+       i++, ion += IOINCR;
+    }
+    p = &iop[i];
+    p->type = type;
+    p->pid = -1;
+    switch (type) {
+    case IO_FILE:
+       if (!(p->ifp = p->ofp = fopen(name, mode))) {
+           if (strncmp(name, "/dev/tcp/", 9) == 0) {
+               if (!(p->ifp = p->ofp = serverconnect(name)))
+                   return -1;
+               break;
+           }
+           path = buildpath(name, FALSE);
+           if (!path || !(p->ifp = p->ofp = fopen(path, mode)))
+               return -1;
+       }
+       break;
+    case IO_PTY:
+       if (!fmt)
+           fmt = "%e";
+       if (!(path = buildpath(name, TRUE)) ||
+           !(command = buildcommand(path, NULL, -1, -1, fmt)))
+           return -1;
+       ptyopen(command, &p->ifp, &p->ofp, &p->pid);
+       if (!p->ifp || !p->ofp)
+           return -1;
+       break;
+    case IO_PIPE:
+       if (!fmt)
+           fmt = "%e";
+       if (!(path = buildpath(name, TRUE)) ||
+           !(command = buildcommand(path, NULL, -1, -1, fmt)))
+           return -1;
+       pipeopen(command, &p->ifp, &p->ofp, &p->pid);
+       if (!p->ifp || !p->ofp)
+           return -1;
+       break;
+    case IO_SOCKET:
+       if (!fmt)
+           fmt = "%e";
+       if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+           return -1;
+       sname.sin_family = AF_INET;
+       sname.sin_port = 0;
+       sname.sin_addr.s_addr = htonl(INADDR_ANY);
+       slen = sizeof(sname);
+       if (bind(sfd, (struct sockaddr *) &sname, slen) < 0 ||
+           getsockname(sfd, (struct sockaddr *) &sname, &slen) < 0)
+           return -1;
+       if (listen(sfd, 5) < 0)
+           return -1;
+       gethostname(hname, sizeof(hname));
+       if (!(path = buildpath(name, TRUE)) ||
+           !(command = buildcommand(path, hname,
+                                    (int) ntohs(sname.sin_port),
+                                    (int) ntohs(sname.sin_port), fmt)))
+           return -1;
+       socketopen(command, sfd, &p->ifp, &p->ofp, &p->pid);
+       if (!p->ifp || !p->ofp)
+           return -1;
+       close(sfd);
+       break;
+#ifdef FEATURE_CS
+    case IO_CS:
+       if (C2Lopen(name, mode, &p->ifp, &p->ofp) == -1)
+           return -1;
+       break;
+#endif
+    default:
+       break;
+    }
+    p->inuse = TRUE;
+    FD_CLR(fileno(p->ifp), &inputfds);
+    FD_CLR(fileno(p->ofp), &inputfds);
+    return i;
+}
+
+int IOclose(int ioi, char *action)
+{
+    io_t *p;
+
+    if (ioi < 0 || ioi >= ion || !iop[ioi].inuse)
+       return -1;
+    p = &iop[ioi];
+    FD_CLR(fileno(p->ifp), &inputfds);
+    FD_CLR(fileno(p->ofp), &inputfds);
+    if (p->ifp != p->ofp)
+       fclose(p->ifp);
+    fclose(p->ofp);
+    p->inuse = FALSE;
+    if (action && Strcmp(action, "kill") == 0 && p->pid != -1)
+       kill(p->pid, 15);
+    return 0;
+}
+
+int IOreadline(int ioi, char *bufp, int bufn)
+{
+    io_t *p;
+    int l;
+
+    if (ioi < 0 || ioi >= ion || !iop[ioi].inuse)
+       return -1;
+    p = &iop[ioi];
+    fseek(p->ofp, 0L, 1);
+    if (fgets(bufp, bufn, p->ifp) == NULL)
+       return -1;
+    l = strlen(bufp) - 1;
+    while (bufp[l] == '\n' || bufp[l] == '\r')
+       bufp[l--] = '\000';
+    return l + 1;
+}
+
+int IOread(int ioi, char *bufp, int bufn)
+{
+    io_t *p;
+    int l;
+
+    if (ioi < 0 || ioi >= ion || !iop[ioi].inuse)
+       return -1;
+    p = &iop[ioi];
+    if ((l = read(fileno(p->ifp), bufp, bufn - 1)) == -1)
+       return -1;
+    else if (l == 0)
+       return 0;
+    bufp[l] = '\000';
+    return l;
+}
+
+int IOwriteline(int ioi, char *bufp)
+{
+    io_t *p;
+
+    if (ioi < 0 || ioi >= ion || !iop[ioi].inuse)
+       return -1;
+    p = &iop[ioi];
+    fseek(p->ofp, 0L, 1);
+    if (fputs(bufp, p->ofp) == EOF || fputs("\n", p->ofp) == EOF)
+       return -1;
+    fflush(p->ofp);
+    fseek(p->ofp, 0L, 1);
+    return 0;
+}
+
+static FILE *serverconnect(char *name)
+{
+    char *host, *portp, buf[1024];
+    int port;
+    struct hostent *hp;
+    struct sockaddr_in sin;
+    int cfd;
+
+    strcpy(buf, name);
+    host = buf + 9;
+    portp = strchr(host, '/');
+    if (*host == 0 || !portp)
+       return NULL;
+    *portp++ = 0, port = atoi(portp);
+    if (!(hp = gethostbyname(host)))
+       return NULL;
+    memset((char *) &sin, 1, sizeof(sin));
+    memcpy((char *) &sin.sin_addr, hp->h_addr, hp->h_length);
+    sin.sin_family = hp->h_addrtype;
+    sin.sin_port = htons(port);
+    if ((cfd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
+       return NULL;
+    if (connect(cfd, (struct sockaddr *) &sin, sizeof(sin)) < 0)
+       return NULL;
+    return fdopen(cfd, "w+");
+}
+
+static void ptyopen(char *cmd, FILE ** ifp, FILE ** ofp, int *pidp)
+{
+    int fd[2];
+
+    if (findpty(fd) == -1) {
+       *ifp = NULL;
+       return;
+    }
+    switch ((*pidp = FORK())) {
+    case -1:
+       panic2(POS, "ptyopen", "cannot fork");
+    case 0:
+       close(fd[0]), close(0), dup(fd[1]);
+       close(1), dup(fd[1]), close(fd[1]);
+       execl(shell, shbname, "-c", cmd, (void *) NULL);
+       panic2(POS, "ptyopen", "child cannot exec: %s\n", cmd);
+    default:
+       close(fd[1]);
+    }
+    fcntl(fd[0], F_SETFD, FD_CLOEXEC);
+    *ifp = fdopen(fd[0], "r"), *ofp = fdopen(fd[0], "a+");
+    return;
+}
+
+static int findpty(int *fd)
+{
+    char *majorp, *minorp;
+    char pty[32], tty[32];
+#ifndef HAVE_TERMIOS_H
+    struct termio tio;
+#else
+    struct termios tio;
+#endif
+    static char ptymajor[] = "pqrs";
+    static char ptyminor[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+    for (majorp = ptymajor; *majorp; majorp++) {
+       for (minorp = ptyminor; *minorp; minorp++) {
+           sprintf(pty, "/dev/pty%c%c", *majorp, *minorp);
+           if ((fd[0] = open(pty, O_RDWR)) >= 0) {
+               sprintf(tty, "/dev/tty%c%c", *majorp, *minorp);
+               if ((fd[1] = open(tty, O_RDWR)) >= 0) {
+#ifndef HAVE_TERMIOS_H
+                   ioctl(fd[1], TCGETA, &tio);
+                   tio.c_lflag &= ~ECHO;
+                   ioctl(fd[1], TCSETA, &tio);
+#else
+                   tcgetattr(fd[1], &tio);
+                   tio.c_lflag &= ~ECHO;
+                   tcsetattr(fd[1], TCSANOW, &tio);
+#endif
+                   return 0;
+               }
+               close(fd[0]);
+           }
+       }
+    }
+    return -1;
+}
+
+static void pipeopen(char *cmd, FILE ** ifp, FILE ** ofp, int *pidp)
+{
+    int p1[2], p2[2];
+    char cmd2[1024];
+    char *s;
+
+    if (pipe(p1) == -1 || pipe(p2) == -1) {
+       *ifp = NULL;
+       return;
+    }
+    switch ((*pidp = FORK())) {
+    case -1:
+       panic2(POS, "pipeopen", "cannot fork");
+    case 0:
+       close(p1[0]), close(p2[1]);
+       for (s = cmd; *s; s++)
+           if (*s == '%' && *(s + 1) && *(s + 1) == 'd') {
+               sprintf(cmd2, cmd, p2[0], p1[1]);
+               execl(shell, shbname, "-c", cmd2, (void *) NULL);
+               panic2(POS, "pipeopen", "child cannot exec: %s\n", cmd2);
+           }
+       close(1), dup(p1[1]), close(p1[1]);
+       close(0), dup(p2[0]), close(p2[0]);
+       execl(shell, shbname, "-c", cmd, NULL);
+       panic2(POS, "pipeopen", "child cannot exec: %s\n", cmd);
+    default:
+       close(p1[1]), close(p2[0]);
+    }
+    fcntl(p1[0], F_SETFD, FD_CLOEXEC);
+    fcntl(p2[1], F_SETFD, FD_CLOEXEC);
+    *ifp = fdopen(p1[0], "r"), *ofp = fdopen(p2[1], "a");
+    return;
+}
+
+static void socketopen(char *cmd, int sfd, FILE ** ifp, FILE ** ofp,
+                      int *pidp)
+{
+    int fd;
+
+    switch ((*pidp = FORK())) {
+    case -1:
+       panic2(POS, "socketopen", "cannot fork");
+    case 0:
+       execl(shell, shbname, "-c", cmd, NULL);
+       panic2(POS, "socketopen", "child cannot exec: %s\n", cmd);
+    default:
+       if ((fd = accept(sfd, NULL, NULL)) < 0) {
+           *ifp = NULL;
+           return;
+       }
+    }
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+    *ifp = fdopen(fd, "r"), *ofp = fdopen(fd, "a+");
+    return;
+}
+
+static void sigchldhandler(int data)
+{
+    while (waitpid(-1, NULL, WNOHANG) > 0);
+    signal(SIGCHLD, sigchldhandler);
+}
diff --git a/cmd/lefty/parse.c b/cmd/lefty/parse.c
new file mode 100644 (file)
index 0000000..406066d
--- /dev/null
@@ -0,0 +1,685 @@
+/* $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 "lex.h"
+#include "parse.h"
+#include "internal.h"
+
+static jmp_buf eljbuf;
+
+#define GTOKIFEQ(t) { \
+    if (Ltok == (t)) \
+        Lgtok (); \
+    else \
+        err ("expected token: '%s', found: '%s'", Lnames[t], Lnames[Ltok]); \
+}
+
+typedef struct lv_t {
+    int si, vi;
+} lv_t;
+static lv_t *lvp;
+static int lvn, flvi, llvi;
+#define GETLVSTR(i) (char *) Cgetstring (lvp[i].si)
+#define GETLVNUM(i) lvp[i].vi
+#define LVINCR 1000
+#define LVSIZE sizeof (lv_t)
+
+static int pexpr(void);
+static int getop(int, int);
+static int pexpi(int);
+static int pexp5(void);
+static int pexp6(void);
+static int pexp7(void);
+static int pargs(void);
+static int pcons(void);
+static int pvar(void);
+static int pfunc(void);
+static int pdecl(int *);
+static int ptcons(void);
+static int pstmt(void);
+static int pifst(void);
+static int pwhilest(void);
+static int pforst(void);
+static int pbreakst(void);
+static int pcontinuest(void);
+static int preturnst(void);
+
+static void addlv(int, int);
+static void err(char *, ...);
+
+void Pinit(void)
+{
+    lvp = Marrayalloc((long) LVINCR * LVSIZE);
+    lvn = LVINCR;
+    flvi = llvi = 0;
+}
+
+void Pterm(void)
+{
+    Marrayfree(lvp);
+    lvp = NULL;
+    lvn = flvi = llvi = 0;
+}
+
+Tobj Punit(Psrc_t * sp)
+{
+    int ui, ei;
+
+    Lsetsrc(sp->flag, sp->s, sp->fp, sp->tok, sp->lnum);
+    Creset();
+    flvi = llvi = 0;
+
+    if (setjmp(eljbuf) != 0)
+       return NULL;
+
+    while (Ltok == L_SEMI)
+       Lgtok();
+    if (Ltok == L_EOF)
+       return NULL;
+
+    ui = Cnew(C_CODE);
+    ei = pexpr();
+    Csetfp(ui, ei);
+    Lgetsrc(&sp->flag, &sp->s, &sp->fp, &sp->tok, &sp->lnum);
+    return Tcode(cbufp, 0, cbufi);
+}
+
+/* shortcut: this function creates a piece of code that corresponds to
+   <internal func name> = function () internal "<internal func name>";
+*/
+Tobj Pfunction(char *ifnam, int ifnum)
+{
+    int ui, ai, vi, si, fi, li1, li2, di, ifi, ifn;
+
+    Creset();
+    ui = Cnew(C_CODE);
+    ai = Cnew(C_ASSIGN);
+    Csetfp(ui, ai);
+    vi = Cnew(C_GVAR);
+    si = Cstring(ifnam);
+    Csetfp(vi, si);
+    Csetfp(ai, vi);
+    fi = Cnew(C_FUNCTION);
+    Csetnext(vi, fi);
+    li1 = Cinteger(0);
+    Csetfp(fi, li1);
+    li2 = Cinteger(0);
+    Csetnext(li1, li2);
+    di = Cnew(C_DECL);
+    Csetfp(di, C_NULL);
+    Csetnext(li2, di);
+    ifi = Cnew(C_INTERNAL);
+    ifn = Cinteger((long) ifnum);
+    Csetfp(ifi, ifn);
+    Csetnext(di, ifi);
+    Csetinteger(li1, (long) (Cgetindex() - fi));
+    Csetinteger(li2, 0);
+    return Tcode(cbufp, 0, cbufi);
+}
+
+/* shortcut: this function creates a piece of code that corresponds to
+   <func name> (<args>); where <args> is the second argument (ao)
+*/
+Tobj Pfcall(Tobj fo, Tobj ao)
+{
+    int ui, fi, ffi, ai, aai;
+
+    Creset();
+    ui = Cnew(C_CODE);
+    fi = Cnew(C_FCALL);
+    Csetfp(ui, fi);
+    ffi = Cnew(C_PVAR);
+    Csetobject(ffi, fo);
+    Csetfp(fi, ffi);
+    ai = Cnew(C_ARGS);
+    Csetnext(ffi, ai);
+    if (ao) {
+       aai = Cnew(C_PVAR);
+       Csetobject(aai, ao);
+       Csetfp(ai, aai);
+    } else
+       Csetfp(ai, C_NULL);
+    return Tcode(cbufp, 0, cbufi);
+}
+
+static int pexpr(void)
+{
+    int ai, ei0, ei1;
+
+    ei0 = pexpi(0);
+    if (Ltok != C_ASSIGN)
+       return ei0;
+
+    ai = Cnew(C_ASSIGN);
+    Csetfp(ai, ei0);
+    Lgtok();
+    ei1 = pexpr();
+    Csetnext(ei0, ei1);
+    return ai;
+}
+
+static int lextab[][7] = {
+    {L_OR, 0, 0, 0, 0, 0, 0},
+    {L_AND, 0, 0, 0, 0, 0, 0},
+    {L_EQ, L_NE, L_LT, L_LE, L_GT, L_GE, 0},
+    {L_PLUS, L_MINUS, 0, 0, 0, 0, 0},
+    {L_MUL, L_DIV, L_MOD, 0, 0, 0, 0},
+    {0, 0, 0, 0, 0, 0, 0}
+};
+
+static int parsetab[][7] = {
+    {C_OR, 0, 0, 0, 0, 0, 0},
+    {C_AND, 0, 0, 0, 0, 0, 0},
+    {C_EQ, C_NE, C_LT, C_LE, C_GT, C_GE, 0},
+    {C_PLUS, C_MINUS, 0, 0, 0, 0, 0},
+    {C_MUL, C_DIV, C_MOD, 0, 0, 0, 0},
+    {0, 0, 0, 0, 0, 0, 0}
+};
+
+static int getop(int t, int i)
+{
+    int j;
+
+    for (j = 0; lextab[i][j] != 0; j++)
+       if (t == lextab[i][j])
+           return parsetab[i][j];
+    return -1;
+}
+
+static int pexpi(int k)
+{
+    int ei0, ei1, ei2, ptok;
+
+    if (lextab[k][0] == 0)
+       return pexp5();
+
+    ei0 = pexpi(k + 1);
+    while ((ptok = getop(Ltok, k)) != -1) {
+       ei1 = Cnew((Ctype_t) ptok);
+       Csetfp(ei1, ei0);
+       Lgtok();
+       ei2 = pexpi(k + 1);
+       Csetnext(ei0, ei2);
+       ei0 = ei1;
+    }
+    return ei0;
+}
+
+static int pexp5(void)
+{
+    int ei0, ei1;
+
+    if (Ltok == L_MINUS) {
+       ei0 = Cnew(C_UMINUS);
+       Lgtok();
+       ei1 = pexp5();
+       Csetfp(ei0, ei1);
+       return ei0;
+    }
+    return pexp6();
+}
+
+static int pexp6(void)
+{
+    int ei0, ei1;
+
+    if (Ltok == L_NOT) {
+       ei0 = Cnew(C_NOT);
+       Lgtok();
+       ei1 = pexp6();
+       Csetfp(ei0, ei1);
+       return ei0;
+    }
+    return pexp7();
+}
+
+static int pexp7(void)
+{
+    int ei0 = 0, ei1, ei2;
+
+    switch (Ltok) {
+    case L_FUNCTION:
+       Lgtok();
+       ei0 = pfunc();
+       break;
+    case L_LP:
+       ei0 = Cnew(C_PEXPR);
+       Lgtok();
+       ei1 = pexpr();
+       GTOKIFEQ(L_RP);
+       Csetfp(ei0, ei1);
+       break;
+    case L_LB:
+       ei0 = ptcons();
+       break;
+    case L_STRING:
+    case L_NUMBER:
+       ei0 = pcons();
+       break;
+    case L_ID:
+       ei0 = pvar();
+       if (Ltok == L_LP) {     /* ie: it's really a function call */
+           ei1 = ei0;
+           ei0 = Cnew(C_FCALL);
+           Csetfp(ei0, ei1);
+           Lgtok();
+           ei2 = pargs();
+           Csetnext(ei1, ei2);
+           GTOKIFEQ(L_RP);
+       }
+       break;
+    default:
+       err("expected EXP7 type token, found: %s", Lnames[Ltok]);
+    }
+    return ei0;
+}
+
+static int pargs(void)
+{
+    int ai, ei0, ei1;
+
+    ai = Cnew(C_ARGS);
+    if (Ltok == L_RP) {
+       Csetfp(ai, C_NULL);
+       return ai;
+    }
+    ei0 = pexpr();
+    Csetfp(ai, ei0);
+    while (Ltok != L_RP) {
+       GTOKIFEQ(L_COMMA);
+       if (Ltok == L_RP)
+           err("expected expression, found: %s", Lnames[Ltok]);
+
+       ei1 = pexpr();
+       Csetnext(ei0, ei1);
+       ei0 = ei1;
+    }
+    return ai;
+}
+
+static int pcons(void)
+{
+    int ci = 0;
+    double d;
+
+    switch (Ltok) {
+    case L_NUMBER:
+       d = atof(Lstrtok);
+       ci = (d == (double) (long) d) ? Cinteger((long) d) : Creal(d);
+       break;
+    case L_STRING:
+       ci = Cstring(Lstrtok);
+       break;
+    default:
+       err("expected scalar constant, found: %s", Lnames[Ltok]);
+    }
+    Lgtok();
+    return ci;
+}
+
+static int pvar(void)
+{
+    int vi, ci0, ci1, i;
+
+    vi = Cnew(C_GVAR);
+    ci0 = Cstring(Lstrtok);
+    Csetfp(vi, ci0);
+    for (i = flvi; i < llvi; i++) {
+       if (Strcmp(GETLVSTR(i), Lstrtok) == 0) {
+           Csettype(vi, C_LVAR);
+           ci1 = Cinteger((long) GETLVNUM(i));
+           Csetnext(ci0, ci1);
+           ci0 = ci1;
+           break;
+       }
+    }
+    Lgtok();
+    if (Ltok != L_DOT && Ltok != L_LB)
+       return vi;
+
+    while (Ltok == L_DOT || Ltok == L_LB) {
+       if (Ltok == L_DOT) {
+           Lgtok();
+           if (Ltok != L_ID)
+               err("expected identifier, found: %s", Lnames[Ltok]);
+           ci1 = Cstring(Lstrtok);
+           Csetnext(ci0, ci1);
+           Lgtok();
+       } else {
+           Lgtok();
+           ci1 = pexpr();
+           Csetnext(ci0, ci1);
+           GTOKIFEQ(L_RB);
+       }
+       ci0 = ci1;
+    }
+    return vi;
+}
+
+static int pfunc(void)
+{
+    int fi, di, si, ifi, ifn, ldi, i, li1, li2;
+    int owncbufi, ownflvi, ownllvi, flvn, ifnum;
+
+    owncbufi = Cgetindex();
+    ownflvi = flvi, ownllvi = llvi;
+    flvi = llvi;
+    flvn = 0;
+
+    fi = Cnew(C_FUNCTION);
+    GTOKIFEQ(L_LP);
+    li1 = Cinteger(0);
+    Csetfp(fi, li1);
+    li2 = Cinteger(0);
+    Csetnext(li1, li2);
+    di = pdecl(&flvn);
+    Csetnext(li2, di);
+    i = di;
+    GTOKIFEQ(L_RP);
+    if (Ltok == L_INTERNAL) {
+       Lgtok();
+       if (Ltok == L_STRING) {
+           if ((ifnum = Igetfunc(Lstrtok)) == -1)
+               err("no such internal function: %s", Lstrtok);
+           ifi = Cnew(C_INTERNAL);
+           ifn = Cinteger((long) ifnum);
+           Csetfp(ifi, ifn);
+           Csetnext(i, ifi);
+           Lgtok();
+       } else
+           err("expected token: STRING, found: '%s'", Lnames[Ltok]);
+    } else {
+       GTOKIFEQ(L_LCB);
+       while (Ltok == L_LOCAL) {
+           Lgtok();
+           ldi = pdecl(&flvn);
+           Csetnext(i, ldi);
+           i = ldi;
+           GTOKIFEQ(L_SEMI);
+       }
+       while (Ltok != L_RCB) {
+           si = pstmt();
+           Csetnext(i, si);
+           i = si;
+       }
+       GTOKIFEQ(L_RCB);
+    }
+    Csetinteger(li1, (long) (Cgetindex() - owncbufi));
+    Csetinteger(li2, (long) flvn);
+    flvi = ownflvi, llvi = ownllvi;
+    return fi;
+}
+
+static int pdecl(int *lvnp)
+{
+    int di, si, i;
+
+    di = Cnew(C_DECL);
+    if (Ltok != L_ID) {
+       Csetfp(di, C_NULL);
+       return di;
+    }
+    si = Cstring(Lstrtok);
+    addlv(si, (*lvnp)++);
+    Csetfp(di, si);
+    i = si;
+    Lgtok();
+    if (Ltok != L_COMMA)
+       return di;
+    Lgtok();
+    while (Ltok == L_ID) {
+       si = Cstring(Lstrtok);
+       addlv(si, (*lvnp)++);
+       Lgtok();
+       Csetnext(i, si);
+       i = si;
+       if (Ltok == L_COMMA) {
+           Lgtok();
+           if (Ltok != L_ID)
+               err("expected identifier, found %s", Lnames[Ltok]);
+       }
+    }
+    return di;
+}
+
+static int ptcons(void)
+{
+    int ti, ei0, ei1;
+
+    ti = Cnew(C_TCONS);
+    Lgtok();
+    if (Ltok == L_RB) {
+       Csetfp(ti, C_NULL);
+       Lgtok();
+       return ti;
+    }
+    ei1 = pexpi(0);
+    Csetfp(ti, ei1);
+    ei0 = ei1;
+    GTOKIFEQ(L_ASSIGN);
+    ei1 = pexpr();
+    Csetnext(ei0, ei1);
+    ei0 = ei1;
+    GTOKIFEQ(L_SEMI);
+    while (Ltok != L_RB) {
+       ei1 = pexpi(0);
+       Csetnext(ei0, ei1);
+       ei0 = ei1;
+       GTOKIFEQ(L_ASSIGN);
+       ei1 = pexpr();
+       Csetnext(ei0, ei1);
+       ei0 = ei1;
+       GTOKIFEQ(L_SEMI);
+    }
+    Lgtok();
+    return ti;
+}
+
+static int pstmt(void)
+{
+    int si, i0, i1;
+
+    si = Cnew(C_STMT);
+    switch (Ltok) {
+    case L_SEMI:
+       Csetfp(si, C_NULL);
+       Lgtok();
+       break;
+    case L_LCB:
+       Lgtok();
+       if (Ltok == L_RCB) {
+           Csetfp(si, C_NULL);
+       } else {
+           i1 = pstmt();
+           Csetfp(si, i1);
+           i0 = i1;
+           while (Ltok != L_RCB) {
+               i1 = pstmt();
+               Csetnext(i0, i1);
+               i0 = i1;
+           }
+       }
+       Lgtok();
+       break;
+    case L_IF:
+       i0 = pifst();
+       Csetfp(si, i0);
+       break;
+    case L_WHILE:
+       i0 = pwhilest();
+       Csetfp(si, i0);
+       break;
+    case L_FOR:
+       i0 = pforst();
+       Csetfp(si, i0);
+       break;
+    case L_BREAK:
+       i0 = pbreakst();
+       Csetfp(si, i0);
+       break;
+    case L_CONTINUE:
+       i0 = pcontinuest();
+       Csetfp(si, i0);
+       break;
+    case L_RETURN:
+       i0 = preturnst();
+       Csetfp(si, i0);
+       break;
+    default:
+       i0 = pexpr();
+       Csetfp(si, i0);
+       GTOKIFEQ(L_SEMI);
+    }
+    return si;
+}
+
+static int pifst(void)
+{
+    int isi, ii, ti, ei;
+
+    isi = Cnew(C_IF);
+    Lgtok();
+    GTOKIFEQ(L_LP);
+    ii = pexpr();
+    Csetfp(isi, ii);
+    GTOKIFEQ(L_RP);
+    ti = pstmt();
+    Csetnext(ii, ti);
+    if (Ltok == L_ELSE) {
+       Lgtok();
+       ei = pstmt();
+       Csetnext(ti, ei);
+    }
+    return isi;
+}
+
+static int pwhilest(void)
+{
+    int wi, ei, si;
+
+    wi = Cnew(C_WHILE);
+    Lgtok();
+    GTOKIFEQ(L_LP);
+    ei = pexpr();
+    Csetfp(wi, ei);
+    GTOKIFEQ(L_RP);
+    si = pstmt();
+    Csetnext(ei, si);
+    return wi;
+}
+
+static int pforst(void)
+{
+    int fi, i0, i1, si;
+
+    fi = Cnew(C_FOR);
+    Lgtok();
+    GTOKIFEQ(L_LP);
+    i0 = (Ltok == L_SEMI) ? Cnew(C_NOP) : pexpr();
+    Csetfp(fi, i0);
+    if (Ltok == L_IN) {
+       Csettype(fi, C_FORIN);
+       Lgtok();
+       i1 = pexpr();
+       Csetnext(i0, i1);
+       i0 = i1;
+    } else {
+       GTOKIFEQ(L_SEMI);
+       i1 = (Ltok == L_SEMI) ? Cnew(C_NOP) : pexpr();
+       Csetnext(i0, i1);
+       i0 = i1;
+       GTOKIFEQ(L_SEMI);
+       i1 = (Ltok == L_SEMI) ? Cnew(C_NOP) : pexpr();
+       Csetnext(i0, i1);
+       i0 = i1;
+    }
+    GTOKIFEQ(L_RP);
+    si = pstmt();
+    Csetnext(i0, si);
+    return fi;
+}
+
+static int pbreakst(void)
+{
+    int bi;
+
+    bi = Cnew(C_BREAK);
+    Csetfp(bi, C_NULL);
+    Lgtok();
+    GTOKIFEQ(L_SEMI);
+    return bi;
+}
+
+static int pcontinuest(void)
+{
+    int ci;
+
+    ci = Cnew(C_CONTINUE);
+    Csetfp(ci, C_NULL);
+    Lgtok();
+    GTOKIFEQ(L_SEMI);
+    return ci;
+}
+
+static int preturnst(void)
+{
+    int ri, ei;
+
+    ri = Cnew(C_RETURN);
+    Lgtok();
+    if (Ltok == L_SEMI) {
+       Csetfp(ri, C_NULL);
+       GTOKIFEQ(L_SEMI);
+       return ri;
+    }
+    ei = pexpr();
+    Csetfp(ri, ei);
+    GTOKIFEQ(L_SEMI);
+    return ri;
+}
+
+static void addlv(int si, int vi)
+{
+    int i;
+
+    if (llvi >= lvn) {
+       lvp = Marraygrow(lvp, (long) (lvn + LVINCR) + LVSIZE);
+       lvn += LVINCR;
+    }
+    lvp[llvi].si = si, lvp[llvi].vi = vi, llvi++;
+    for (i = llvi - 2; i >= flvi; i--)
+       if (Strcmp(GETLVSTR(i), GETLVSTR(llvi - 1)) == 0)
+           err("local variable %s multiply defined", GETLVSTR(i));
+}
+
+static void err(char *fmt, ...)
+{
+    va_list args;
+
+    va_start(args, fmt);
+    Lprintpos();
+    vfprintf(stderr, fmt, args);
+    fprintf(stderr, "\n");
+    fflush(stdout);
+    longjmp(eljbuf, 1);
+}
diff --git a/cmd/lefty/parse.h b/cmd/lefty/parse.h
new file mode 100644 (file)
index 0000000..191fd86
--- /dev/null
@@ -0,0 +1,43 @@
+/* $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 _PARSE_H
+#define _PARSE_H
+    typedef struct Psrc_t {
+       int flag;
+       char *s;
+       FILE *fp;
+       int tok;
+       int lnum;
+    } Psrc_t;
+
+    void Pinit(void);
+    void Pterm(void);
+    Tobj Punit(Psrc_t *);
+    Tobj Pfcall(Tobj, Tobj);
+    Tobj Pfunction(char *, int);
+#endif                         /* _PARSE_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/str.c b/cmd/lefty/str.c
new file mode 100644 (file)
index 0000000..a355907
--- /dev/null
@@ -0,0 +1,554 @@
+/* $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 "internal.h"
+
+static int highci;
+static int indent;
+#define INDINC(i) (indent += (i))
+#define INDDEC(i) (indent -= (i))
+
+static char *sbufp;
+static int sbufi, sbufn;
+#define SBUFINCR 1000
+#define SBUFSIZE sizeof (char)
+
+static void scalarstr(Tobj);
+static void codestr(Tobj, int);
+static void appends(char *);
+static void appendi(long);
+static void appendd(double);
+static void appendnl(void);
+static void growsbuf(int);
+static char *copysbuf(void);
+
+void Sinit(void)
+{
+    if (!(sbufp = malloc(SBUFINCR * SBUFSIZE)))
+       panic(POS, "Sinit", "sbuf malloc failed");
+    sbufi = 0;
+    sbufn = SBUFINCR;
+    indent = 0;
+    highci = -1;
+}
+
+void Sterm(void)
+{
+    indent = 0;
+    free(sbufp), sbufp = NULL;
+    sbufn = sbufi = 0;
+}
+
+char *Spath(char *path, Tobj ko)
+{
+    sbufp[(sbufi = 0)] = '\000';
+    appends((path) ? path : "");
+    scalarstr(ko);
+    return copysbuf();
+}
+
+char *Sseen(Tobj ko, char *path)
+{
+    sbufp[(sbufi = 0)] = '\000';
+    scalarstr(ko), appends(" = "), appends(path), appends(";");
+    return copysbuf();
+}
+
+char *Sabstract(Tobj ko, Tobj vo)
+{
+    sbufp[(sbufi = 0)] = '\000';
+    scalarstr(ko), appends(" = ");
+    switch (Tgettype(vo)) {
+    case T_STRING:
+    case T_INTEGER:
+    case T_REAL:
+       scalarstr(vo);
+       break;
+    case T_CODE:
+       appends("function (...) { ... }");
+       break;
+    case T_TABLE:
+       appends("[ ... ]");
+       break;
+    case T_SIZE:
+       break;
+    }
+    appends(";");
+    return copysbuf();
+}
+
+char *Stfull(Tobj ko)
+{
+    sbufp[(sbufi = 0)] = '\000';
+    scalarstr(ko), appends(" = [");
+    return copysbuf();
+}
+
+char *Ssfull(Tobj ko, Tobj vo)
+{
+    sbufp[(sbufi = 0)] = '\000';
+    if (ko)
+       scalarstr(ko), appends(" = ");
+    switch (Tgettype(vo)) {
+    case T_STRING:
+    case T_INTEGER:
+    case T_REAL:
+    case T_CODE:
+       scalarstr(vo);
+       break;
+    case T_TABLE:
+    case T_SIZE:
+       break;
+    }
+    appends(";");
+    return copysbuf();
+}
+
+char *Scfull(Tobj co, int ci, int mci)
+{
+    sbufp[(sbufi = 0)] = '\000';
+    highci = mci;
+    codestr(co, ci);
+    highci = -1;
+    return copysbuf();
+}
+
+static void scalarstr(Tobj to)
+{
+    switch (Tgettype(to)) {
+    case T_INTEGER:
+       appendi(Tgetinteger(to));
+       break;
+    case T_REAL:
+       appendd(Tgetreal(to));
+       break;
+    case T_STRING:
+       appends("\""), appends(Tgetstring(to)), appends("\"");
+       break;
+    case T_CODE:
+       codestr(to, 0);
+       break;
+    case T_TABLE:
+    case T_SIZE:
+       break;
+    }
+}
+
+static void codestr(Tobj co, int ci)
+{
+    Ctype_t ct, ct1;
+    int ci1, ci2;
+
+    if (highci == ci)
+       appends(" >> ");
+    switch ((ct = TCgettype(co, ci))) {
+    case C_ASSIGN:
+       codestr(co, (ci1 = TCgetfp(co, ci)));
+       appends(" = ");
+       codestr(co, TCgetnext(co, ci1));
+       break;
+    case C_OR:
+    case C_AND:
+    case C_EQ:
+    case C_NE:
+    case C_LT:
+    case C_LE:
+    case C_GT:
+    case C_GE:
+    case C_PLUS:
+    case C_MINUS:
+    case C_MUL:
+    case C_DIV:
+    case C_MOD:
+       codestr(co, (ci1 = TCgetfp(co, ci)));
+       switch (ct) {
+       case C_OR:
+           appends(" | ");
+           break;
+       case C_AND:
+           appends(" & ");
+           break;
+       case C_EQ:
+           appends(" == ");
+           break;
+       case C_NE:
+           appends(" ~= ");
+           break;
+       case C_LT:
+           appends(" < ");
+           break;
+       case C_LE:
+           appends(" <= ");
+           break;
+       case C_GT:
+           appends(" > ");
+           break;
+       case C_GE:
+           appends(" >= ");
+           break;
+       case C_PLUS:
+           appends(" + ");
+           break;
+       case C_MINUS:
+           appends(" - ");
+           break;
+       case C_MUL:
+           appends(" * ");
+           break;
+       case C_DIV:
+           appends(" / ");
+           break;
+       case C_MOD:
+           appends(" % ");
+           break;
+       default:
+           break;
+       }
+       codestr(co, TCgetnext(co, ci1));
+       break;
+    case C_NOT:
+       appends("~");
+       codestr(co, TCgetfp(co, ci));
+       break;
+    case C_UMINUS:
+       appends("-");
+       codestr(co, TCgetfp(co, ci));
+       break;
+    case C_PEXPR:
+       appends("(");
+       codestr(co, TCgetfp(co, ci));
+       appends(")");
+       break;
+    case C_FCALL:
+       codestr(co, (ci1 = TCgetfp(co, ci)));
+       appends(" (");
+       codestr(co, TCgetnext(co, ci1));
+       appends(")");
+       break;
+    case C_INTEGER:
+       appendi(TCgetinteger(co, ci));
+       break;
+    case C_REAL:
+       appendd(TCgetreal(co, ci));
+       break;
+    case C_STRING:
+       appends("\""), appends(TCgetstring(co, ci)), appends("\"");
+       break;
+    case C_GVAR:
+    case C_LVAR:
+       ci1 = TCgetfp(co, ci);
+       appends(TCgetstring(co, ci1));
+       if (ct == C_LVAR)
+           ci1 = TCgetnext(co, ci1);
+       for (ci1 = TCgetnext(co, ci1); ci1 != C_NULL;
+            ci1 = TCgetnext(co, ci1)) {
+           switch (TCgettype(co, ci1)) {
+           case C_STRING:
+               appends("."), appends(TCgetstring(co, ci1));
+               break;
+           case C_INTEGER:
+               appends("[");
+               appendi(TCgetinteger(co, ci1));
+               appends("]");
+               break;
+           case C_REAL:
+               appends("[");
+               appendd(TCgetreal(co, ci1));
+               appends("]");
+               break;
+           default:
+               appends("[");
+               codestr(co, ci1);
+               appends("]");
+           }
+       }
+       break;
+    case C_PVAR:
+       appends("<var>");
+       break;
+    case C_FUNCTION:
+       ci1 = TCgetnext(co, TCgetnext(co, TCgetfp(co, ci)));
+       appends("function (");
+       codestr(co, ci1);
+       ci1 = TCgetnext(co, ci1);
+       if (TCgettype(co, ci1) == C_INTERNAL) {
+           appends(") internal \"");
+           appends(Ifuncs[TCgetinteger(co, TCgetfp(co, ci1))].name);
+           appends("\"");
+       } else {
+           appends(") {");
+           INDINC(2);
+           for (; ci1 != C_NULL; ci1 = TCgetnext(co, ci1)) {
+               appendnl();
+               if (TCgettype(co, ci1) == C_DECL)
+                   appends("local "), codestr(co, ci1), appends(";");
+               else
+                   codestr(co, ci1);
+           }
+           INDDEC(2);
+           appendnl();
+           appends("}");
+       }
+       break;
+    case C_TCONS:
+       appends("[");
+       INDINC(2);
+       ci1 = TCgetfp(co, ci);
+       while (ci1 != C_NULL) {
+           appendnl();
+           codestr(co, ci1);
+           appends(" = ");
+           ci1 = TCgetnext(co, ci1);
+           codestr(co, ci1);
+           appends(";");
+           ci1 = TCgetnext(co, ci1);
+       }
+       INDDEC(2);
+       appendnl();
+       appends("]");
+       break;
+    case C_DECL:
+       ci1 = TCgetfp(co, ci);
+       while (ci1 != C_NULL) {
+           appends(TCgetstring(co, ci1));
+           ci1 = TCgetnext(co, ci1);
+           if (ci1 != C_NULL)
+               appends(", ");
+       }
+       break;
+    case C_STMT:
+       ci1 = TCgetfp(co, ci);
+       if (ci1 == C_NULL) {
+           appends(";");
+           break;
+       }
+       if (TCgetnext(co, ci1) == C_NULL) {
+           codestr(co, ci1);
+           ct1 = TCgettype(co, ci1);
+           if (!C_ISSTMT(ct1))
+               appends(";");
+       } else {
+           appends(" {");
+           INDINC(2);
+           for (; ci1 != C_NULL; ci1 = TCgetnext(co, ci1)) {
+               appendnl();
+               codestr(co, ci1);
+           }
+           INDDEC(2);
+           appendnl();
+           appends("}");
+       }
+       break;
+    case C_IF:
+       ci1 = TCgetfp(co, ci);
+       appends("if (");
+       codestr(co, ci1);
+       appends(")");
+       ci1 = TCgetnext(co, ci1);
+       ci2 = TCgetfp(co, ci1);
+       if (ci2 == C_NULL || TCgetnext(co, ci2) == C_NULL) {
+           INDINC(2);
+           appendnl();
+           codestr(co, ci1);
+           INDDEC(2);
+       } else {
+           codestr(co, ci1);
+       }
+       ci1 = TCgetnext(co, ci1);
+       if (ci1 == C_NULL)
+           break;
+       if (ci2 == C_NULL || TCgetnext(co, ci2) == C_NULL) {
+           appendnl();
+           appends("else");
+       } else {
+           appends(" else");
+       }
+       ci2 = TCgetfp(co, ci1);
+       if (ci2 == C_NULL || TCgetnext(co, ci2) == C_NULL) {
+           INDINC(2);
+           appendnl();
+           codestr(co, ci1);
+           INDDEC(2);
+       } else {
+           codestr(co, ci1);
+       }
+       break;
+    case C_WHILE:
+       ci1 = TCgetfp(co, ci);
+       appends("while (");
+       codestr(co, ci1);
+       ci1 = TCgetnext(co, ci1);
+       ci2 = TCgetfp(co, ci1);
+       if (ci2 == C_NULL || TCgetnext(co, ci2) == C_NULL) {
+           appends(")");
+           INDINC(2);
+           appendnl();
+           codestr(co, ci1);
+           INDDEC(2);
+       } else {
+           appends(")");
+           codestr(co, ci1);
+       }
+       break;
+    case C_FOR:
+       ci1 = TCgetfp(co, ci);
+       appends("for (");
+       codestr(co, ci1);
+       appends("; ");
+       ci1 = TCgetnext(co, ci1);
+       codestr(co, ci1);
+       appends("; ");
+       ci1 = TCgetnext(co, ci1);
+       codestr(co, ci1);
+       ci1 = TCgetnext(co, ci1);
+       ci2 = TCgetfp(co, ci1);
+       if (ci2 == C_NULL || TCgetnext(co, ci2) == C_NULL) {
+           appends(")");
+           INDINC(2);
+           appendnl();
+           codestr(co, ci1);
+           INDDEC(2);
+       } else {
+           appends(")");
+           codestr(co, ci1);
+       }
+       break;
+    case C_FORIN:
+       ci1 = TCgetfp(co, ci);
+       appends("for (");
+       codestr(co, ci1);
+       appends(" in ");
+       ci1 = TCgetnext(co, ci1);
+       codestr(co, ci1);
+       ci1 = TCgetnext(co, ci1);
+       ci2 = TCgetfp(co, ci1);
+       if (ci2 == C_NULL || TCgetnext(co, ci2) == C_NULL) {
+           appends(")");
+           INDINC(2);
+           appendnl();
+           codestr(co, ci1);
+           INDDEC(2);
+       } else {
+           appends(")");
+           codestr(co, ci1);
+       }
+       break;
+    case C_BREAK:
+       appends("break;");
+       break;
+    case C_CONTINUE:
+       appends("continue;");
+       break;
+    case C_RETURN:
+       ci1 = TCgetfp(co, ci);
+       appends("return");
+       if (ci1 != C_NULL) {
+           appends(" ");
+           codestr(co, ci1);
+       }
+       appends(";");
+       break;
+    case C_ARGS:
+       ci1 = TCgetfp(co, ci);
+       while (ci1 != C_NULL) {
+           codestr(co, ci1);
+           ci1 = TCgetnext(co, ci1);
+           if (ci1 != C_NULL)
+               appends(", ");
+       }
+       break;
+    default:
+       panic(POS, "codestr", "bad object type: %d", ct);
+    }
+}
+
+static void appends(char *s)
+{
+    int n;
+
+    n = strlen(s) + 1;
+    if (sbufi + n > sbufn)
+       growsbuf(n);
+    strcpy(&sbufp[sbufi], s);
+    sbufi += (n - 1);
+}
+
+static void appendi(long i)
+{
+    char buf[40];
+    int n;
+
+    sprintf(buf, "%ld", i);
+    n = strlen(buf) + 1;
+    if (sbufi + n > sbufn)
+       growsbuf(n);
+    strcpy(&sbufp[sbufi], buf);
+    sbufi += (n - 1);
+}
+
+static void appendd(double d)
+{
+    char buf[40];
+    int n;
+
+    sprintf(buf, "%f", d);
+    n = strlen(buf) + 1;
+    if (sbufi + n > sbufn)
+       growsbuf(n);
+    strcpy(&sbufp[sbufi], buf);
+    sbufi += (n - 1);
+}
+
+static void appendnl(void)
+{
+    int i, n;
+
+    n = indent + 1;
+    if (sbufi + n > sbufn)
+       growsbuf(n);
+    sbufp[sbufi++] = '\n';
+    for (i = 0; i < indent; i++)
+       sbufp[sbufi++] = ' ';
+}
+
+static void growsbuf(int ssize)
+{
+    int nsize;
+
+    nsize = ((sbufn + ssize) / SBUFINCR + 1) * SBUFINCR;
+    if (!(sbufp = realloc(sbufp, nsize * SBUFSIZE)))
+       panic(POS, "growsbuf", "sbuf realloc failed");
+    sbufn = nsize;
+}
+
+static char *copysbuf(void)
+{
+    char *newsbufp;
+
+    sbufp[sbufi++] = '\000';
+    if (!(newsbufp = malloc(sbufi * sizeof(char))))
+       panic(POS, "copysbuf", "newsbuf malloc failed");
+    strcpy(newsbufp, sbufp);
+    return newsbufp;
+}
diff --git a/cmd/lefty/str.h b/cmd/lefty/str.h
new file mode 100644 (file)
index 0000000..b2802e5
--- /dev/null
@@ -0,0 +1,38 @@
+/* $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 _STR_H
+#define _STR_H
+    void Sinit(void);
+    void Sterm(void);
+    char *Spath(char *, Tobj);
+    char *Sseen(Tobj, char *);
+    char *Sabstract(Tobj, Tobj);
+    char *Stfull(Tobj);
+    char *Ssfull(Tobj, Tobj);
+    char *Scfull(Tobj, int, int);
+#endif                         /* _STR_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/tbl.c b/cmd/lefty/tbl.c
new file mode 100644 (file)
index 0000000..08e48eb
--- /dev/null
@@ -0,0 +1,730 @@
+/* $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_t {
+    struct mapentry_t *list[MAPLISTN];
+} Map_t;
+static Map_t 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 = NULL;
+    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 = NULL, *nkvlp;
+    Tkv_t *kvp;
+    Ttype_t kt;
+    long ik = 0, i, ind = 0, nln;
+    double rk = 0.0;
+
+    switch ((kt = (Ttype_t) 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;
+    default:
+       break;
+    }
+    if ((nln = tp->n + 1) > 4 * tp->ln && nln < M_SIZEMAX) {
+       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;
+       default:
+           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;
+       default:
+           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 (sk == NULL)
+           return NULL;
+       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 = NULL, *nkvlp;
+    Tkv_t *kvp;
+    long ik, oln, i, j, k, ind = 0;
+    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/tbl.h b/cmd/lefty/tbl.h
new file mode 100644 (file)
index 0000000..7791693
--- /dev/null
@@ -0,0 +1,137 @@
+/* $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 _TBL_H
+#define _TBL_H
+    typedef enum {
+       T_INTEGER = 1, T_REAL, T_STRING, T_CODE, T_TABLE, T_SIZE
+    } Ttype_t;
+
+    typedef struct Tinteger_t {
+       Mheader_t head;
+       long i;
+    } Tinteger_t;
+#define T_INTEGERSIZE sizeof (Tinteger_t)
+    typedef struct Treal_t {
+       Mheader_t head;
+       double d;
+    } Treal_t;
+#define T_REALSIZE sizeof (Treal_t)
+    typedef struct Tstring_t {
+       Mheader_t head;
+       char s[1];
+    } Tstring_t;
+#define T_STRINGSIZE(l) (l + Tstringoffset)
+    typedef struct Tcode_t {
+       Mheader_t head;
+       Code_t c[1];
+    } Tcode_t;
+#define T_CODESIZE(l) (l * C_CODESIZE + Tcodeoffset)
+    typedef struct Tkv_t {
+       void *ko;
+       void *vo;
+    } Tkv_t;
+#define T_KVSIZE sizeof (Tkv_t)
+    typedef struct Tkvlist_t {
+       long i, n;
+       Tkv_t kv[1];
+    } Tkvlist_t;
+#define T_KVLISTSIZE(l) (l * T_KVSIZE + Tkvoffset)
+#define T_KVLISTPTRSIZE sizeof (Tkvlist_t *)
+    typedef struct Ttable_t {
+       Mheader_t head;
+       long n, ln;
+       long time;
+       Tkvlist_t **lp;
+    } Ttable_t;
+#define T_TABLESIZE sizeof (Ttable_t)
+    typedef struct Tkvindex_t {
+       Ttable_t *tp;
+       Tkv_t *kvp;
+       long i, j;
+    } Tkvindex_t;
+
+    typedef void *Tobj;
+
+    typedef struct Tonm_t {    /* Object aNd Mark */
+       Tobj o;
+       long m;
+    } Tonm_t;
+
+#define T_ISSTRING(o)  (M_TYPEOF (o) == T_STRING)
+#define T_ISTABLE(o)   (M_TYPEOF (o) == T_TABLE)
+#define T_ISINTEGER(o) (M_TYPEOF (o) == T_INTEGER)
+#define T_ISREAL(o)    (M_TYPEOF (o) == T_REAL)
+#define T_ISNUMBER(o)  (M_TYPEOF (o) == T_REAL || M_TYPEOF (o) == T_INTEGER)
+
+#define Tgettype(p)    ((Ttype_t)(((Mheader_t *) p)->type))
+#define Tgettablen(p)  (((Ttable_t *) p)->n)
+#define Tgettime(p)    (((Ttable_t *) p)->time)
+#define Tgetstring(p)  (char *) &(((Tstring_t *) p)->s[0])
+#define Tgetcode(p)    (Code_t *) &(((Tcode_t *) p)->c[0])
+#define Tgetinteger(p) (((Tinteger_t *) p)->i)
+#define Tgetreal(p)    (((Treal_t *) p)->d)
+#define Tgetnumber(p)  (T_ISINTEGER (p) ? Tgetinteger (p) : Tgetreal (p))
+
+#define TCgettype(p, off)    ((Tgetcode (p) + off)->ctype)
+#define TCgetfp(p, off)      ((Tgetcode (p) + off)->u.fp)
+#define TCgetinteger(p, off) ((Tgetcode (p) + off)->u.i)
+#define TCgetobject(p, off)  ((Tgetcode (p) + off)->u.o)
+#define TCgetreal(p, off)    ((Tgetcode (p) + off)->u.d)
+#define TCgetstring(p, off)  ((char *) &((Tgetcode (p) + off)->u.s))
+#define TCgetnext(p, off)    ((Tgetcode (p) + off)->next)
+#define TCgetaddr(p, off)    (Tgetcode (p) + off)
+
+    extern long Ttime;
+    extern int Tstringoffset, Tcodeoffset, Tkvoffset;
+    extern Tobj Ttrue, Tfalse;
+
+    void Tinit(void);
+    void Tterm(void);
+    void Tgchelper(void *);
+    void Tfreehelper(void *);
+    Tobj Tinteger(long);
+    Tobj Treal(double);
+    Tobj Tstring(char *);
+    Tobj Tcode(Code_t *, int, int);
+    Tobj Ttable(long);
+    void Tinsi(Tobj, long, Tobj);
+    void Tinsr(Tobj, double, Tobj);
+    void Tinss(Tobj, char *, Tobj);
+    void Tinso(Tobj, Tobj, Tobj);
+    Tobj Tfindi(Tobj, long);
+    Tobj Tfindr(Tobj, double);
+    Tobj Tfinds(Tobj, char *);
+    Tobj Tfindo(Tobj, Tobj);
+    void Tdeli(Tobj, long);
+    void Tdelr(Tobj, double);
+    void Tdels(Tobj, char *);
+    void Tdelo(Tobj, Tobj);
+    Tobj Tcopy(Tobj);
+    void Tgetfirst(Tobj, Tkvindex_t *);
+    void Tgetnext(Tkvindex_t *);
+#endif                         /* _TBL_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/txtview.c b/cmd/lefty/txtview.c
new file mode 100644 (file)
index 0000000..34ad53c
--- /dev/null
@@ -0,0 +1,702 @@
+/* $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 "code.h"
+#include "tbl.h"
+#include "parse.h"
+#include "exec.h"
+#include "str.h"
+#include "txtview.h"
+
+typedef enum {
+    TXT_SEEN, TXT_ABSTRACT, TXT_FULL
+} txtmode_t;
+typedef struct txtnode_t {
+    txtmode_t mode;
+    Ttype_t ttype;
+    Tobj ko, vo;
+    long time;
+    char *path;
+    union {
+       struct {
+           struct txtnode_t *txtnode;
+           Tobj ko;
+           char *text;
+           int wi;
+       } s;
+       struct {
+           char *text;
+           int wi;
+       } a;
+       union {
+           struct {
+               char *text;
+               int wi;
+           } s;
+           struct {
+               char *ftext, *ltext;
+               int fwi, mwi, lwi;
+               struct txtnode_t *list;
+               int n;
+           } t;
+       } f;
+    } u;
+} txtnode_t;
+static txtnode_t *txtroot;
+static txtnode_t defnode;
+
+static txtnode_t **seenp;
+static long seeni, seenn;
+#define SEENINCR 100
+#define SEENSIZE sizeof (txtnode_t)
+
+static Tobj txtvo2toggle = NULL;
+
+static Gwattr_t arraydata[2], buttondata[5];
+
+static int txton, txtx, txty, txtwidth, txtheight;
+static int txtwi, queryws[G_QWMODES + 1], listwi, scrollwi, arraywi, cmdwi;
+
+static Psrc_t src;
+
+#define max(a, b) (((a) >= (b)) ? (a) : (b))
+
+static void viewon(void);
+static void viewoff(void);
+static void update(txtnode_t *, long);
+static void buildlist(txtnode_t *);
+static void rebuildlist(txtnode_t *);
+static void fillnode(txtnode_t *, txtnode_t *);
+static void unfillnode(txtnode_t *);
+static int cmp(const void *, const void *);
+static txtnode_t *findseen(txtnode_t *);
+static void add2seen(txtnode_t *);
+static void orderfunc(void *, Gawdata_t *);
+static void coordsfunc(int, Gawdata_t *);
+static void coords2func(int, Gawdata_t *);
+
+void TXTinit(Grect_t r)
+{
+    Gwattr_t data[1];
+    int i;
+
+    txton = TRUE;
+    txtwi = -1;
+    txtx = r.o.x, txty = r.o.y;
+    txtwidth = r.c.x - r.o.x + 1, txtheight = r.c.y - r.o.y + 1;
+
+    buttondata[0].id = G_ATTRBUTTONCB;
+    buttondata[0].u.func = (void *) (TXTtoggle);
+    buttondata[1].id = G_ATTRSIZE;
+    buttondata[1].u.s.x = buttondata[1].u.s.y = 0;
+    buttondata[2].id = G_ATTRBORDERWIDTH;
+    buttondata[2].u.i = 0;
+    buttondata[3].id = G_ATTRTEXT;
+    buttondata[4].id = G_ATTRUSERDATA;
+
+    arraydata[0].id = G_ATTRRESIZECB;
+    arraydata[0].u.func = (void *) (coordsfunc);
+    arraydata[1].id = G_ATTRSIZE;
+    arraydata[1].u.s.x = arraydata[1].u.s.y = 0;
+
+    defnode.mode = TXT_ABSTRACT;
+    defnode.ttype = T_TABLE;
+    defnode.ko = defnode.vo = NULL;
+    defnode.time = -1;
+    defnode.path = NULL;
+    defnode.u.s.txtnode = NULL;
+    defnode.u.s.ko = NULL;
+    defnode.u.s.text = NULL;
+    defnode.u.s.wi = 0;
+    defnode.u.a.text = NULL;
+    defnode.u.a.wi = 0;
+    defnode.u.f.s.text = NULL;
+    defnode.u.f.s.wi = 0;
+    defnode.u.f.t.ftext = defnode.u.f.t.ltext = NULL;
+    defnode.u.f.t.fwi = 0;
+    defnode.u.f.t.mwi = 0;
+    defnode.u.f.t.lwi = 0;
+    defnode.u.f.t.list = NULL;
+    defnode.u.f.t.n = 0;
+
+    for (i = 1; i <= G_QWMODES; i++) {
+       data[0].id = G_ATTRMODE;
+       switch (i) {
+       case G_QWSTRING:
+           data[0].u.t = "string";
+           break;
+       case G_QWFILE:
+           data[0].u.t = "file";
+           break;
+       case G_QWCHOICE:
+           data[0].u.t = "choice";
+           break;
+       }
+       queryws[i] = Gcreatewidget(-1, G_QUERYWIDGET, 1, &data[0]);
+    }
+
+    src.flag = CHARSRC, src.fp = NULL, src.tok = -1, src.lnum = 1;
+}
+
+void TXTterm(void)
+{
+    int i;
+
+    for (i = 1; i <= G_QWMODES; i++)
+       Gdestroywidget(queryws[i]);
+    if (txton)
+       viewoff(), txton = FALSE;
+}
+
+/* LEFTY builtin */
+int TXTmode(int argc, lvar_t * argv)
+{
+    char *s;
+
+    if (!argv[0].o || Tgettype(argv[0].o) != T_STRING)
+       return L_FAILURE;
+    s = Tgetstring(argv[0].o);
+    if (Strcmp(s, "on") == 0) {
+       if (txtwi == -1)
+           viewon();
+    } else if (Strcmp(s, "off") == 0) {
+       if (txtwi != -1)
+           viewoff();
+       else
+           txton = FALSE;
+    }
+    return L_SUCCESS;
+}
+
+static void viewon(void)
+{
+    Gwattr_t data[5];
+
+    data[0].id = G_ATTRNAME;
+    data[0].u.t = "LEFTY text view";
+    data[1].id = G_ATTRORIGIN;
+    data[1].u.p.x = txtx, data[1].u.p.y = txty;
+    data[2].id = G_ATTRSIZE;
+    data[2].u.s.x = txtwidth, data[2].u.s.y = txtheight;
+    txtwi = Gcreatewidget(-1, G_VIEWWIDGET, 3, &data[0]);
+
+    data[0].id = G_ATTRSIZE;
+    data[0].u.s.x = txtwidth, data[0].u.s.y = txtheight;
+    data[1].id = G_ATTRBORDERWIDTH;
+    data[1].u.i = 1;
+    data[2].id = G_ATTRRESIZECB;
+    data[2].u.func = (void *) (coords2func);
+    listwi = Gcreatewidget(txtwi, G_ARRAYWIDGET, 3, &data[0]);
+    Gawsetmode(&Gwidgets[listwi], TRUE);
+
+    data[0].id = G_ATTRSIZE;
+    data[0].u.s.x = txtwidth, data[0].u.s.y = txtheight / 3;
+    data[1].id = G_ATTRBORDERWIDTH;
+    data[1].u.i = 0;
+    data[2].id = G_ATTRTEXT;
+    data[2].u.t = NULL;
+    data[3].id = G_ATTRNEWLINECB;
+    data[3].u.func = (void *) (TXTprocess);
+    data[4].id = G_ATTRMODE;
+    data[4].u.t = "oneline";
+    cmdwi = Gcreatewidget(listwi, G_TEXTWIDGET, 5, &data[0]);
+
+    data[0].id = G_ATTRSIZE;
+    data[0].u.s.x = txtwidth, data[0].u.s.y = txtheight * 2 / 3;
+    data[1].id = G_ATTRMODE;
+    data[1].u.t = "forcebars";
+    scrollwi = Gcreatewidget(listwi, G_SCROLLWIDGET, 2, &data[0]);
+
+    data[0].id = G_ATTRRESIZECB;
+    data[0].u.func = (void *) (coordsfunc);
+    data[1].id = G_ATTRSIZE;
+    data[1].u.s.x = txtwidth, data[1].u.s.y = txtheight;
+    data[2].id = G_ATTRBORDERWIDTH;
+    data[2].u.i = 0;
+    arraywi = Gcreatewidget(scrollwi, G_ARRAYWIDGET, 3, &data[0]);
+
+    Gawsetmode(&Gwidgets[listwi], FALSE);
+
+    if (!(txtroot = malloc(sizeof(txtnode_t))))
+       panic(POS, "TXTinit", "txtroot malloc failed");
+    *txtroot = defnode;
+    txtroot->mode = TXT_FULL;
+    txtroot->vo = root;
+    txtroot->path = NULL;
+    txtroot->u.f.t.mwi = arraywi;
+
+    seenp = Marrayalloc((long) SEENINCR * SEENSIZE);
+    seeni = 0;
+    seenn = SEENINCR;
+    txton = TRUE;
+}
+
+static void viewoff(void)
+{
+    Marrayfree(seenp);
+    seeni = seenn = 0;
+    unfillnode(txtroot);
+    free(txtroot);
+    Gdestroywidget(arraywi);
+    Gdestroywidget(scrollwi);
+    Gdestroywidget(cmdwi);
+    Gdestroywidget(listwi);
+    Gdestroywidget(txtwi);
+    txton = FALSE;
+    txtwi = -1;
+}
+
+/* LEFTY builtin */
+int TXTask(int argc, lvar_t * argv)
+{
+    Tobj so, ao;
+    char buf[1200];
+    char *sp, *ap;
+    int mode = 0;
+    ap = 0;
+
+    if (argc < 2)
+       mode = G_QWSTRING;
+    else {
+       so = argv[1].o;
+       if (T_ISSTRING(so)) {
+           sp = Tgetstring(so);
+           if (Strcmp(sp, "string") == 0)
+               mode = G_QWSTRING;
+           else if (Strcmp(sp, "file") == 0)
+               mode = G_QWFILE;
+           else if (Strcmp(sp, "choice") == 0)
+               mode = G_QWCHOICE;
+       }
+    }
+    if (argc < 3)
+       ap = NULL;
+    else {
+       ao = argv[2].o;
+       if (T_ISSTRING(ao))
+           ap = Tgetstring(ao);
+    }
+    if (Gqueryask(queryws[mode], Tgetstring(argv[0].o), ap, buf, 1200) ==
+       0)
+       rtno = Tstring(buf);
+    else
+       rtno = NULL;
+#ifndef NOQUERYFIX
+    if (mode == G_QWCHOICE) {
+       Gwattr_t data[1];
+
+       data[0].id = G_ATTRMODE;
+       data[0].u.t = "choice";
+       Gdestroywidget(queryws[mode]);
+       queryws[mode] = Gcreatewidget(-1, G_QUERYWIDGET, 1, &data[0]);
+    }
+#endif
+    return L_SUCCESS;
+}
+
+void TXTprocess(int wi, char *sp)
+{
+    Tobj co;
+
+    src.s = sp;
+    if ((co = Punit(&src)))
+       Eoktorun = TRUE, Eunit(co);
+}
+
+void TXTupdate(void)
+{
+    if (!txton)
+       return;
+    if (txtwi == -1)
+       viewon();
+    seeni = 0;
+    update(txtroot, txtroot->time);
+    txtroot->time = Ttime;
+    Ttime++;
+}
+
+void TXTtoggle(int wi, void *data)
+{
+    txtvo2toggle = data;
+    TXTupdate();
+    txtvo2toggle = NULL;
+}
+
+/* update is called only for TXT_FULL tables */
+static void update(txtnode_t * pnode, long ptim)
+{
+    txtnode_t *cnode, *seennode;
+    long ctim;
+    int i;
+
+    Gawsetmode(&Gwidgets[pnode->u.f.t.mwi], TRUE);
+    if (!pnode->u.f.t.list)
+       buildlist(pnode);
+    else if (ptim < Tgettime(pnode->vo))
+       rebuildlist(pnode);
+    for (i = 0, cnode = &pnode->u.f.t.list[0]; i < pnode->u.f.t.n;
+        i++, cnode++) {
+       ctim = cnode->time;
+       if (txtvo2toggle == cnode->vo) {
+           switch (cnode->mode) {
+           case TXT_SEEN:
+               break;
+           case TXT_ABSTRACT:
+               unfillnode(cnode);
+               cnode->mode = TXT_FULL;
+               fillnode(pnode, cnode);
+               break;
+           case TXT_FULL:
+               unfillnode(cnode);
+               cnode->mode = TXT_ABSTRACT;
+               fillnode(pnode, cnode);
+               break;
+           }
+       }
+       if (!(seennode = findseen(cnode)))
+           add2seen(cnode);
+       if (seennode && cnode->mode == TXT_SEEN &&
+           seennode->ko != cnode->u.s.ko) {
+           unfillnode(cnode);
+           cnode->u.s.txtnode = seennode;
+           cnode->u.s.ko = seennode->ko;
+           fillnode(pnode, cnode);
+       } else if (seennode && cnode->mode != TXT_SEEN) {
+           unfillnode(cnode);
+           cnode->mode = TXT_SEEN;
+           cnode->u.s.txtnode = seennode;
+           cnode->u.s.ko = seennode->ko;
+           fillnode(pnode, cnode);
+       } else if (!seennode && cnode->mode == TXT_SEEN) {
+           unfillnode(cnode);
+           cnode->mode = TXT_ABSTRACT;
+           fillnode(pnode, cnode);
+       } else if (cnode->time == -1) {
+           unfillnode(cnode);
+           if (seennode)
+               cnode->u.s.txtnode = seennode;
+           fillnode(pnode, cnode);
+       }
+       if (cnode->ttype == T_TABLE && cnode->mode == TXT_FULL)
+           update(cnode, ctim);
+    }
+    Gaworder(&Gwidgets[pnode->u.f.t.mwi], pnode, orderfunc);
+    Gawsetmode(&Gwidgets[pnode->u.f.t.mwi], FALSE);
+}
+
+static void buildlist(txtnode_t * pnode)
+{
+    Tkvindex_t tkvi;
+    Tobj ko, vo;
+    Ttype_t vtype;
+    txtnode_t *cnode;
+
+    pnode->u.f.t.n = ((Ttable_t *) pnode->vo)->n;
+    if (!(pnode->u.f.t.list =
+         malloc(max(pnode->u.f.t.n, 1) * sizeof(txtnode_t))))
+       panic(POS, "buildlist", "list malloc failed");
+
+    for (cnode = &pnode->u.f.t.list[0], Tgetfirst(pnode->vo, &tkvi);
+        tkvi.kvp; Tgetnext(&tkvi)) {
+       ko = tkvi.kvp->ko, vo = tkvi.kvp->vo;
+       vtype = Tgettype(vo);
+       if (vtype == T_CODE && TCgettype(vo, TCgetnext(vo, TCgetnext(vo,
+                                                                    TCgetnext
+                                                                    (vo,
+                                                                     TCgetfp
+                                                                     (vo,
+                                                                      0)))))
+           == C_INTERNAL) {
+           pnode->u.f.t.n--;
+           continue;
+       }
+       *cnode = defnode;
+       cnode->vo = vo;
+       cnode->ko = ko;
+       cnode->ttype = vtype;
+       cnode++;
+    }
+    qsort((char *) pnode->u.f.t.list, pnode->u.f.t.n,
+         sizeof(txtnode_t), cmp);
+}
+
+static void rebuildlist(txtnode_t * pnode)
+{
+    Tkvindex_t tkvi;
+    Tobj ko, vo;
+    Ttype_t vtype;
+    txtnode_t *cnode;
+    txtnode_t *olist, *nlist;
+    txtnode_t tmpnode;
+    int on, nn, i, j, cmpval = 0;
+
+    olist = pnode->u.f.t.list;
+    on = pnode->u.f.t.n;
+    pnode->u.f.t.n = ((Ttable_t *) pnode->vo)->n;
+    if (!(pnode->u.f.t.list =
+         malloc(max(pnode->u.f.t.n, 1) * sizeof(txtnode_t))))
+       panic(POS, "rebuildlist", "list malloc failed");
+
+    for (cnode = &pnode->u.f.t.list[0], Tgetfirst(pnode->vo, &tkvi);
+        tkvi.kvp; Tgetnext(&tkvi)) {
+       ko = tkvi.kvp->ko, vo = tkvi.kvp->vo;
+       vtype = Tgettype(vo);
+       if (vtype == T_CODE && TCgettype(vo, TCgetnext(vo, TCgetnext(vo,
+                                                                    TCgetnext
+                                                                    (vo,
+                                                                     TCgetfp
+                                                                     (vo,
+                                                                      0)))))
+           == C_INTERNAL) {
+           pnode->u.f.t.n--;
+           continue;
+       }
+       *cnode = defnode;
+       cnode->vo = vo;
+       cnode->ko = ko;
+       cnode->ttype = vtype;
+       cnode++;
+    }
+    qsort((char *) pnode->u.f.t.list, pnode->u.f.t.n, sizeof(txtnode_t),
+         cmp);
+    nlist = pnode->u.f.t.list;
+    nn = pnode->u.f.t.n;
+    for (i = 0, j = 0; i < nn; i++) {
+       while (j < on && (cmpval = cmp(&olist[j], &nlist[i])) < 0)
+           j++;
+       if (j < on && cmpval == 0 && nlist[i].vo == olist[j].vo)
+           tmpnode = olist[j], olist[j] = nlist[i], nlist[i] =
+               tmpnode, j++;
+    }
+    for (j = 0; j < on; j++)
+       unfillnode(&olist[j]);
+    free(olist);
+}
+
+static void fillnode(txtnode_t * pnode, txtnode_t * cnode)
+{
+    cnode->time = Ttime;
+    cnode->path = Spath(pnode->path, cnode->ko);
+    switch (cnode->mode) {
+    case TXT_SEEN:
+       cnode->u.s.text = Sseen(cnode->ko, cnode->u.s.txtnode->path);
+       buttondata[3].u.t = cnode->u.s.text;
+       buttondata[4].u.u = (unsigned long) cnode->vo;
+       cnode->u.s.wi = Gcreatewidget(pnode->u.f.t.mwi,
+                                     G_BUTTONWIDGET, 5, &buttondata[0]);
+       break;
+    case TXT_ABSTRACT:
+       cnode->u.a.text = Sabstract(cnode->ko, cnode->vo);
+       buttondata[3].u.t = cnode->u.a.text;
+       buttondata[4].u.u = (unsigned long) cnode->vo;
+       cnode->u.a.wi = Gcreatewidget(pnode->u.f.t.mwi,
+                                     G_BUTTONWIDGET, 5, &buttondata[0]);
+       break;
+    case TXT_FULL:
+       if (cnode->ttype == T_TABLE) {
+           cnode->u.f.t.ftext = Stfull(cnode->ko);
+           cnode->u.f.t.ltext = "];";
+           buttondata[3].u.t = cnode->u.f.t.ftext;
+           buttondata[4].u.u = (unsigned long) cnode->vo;
+           cnode->u.f.t.fwi = Gcreatewidget(pnode->u.f.t.mwi,
+                                            G_BUTTONWIDGET, 5,
+                                            &buttondata[0]);
+           cnode->u.f.t.mwi =
+               Gcreatewidget(pnode->u.f.t.mwi, G_ARRAYWIDGET, 2,
+                             &arraydata[0]);
+           buttondata[3].u.t = cnode->u.f.t.ltext;
+           buttondata[4].u.u = (unsigned long) cnode->vo;
+           cnode->u.f.t.lwi = Gcreatewidget(pnode->u.f.t.mwi,
+                                            G_BUTTONWIDGET, 5,
+                                            &buttondata[0]);
+       } else {
+           cnode->u.f.s.text = Ssfull(cnode->ko, cnode->vo);
+           buttondata[3].u.t = cnode->u.f.s.text;
+           buttondata[4].u.u = (unsigned long) cnode->vo;
+           cnode->u.f.s.wi = Gcreatewidget(pnode->u.f.t.mwi,
+                                           G_BUTTONWIDGET, 5,
+                                           &buttondata[0]);
+       }
+       break;
+    }
+}
+
+static void unfillnode(txtnode_t * cnode)
+{
+    int i;
+
+    if (cnode->path)
+       free(cnode->path), cnode->path = NULL;
+    switch (cnode->mode) {
+    case TXT_SEEN:
+       if (cnode->u.s.text)
+           free(cnode->u.s.text);
+       if (cnode->u.s.wi)
+           Gdestroywidget(cnode->u.s.wi);
+       break;
+    case TXT_ABSTRACT:
+       if (cnode->u.a.text)
+           free(cnode->u.a.text);
+       if (cnode->u.a.wi)
+           Gdestroywidget(cnode->u.a.wi);
+       break;
+    case TXT_FULL:
+       if (cnode->ttype == T_TABLE) {
+           for (i = 0; i < cnode->u.f.t.n; i++)
+               unfillnode(&cnode->u.f.t.list[i]);
+           if (cnode->u.f.t.list)
+               free(cnode->u.f.t.list);
+           if (cnode->u.f.t.ftext)
+               free(cnode->u.f.t.ftext);
+           if (cnode->u.f.t.fwi) {
+               Gdestroywidget(cnode->u.f.t.fwi);
+               Gdestroywidget(cnode->u.f.t.mwi);
+               Gdestroywidget(cnode->u.f.t.lwi);
+           }
+       } else {
+           if (cnode->u.f.s.text)
+               free(cnode->u.f.s.text);
+           if (cnode->u.f.s.wi)
+               Gdestroywidget(cnode->u.f.s.wi);
+       }
+       break;
+    }
+    cnode->u.s.text = NULL;
+    cnode->u.s.wi = 0;
+    cnode->u.a.text = NULL;
+    cnode->u.a.wi = 0;
+    cnode->u.f.t.list = NULL;
+    cnode->u.f.t.ftext = NULL;
+    cnode->u.f.t.fwi = 0;
+    cnode->u.f.t.mwi = 0;
+    cnode->u.f.t.lwi = 0;
+    cnode->u.f.s.text = NULL;
+    cnode->u.f.s.wi = 0;
+}
+
+static int cmp(const void *a, const void *b)
+{
+    Ttype_t atype, btype;
+    txtnode_t *anode, *bnode;
+    double d1 = 0.0, d2 = 0.0;
+
+    anode = (txtnode_t *) a, bnode = (txtnode_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 txtnode_t *findseen(txtnode_t * cnode)
+{
+    int i;
+
+    for (i = 0; i < seeni; i++)
+       if (seenp[i]->vo == cnode->vo)
+           return seenp[i];
+    return NULL;
+}
+
+static void add2seen(txtnode_t * cnode)
+{
+    if (seeni >= seenn) {
+       seenp = Marraygrow(seenp, (long) (seenn + SEENINCR) * SEENSIZE);
+       seenn += SEENINCR;
+    }
+    seenp[seeni++] = cnode;
+}
+
+static void orderfunc(void *data, Gawdata_t * dp)
+{
+    txtnode_t *pnode, *cnode;
+    int i;
+
+    pnode = data;
+    dp->cj = 0;
+    for (i = 0, cnode = &pnode->u.f.t.list[0]; i < pnode->u.f.t.n;
+        i++, cnode++)
+       switch (cnode->mode) {
+       case TXT_SEEN:
+           dp->carray[dp->cj++].w = Gwidgets[cnode->u.s.wi].w;
+           break;
+       case TXT_ABSTRACT:
+           dp->carray[dp->cj++].w = Gwidgets[cnode->u.a.wi].w;
+           break;
+       case TXT_FULL:
+           if (cnode->ttype == T_TABLE) {
+               dp->carray[dp->cj++].w = Gwidgets[cnode->u.f.t.fwi].w;
+               dp->carray[dp->cj++].w = Gwidgets[cnode->u.f.t.mwi].w;
+               dp->carray[dp->cj++].w = Gwidgets[cnode->u.f.t.lwi].w;
+           } else
+               dp->carray[dp->cj++].w = Gwidgets[cnode->u.f.s.wi].w;
+           break;
+       }
+}
+
+static void coordsfunc(int wi, Gawdata_t * dp)
+{
+    Gawcarray_t *cp;
+    int cox, coy;
+    int ci;
+
+    cox = coy = 0;
+    for (ci = 0; ci < dp->cj; ci++) {
+       cp = &dp->carray[ci];
+       if (!cp->flag)
+           continue;
+       cp->ox = cox, cp->oy = coy;
+       cp->sx = dp->sx - 2 * cp->bs;
+       coy += cp->sy + 2 * cp->bs;
+    }
+    dp->sy = coy;
+}
+
+static void coords2func(int wi, Gawdata_t * dp)
+{
+    Gawcarray_t *cp;
+    int cox, coy;
+    int ci, cj;
+
+    cox = coy = 0;
+    for (ci = 0, cj = 0; ci < dp->cj; ci++) {
+       cp = &dp->carray[ci];
+       if (!cp->flag)
+           continue;
+       cp->ox = cox, cp->oy = coy;
+       cj++;
+       cp->sx = dp->sx - 2 * cp->bs;
+       if (cj == 2)
+           cp->sy = dp->sy - coy - 2 * cp->bs;
+       coy += cp->sy + 2 * cp->bs;
+    }
+}
diff --git a/cmd/lefty/txtview.h b/cmd/lefty/txtview.h
new file mode 100644 (file)
index 0000000..23dc2c2
--- /dev/null
@@ -0,0 +1,37 @@
+/* $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 _TXTVIEW_H
+#define _TXTVIEW_H
+    void TXTinit(Grect_t);
+    void TXTterm(void);
+    int TXTmode(int argc, lvar_t * argv);
+    int TXTask(int argc, lvar_t * argv);
+    void TXTprocess(int, char *);
+    void TXTupdate(void);
+    void TXTtoggle(int, void *);
+#endif                         /* _TXTVIEW_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/cmd/lefty/ws/.cvsignore b/cmd/lefty/ws/.cvsignore
new file mode 100644 (file)
index 0000000..9fb9857
--- /dev/null
@@ -0,0 +1,6 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
diff --git a/cmd/lefty/ws/Makefile.am b/cmd/lefty/ws/Makefile.am
new file mode 100644 (file)
index 0000000..c58b50e
--- /dev/null
@@ -0,0 +1,5 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = x11
+
+EXTRA_DIST = mswin32 none