diff options
34 files changed, 996 insertions, 3 deletions
@@ -3,7 +3,7 @@ include Makefile.dirs all: @for i in $(MIXED_DIRS) ; \ do ( if [ -d $$i ] ; then cd $$i; $(MAKE) ; fi ) ; done - cd doc/src; $(MAKE) + @if [ -d doc/src ] ; then cd doc/src ; $(MAKE) ; fi clean cleanall: @for i in $(MIXED_DIRS) ; \ diff --git a/Makefile.common b/Makefile.common index d106e40..4dd5f25 100644 --- a/Makefile.common +++ b/Makefile.common @@ -50,7 +50,8 @@ ifeq ($(OS_NAME),MinGW) CC = gcc X_SUFFIX = dll DEFINES = -DNT -DMSW -OPT_CFLAGS = -funroll-loops +# assuming pd itself was either vc-compiled or declared -mms-bitfields too... +OPT_CFLAGS = -funroll-loops -mms-bitfields LFLAGS = -shared $(PD_DIR)/../bin/pd.dll -Wl,--enable-runtime-pseudo-reloc endif diff --git a/Makefile.dirs b/Makefile.dirs index 8f6e0d9..e5521a0 100644 --- a/Makefile.dirs +++ b/Makefile.dirs @@ -2,9 +2,11 @@ MIXED_DIRS = \ shared \ cyclone \ toxy \ + pddp \ xeq RELEASE_DIRS = \ cyclone \ toxy \ + pddp \ xeq diff --git a/ViCious/pddp/makefile b/ViCious/pddp/makefile new file mode 100644 index 0000000..fee9c8e --- /dev/null +++ b/ViCious/pddp/makefile @@ -0,0 +1,53 @@ +current: pddp
+
+.SUFFIXES: .dll .obj
+
+ROOTDIR = ..\..\..
+SRCDIR = $(ROOTDIR)\miXed\pddp
+BINDIR = $(ROOTDIR)\miXed\bin
+SHAREDDIR = $(ROOTDIR)\miXed\shared
+EXTDIR = e:\PureData\externals
+PDDIR = e:\PureData\pd
+VCLIBDIR = "e:\Program Files\Microsoft Visual Studio\Vc98\lib"
+ZIPCOMMAND = d:\dosowe\arc\info-zip\zip
+
+INCLUDES = /I. /I$(SHAREDDIR) /I$(PDDIR)\src
+CFLAGS = /W3 /WX /DNT /DMSW /DPD /nologo
+
+LIBS = $(VCLIBDIR)\libc.lib \
+ $(VCLIBDIR)\oldnames.lib \
+ $(VCLIBDIR)\wsock32.lib \
+ $(VCLIBDIR)\kernel32.lib \
+ $(VCLIBDIR)\uuid.lib \
+ $(PDDIR)\bin\pd.lib
+
+!INCLUDE objects
+!INCLUDE snapfiles
+!INCLUDE $(SRCDIR)\build_counter
+
+pddp: pddpboot pddplink.dll
+ @cd $(ROOTDIR)
+ @$(ZIPCOMMAND) pddp-$(PDDP_SNAPSHOT)-dll.zip $(SNAPFILES)
+
+pddpboot:
+ -@copy $(SRCDIR)\pddpboot.tcl $(EXTDIR)
+ -@copy $(SRCDIR)\pddpclient.tcl $(EXTDIR)
+ -@copy $(SRCDIR)\pddpserver.tcl $(EXTDIR)
+ -@copy $(SRCDIR)\pddpboot.tcl $(BINDIR)
+ -@copy $(SRCDIR)\pddpclient.tcl $(BINDIR)
+ -@copy $(SRCDIR)\pddpserver.tcl $(BINDIR)
+
+pddplink.dll: $(PDDPLINK_OBJECTS)
+ -link /dll /out:$@ $(LIBS) $** /export:$(@:.dll=_setup)
+ -@copy $@ $(EXTDIR)
+ -@move $@ $(BINDIR)
+ -del $(@:.dll=.exp)
+ -del $(@:.dll=.lib)
+
+.c.obj: ; cl /c $(CFLAGS) $(INCLUDES) /Fo$*.obj $*.c
+
+clean:
+ del *.obj
+ del *.exp
+ del *.lib
+ del *.dll
diff --git a/ViCious/pddp/objects b/ViCious/pddp/objects new file mode 100644 index 0000000..85d65b4 --- /dev/null +++ b/ViCious/pddp/objects @@ -0,0 +1,3 @@ +PDDPLINK_OBJECTS = $(SRCDIR)\pddplink.obj \
+ $(SHAREDDIR)\common\loud.obj \
+ $(SHAREDDIR)\common\os.obj
diff --git a/ViCious/pddp/snapfiles b/ViCious/pddp/snapfiles new file mode 100644 index 0000000..9dc4dbd --- /dev/null +++ b/ViCious/pddp/snapfiles @@ -0,0 +1,10 @@ +SNAPFILES = \
+ miXed\bin\pddplink.dll \
+ miXed\bin\pddpboot.tcl \
+ miXed\bin\pddpclient.tcl \
+ miXed\bin\pddpserver.tcl \
+ miXed\ViCious\pddp\makefile \
+ miXed\ViCious\pddp\objects \
+ miXed\ViCious\pddp\snapfiles \
+ miXed\LICENSE.txt \
+ miXed\test\pddp\*.*
diff --git a/ViCious/toxy/objects b/ViCious/toxy/objects index afe7080..6b7480a 100644 --- a/ViCious/toxy/objects +++ b/ViCious/toxy/objects @@ -14,6 +14,7 @@ TOW_OBJECTS = $(SRCDIR)\tow.obj \ WIDGET_OBJECTS = $(SRCDIR)\widget.obj \
$(SRCDIR)\widgettype.obj \
+ $(SRCDIR)\widgethandlers.obj \
$(SHAREDDIR)\unstable\forky.obj \
$(SHAREDDIR)\common\loud.obj \
$(SHAREDDIR)\common\grow.obj \
diff --git a/ViCious/toxy/snapfiles b/ViCious/toxy/snapfiles index ee0ad69..f2ef4c7 100644 --- a/ViCious/toxy/snapfiles +++ b/ViCious/toxy/snapfiles @@ -7,4 +7,5 @@ SNAPFILES = \ miXed\ViCious\toxy\snapfiles \
miXed\LICENSE.txt \
miXed\test\toxy\*.* \
+ miXed\test\toxy\editors\*.* \
miXed\test\toxy\stress\*.*
diff --git a/cyclone/hammer/testmess.c b/cyclone/hammer/testmess.c index 862e0f1..21ebb7b 100644 --- a/cyclone/hammer/testmess.c +++ b/cyclone/hammer/testmess.c @@ -251,7 +251,7 @@ static void testmess_setnames(t_testmess *x, t_symbol *s, if (natoms > 0 && testmess_makeroom(x, natoms * 2, 0)) { testmess_setnatoms(x, natoms); - fragile_class_getnames(x->x_message); + fragile_class_getnames(x->x_message, natoms); } } else diff --git a/cyclone/notes.txt b/cyclone/notes.txt index 6c62f4c..8ac4f0f 100644 --- a/cyclone/notes.txt +++ b/cyclone/notes.txt @@ -1,4 +1,6 @@ TODO for cyclone + * add folder class, so people forget about using tot for that purpose + * plug import into file/open DONE for cyclone diff --git a/pddp/Makefile b/pddp/Makefile new file mode 100644 index 0000000..b65c310 --- /dev/null +++ b/pddp/Makefile @@ -0,0 +1,10 @@ +ROOT_DIR = .. +redefault: pddpboot default + +pddpboot: $(ROOT_DIR)/bin/pddpboot.tcl \ + $(ROOT_DIR)/bin/pddpclient.tcl $(ROOT_DIR)/bin/pddpserver.tcl + +$(ROOT_DIR)/bin/%.tcl: %.tcl + cp $< $@ + +include $(ROOT_DIR)/Makefile.common diff --git a/pddp/Makefile.objects b/pddp/Makefile.objects new file mode 100644 index 0000000..3ad9b7a --- /dev/null +++ b/pddp/Makefile.objects @@ -0,0 +1,3 @@ +PDDPLINK_OBJECTS = \ +common/loud.o \ +common/os.o diff --git a/pddp/Makefile.sources b/pddp/Makefile.sources new file mode 100644 index 0000000..6349255 --- /dev/null +++ b/pddp/Makefile.sources @@ -0,0 +1,3 @@ +TYPES = PDDPLINK + +PDDPLINK_SOURCES = pddplink.c diff --git a/pddp/build_counter b/pddp/build_counter new file mode 100644 index 0000000..ddd868f --- /dev/null +++ b/pddp/build_counter @@ -0,0 +1,7 @@ +#define PDDP_VERSION "0.1" +#define PDDP_RELEASE "alpha" +#define PDDP_BUILD 1 + +#if 0 +PDDP_SNAPSHOT = 0.1-alpha1 +#endif diff --git a/pddp/notes.txt b/pddp/notes.txt new file mode 100644 index 0000000..e156fe1 --- /dev/null +++ b/pddp/notes.txt @@ -0,0 +1,10 @@ +TODO for pddp + * pddplink: + . standardize server's root directory (use $help_directory from pd.tk?) + . nonboxed version + +DONE for pddp + +alpha1 + * prototype versions of the external "pddplink" and two pd-gui extensions + "pddpserver.tcl", "pddpclient.tcl" diff --git a/pddp/pddp-all.exclude b/pddp/pddp-all.exclude new file mode 100644 index 0000000..d9e9df1 --- /dev/null +++ b/pddp/pddp-all.exclude @@ -0,0 +1,8 @@ +*~ +*.o +*.gz +*.html +*.out +ref +ref/* +dumpsetups diff --git a/pddp/pddp-help.include b/pddp/pddp-help.include new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pddp/pddp-help.include diff --git a/pddp/pddp-shared.include b/pddp/pddp-shared.include new file mode 100644 index 0000000..ab7ac29 --- /dev/null +++ b/pddp/pddp-shared.include @@ -0,0 +1,5 @@ +shared/shared.h +shared/common/loud.c +shared/common/loud.h +shared/common/os.c +shared/common/os.h diff --git a/pddp/pddp-test.exclude b/pddp/pddp-test.exclude new file mode 100644 index 0000000..6b3cc43 --- /dev/null +++ b/pddp/pddp-test.exclude @@ -0,0 +1,5 @@ +*~ +import-result.pd +import-debug.pd +temporary +temporary/* diff --git a/pddp/pddp-vicious.exclude b/pddp/pddp-vicious.exclude new file mode 100644 index 0000000..5e5a82e --- /dev/null +++ b/pddp/pddp-vicious.exclude @@ -0,0 +1,3 @@ +*~ +old +old/* diff --git a/pddp/pddpboot.tcl b/pddp/pddpboot.tcl new file mode 100755 index 0000000..3cec684 --- /dev/null +++ b/pddp/pddpboot.tcl @@ -0,0 +1,32 @@ +#!/bin/sh +# \ +exec tclsh "$0" -- "$@" + +# Synopsis +# test run: +# ./pddpboot.tcl [root [port [path]]] +# from Pd: +# source pddpboot.tcl +# ::pddp::srvUse root (or ::pddp::srvStart root [port]) +# ::pddp::cliOpen path +# ... (more "::pddp::cliOpen" calls) ... +# ::pddp::srvStop + +if {[namespace exists ::pddp]} { ;# created by pddplink's setup + puts stderr "Booting pddp" + set ::pddp::testrun 0 +} else { + puts stderr "Booting pddp, test run..." + namespace eval ::pddp { variable testrun 1 } +} + +puts stderr "current directory: [pwd]" + +source pddpclient.tcl +source pddpserver.tcl + +if {[info exists ::pddp::theDir]} { + puts stderr "restoring directory: $::pddp::theDir" + cd $::pddp::theDir + unset ::pddp::theDir +} diff --git a/pddp/pddpclient.tcl b/pddp/pddpclient.tcl new file mode 100644 index 0000000..7633394 --- /dev/null +++ b/pddp/pddpclient.tcl @@ -0,0 +1,82 @@ +# pddpclient.tcl + +# Synopsis +# not to be run by itself (see pddpboot.tcl) + +if {![namespace exists ::pddp]} { + puts stderr "Error: invalid invocation of pddpclient (boot pddp first)" + puts stderr "exiting..." + exit 1 +} + +if {$::pddp::testrun} { ;# true if sourced from standalone "pddpboot.tcl" + puts stderr "Loading pddpclient, test run..." + if {$argc > 3} { + set path [lindex $argv 3] + if {[string length $path]} { + puts stderr "Scheduling \"$path\" for opening" + after idle ::pddp::cliOpen $path + } + unset path + } +} else { + puts stderr "Loading pddpclient" +} + +namespace eval ::pddp { + variable theBrowserCommand + + switch -- $::tcl_platform(platform) { + unix { + switch -- $tcl_platform(os) { + Darwin { + set theBrowserCommand "sh -c open %s" + } + Linux { + foreach candidate \ + {firefox mozilla galeon konqueror netscape lynx} { + set browser [lindex [auto_execok $candidate] 0] + if {[string length $browser]} { + set theBrowserCommand "$browser %s &" + break + } + } + } + } + } + windows { + # should not this be just: [auto_execok start]? + set theBrowserCommand \ + "rundll32 url.dll,FileProtocolHandler file:%s &" + } + } +} + +proc ::pddp::cliError {err} { + puts stderr "Error in pddpclient: $err" +} + +proc ::pddp::cliOpen {path} { + if {[string first "://" $path] < 1} { + if {[info exists ::pddp::thePort]} { + set path "http://localhost:$::pddp::thePort/$path" + } else { + cliError "pddpserver not running" + return + } + } + variable theBrowserCommand + if {[string length $theBrowserCommand]} { + set command [format $theBrowserCommand $path] + puts stderr "pddpclient: open $command" + if {[catch {eval [list exec] $command} err]} { + if {[lindex $::errorCode 0] eq "CHILDSTATUS"} { + cliError "$err (child status [lindex $::errorCode 2])" + } else { + cliError $err + } + } + } else { + cliError "browser unavailable" + } +} diff --git a/pddp/pddplink.c b/pddp/pddplink.c new file mode 100644 index 0000000..d60a90a --- /dev/null +++ b/pddp/pddplink.c @@ -0,0 +1,136 @@ +/* Copyright (c) 2005 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include <stdio.h> +#include <string.h> +#include "m_pd.h" +#include "m_imp.h" /* FIXME need access to c_externdir... */ +#include "g_canvas.h" +#include "common/loud.h" +#include "build_counter" + +#ifdef KRZYSZCZ +//#define PDDPLINK_DEBUG +#endif + +enum { PDDPLINK_PD, PDDPLINK_HTML }; /* LATER add others */ + +typedef struct _pddplink +{ + t_object x_ob; + t_glist *x_glist; + int x_isboxed; + t_symbol *x_dirsym; + t_symbol *x_ulink; + t_atom x_openargs[2]; + int x_linktype; + int x_ishit; +} t_pddplink; + +static t_class *pddplink_class; + +/* FIXME need access to glob_pdobject... */ +static t_pd *pddplink_pdtarget(t_pddplink *x) +{ + t_pd *pdtarget = gensym("pd")->s_thing; + if (pdtarget && !strcmp(class_getname(*pdtarget), "pd")) + return (pdtarget); + else + return ((t_pd *)x); /* internal error */ +} + +static void pddplink_anything(t_pddplink *x, t_symbol *s, int ac, t_atom *av) +{ + if (x->x_ishit) + { + startpost("pddplink: internal error (%s", (s ? s->s_name : "")); + postatom(ac, av); + post(")"); + } +} + +static void pddplink_click(t_pddplink *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + x->x_ishit = 1; + switch (x->x_linktype) + { + case PDDPLINK_PD: + typedmess(pddplink_pdtarget(x), gensym("open"), 2, x->x_openargs); + break; + case PDDPLINK_HTML: + sys_vgui("after 0 {::pddp::cliOpen %s}\n", x->x_ulink->s_name); + break; + } + x->x_ishit = 0; +} + +#ifdef PDDPLINK_DEBUG +static void pddplink_debug(t_pddplink *x) +{ +} +#endif + +static void pddplink_free(t_pddplink *x) +{ +} + +static void *pddplink_new(t_symbol *s1, t_symbol *s2) +{ + t_pddplink *x = (t_pddplink *)pd_new(pddplink_class); + t_symbol *dirsym; + x->x_glist = canvas_getcurrent(); + x->x_isboxed = (s2 == gensym("box")); + x->x_dirsym = canvas_getdir(x->x_glist); /* FIXME */ + if (!s1 || s1 == &s_) + { + x->x_linktype = PDDPLINK_HTML; + x->x_ulink = gensym("index.html"); + } + else + { + int len = strlen(s1->s_name); + if (len > 3 && !strcmp(s1->s_name + len - 3, ".pd")) + x->x_linktype = PDDPLINK_PD; + else + x->x_linktype = PDDPLINK_HTML; + x->x_ulink = s1; + } + SETSYMBOL(&x->x_openargs[0], x->x_ulink); + SETSYMBOL(&x->x_openargs[1], x->x_dirsym); + x->x_ishit = 0; + if (x->x_isboxed) + { + inlet_new((t_object *)x, (t_pd *)x, 0, 0); + outlet_new((t_object *)x, &s_anything); + } + if (x->x_linktype == PDDPLINK_HTML) + sys_vgui("after 0 {::pddp::srvUse %s}\n", x->x_dirsym->s_name); + return (x); +} + +void pddplink_setup(void) +{ + t_symbol *dirsym; + post("this is pddplink %s, %s %s build...", + PDDP_VERSION, loud_ordinal(PDDP_BUILD), PDDP_RELEASE); + pddplink_class = class_new(gensym("pddplink"), + (t_newmethod)pddplink_new, + (t_method)pddplink_free, + sizeof(t_pddplink), + CLASS_NOINLET | CLASS_PATCHABLE, + A_DEFSYM, A_DEFSYM, 0); + class_addanything(pddplink_class, pddplink_anything); + class_addmethod(pddplink_class, (t_method)pddplink_click, + gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); +#ifdef PDDPLINK_DEBUG + class_addmethod(pddplink_class, (t_method)pddplink_debug, + gensym("debug"), 0); +#endif + dirsym = pddplink_class->c_externdir; /* FIXME */ + sys_vgui("namespace eval ::pddp {variable theDir [pwd]}; cd %s\n", + dirsym->s_name); + sys_gui("after 0 {source pddpboot.tcl}\n"); +} diff --git a/pddp/pddpserver.tcl b/pddp/pddpserver.tcl new file mode 100644 index 0000000..fe17948 --- /dev/null +++ b/pddp/pddpserver.tcl @@ -0,0 +1,490 @@ +# pddpserver.tcl + +# Synopsis +# not to be run by itself (see pddpboot.tcl) + +# based on: + +# Simple Sample httpd/1.[01] server +# Stephen Uhler (c) 1996-1997 Sun Microsystems + +# http://cvs.sourceforge.net/viewcvs.py/tclhttpd/tclhttpd/bin/mini/mini1.1.tcl + +# modified by krzYszcz (2005): +# putting per-server data and all commands in a namespace "::pddp" +# supporting sourcing from within Pd, through the "pddpboot.tcl" wrapper +# inserting the .pd handler +# lots of other changes, too many to list here (run "diff" if curious...) + +if {![namespace exists ::pddp]} { + puts stderr "Error: invalid invocation of pddpserver (boot pddp first)" + puts stderr "exiting..." + exit 1 +} + +if {$::pddp::testrun} { ;# true if sourced from standalone "pddpboot.tcl" + puts stderr "Loading pddpserver, test run..." + proc bgerror {msg} { + global errorInfo + puts stderr "bgerror: $msg\n$errorInfo" + } +} else { + puts stderr "Loading pddpserver" + catch {console show} +} + +namespace eval ::pddp { + variable thePort 8080 + variable theState + variable theMimeTypes + variable theErrors + variable theErrorFormat + + # "theState" contains the server state: + # root: the root of the document directory + # default: default document name + # listen: the main listening socket id + # naccepts: a count of accepted connections so far + # maxtime: the max time (msec) allowed to complete an http request + # maxused: the max # of requests for a socket + array set theState { + root "" + default index.html + listen "" + naccepts 0 + nrequests 0 + nerrors 0 + maxtime 600000 + maxused 25 + bufsize 32768 + } + + set theState(root) $env(HOME) + + array set theMimeTypes { + {} text/plain + .txt text/plain + .html text/html + .gif image/gif + .jpg image/jpeg + .pd text/html + } + + # HTTP/1.[01] error codes (the ones we use) + array set theErrors { + 204 {No Content} + 400 {Bad Request} + 404 {Not Found} + 405 {Method Not Allowed} + 408 {Request Timeout} + 411 {Length Required} + 419 {Expectation Failed} + 500 {Internal Server Error} + 503 {Service Unavailable} + 504 {Service Temporarily Unavailable} + 505 {HTTP Version Not Supported} + } + + # Generic error response + set theErrorFormat { + <title>Error: %1$s</title> + Got the error: <b>%2$s</b><br> + while trying to obtain <b>%3$s</b> + } +} + +proc ::pddp::srvUse {{root {}} {port 8080}} { + variable theState + if {[string length $theState(listen)]} { + if {[string length $root] && ![string equal $root $theState(root)]} { + srvLog $theState(listen) Warning "Redirection attempt for $root" + } + } else { + srvStart $root $port + } +} + +# Start the server by listening for connections on the desired port. + +proc ::pddp::srvStart {{root {}} {port 8080}} { + variable thePort + variable theState + + puts stderr "Starting pddp server on [info hostname]" + if {[string length $root]} { + set theState(root) $root + } + # we do not handle multiple pddpservers, LATER rethink + srvStop + array set theState [list naccepts 0 nrequests 0 nerrors 0] + + for { set thePort $port } {$thePort < 32767 } {incr thePort } { + if {[catch {set theState(listen) \ + [socket -server ::pddp::srvAccept $thePort]} res]} { + if {$thePort == 0} { + # FIXME this is a critical error + set thePort 8080 + } + } else { break } + } + if {$thePort == 32767} { + srvLog none Error "Could not find port available for listening" + } else { + if {$thePort == 0} { + set thePort [lindex [fconfigure $theState(listen) -sockname] 2] + } + srvLog $theState(listen) Port $thePort + srvLog $theState(listen) Root directory \"$root\" + } + after 120 update ;# FIXME might be needed on windows they say, test there + return $thePort +} + +proc ::pddp::srvStop {} { + variable thePort + variable theState + if {[string length $theState(listen)]} { + if {[catch {close $theState(listen)} res]} { + srvLog $theState(listen) Warning [list $res while closing socket] + } else { + srvLog $theState(listen) Closed. + } + set theState(listen) "" + update + } +} + +# Accept a new connection from the server and set up a handler +# to read the request from the client. + +proc ::pddp::srvAccept {sock ipaddr port} { + variable theState + variable theSockData$sock + # reject remote requests, LATER revisit + if {[string equal $ipaddr "127.0.0.1"]} { + incr theState(naccepts) + srvReset $sock $theState(maxused) + srvLog $sock Connect $ipaddr $port + } else { + srvLog $sock Warning "rejecting remote connection request from $ipaddr" + srvSockDone $sock 1 + } +} + +# Initialize or reset the socket state + +proc ::pddp::srvReset {sock nlft} { + variable theState + upvar 0 ::pddp::theSockData$sock sockData + array set sockData [list state start linemode 1 version 0 nleft $nlft] + set sockData(cancel) \ + [after $theState(maxtime) [list srvTimeout $sock]] + fconfigure $sock -blocking 0 -buffersize $theState(bufsize) \ + -translation {auto crlf} + fileevent $sock readable [list ::pddp::srvRead $sock] +} + +# Read data from a client request +# 1) read the request line +# 2) read the mime headers +# 3) read the additional data (if post && content-length not satisfied) + +proc ::pddp::srvRead {sock} { + variable theState + upvar 0 ::pddp::theSockData$sock sockData + + # Use line mode to read the request and the mime headers + + if {$sockData(linemode)} { + set readCount [gets $sock line] + set state [string compare $readCount 0],$sockData(state) + switch -glob -- $state { + 1,start { + if {[regexp {(HEAD|POST|GET) ([^?]+)\??([^ ]*) HTTP/1.([01])} \ + $line x sockData(proto) sockData(url) \ + sockData(query) sockData(version)]} { + set sockData(state) mime + incr theState(nrequests) + srvLog $sock Request $sockData(nleft) $line + } else { + srvError $sock 400 $line + } + } + 0,start { + srvLog $sock Warning "Initial blank line fetching request" + } + 1,mime { + if {[regexp {([^:]+):[ ]*(.*)} $line {} key value]} { + set key [string tolower $key] + set sockData(key) $key + if {[info exists sockData(mime,$key)]} { + append sockData(mime,$key) ", $value" + } else { + set sockData(mime,$key) $value + } + } elseif {[regexp {^[ ]+(.+)} $line {} value] && \ + [info exists sockData(key)]} { + append sockData(mime,$sockData($key)) " " $value + } else { + srvError $sock 400 $line + } + } + 0,mime { + if {$sockData(proto) == "POST" && \ + [info exists sockData(mime,content-length)]} { + set sockData(linemode) 0 + set sockData(count) $sockData(mime,content-length) + if {$sockData(version) && \ + [info exists sockData(mime,expect)]} { + if {$sockData(mime,expect) == "100-continue"} { + puts $sock "100 Continue HTTP/1.1\n" + flush $sock + } else { + srvError $sock 419 $sockData(mime,expect) + } + } + fconfigure $sock -translation {binary crlf} + } elseif {$sockData(proto) != "POST"} { + srvRespond $sock + } else { + srvError $sock 411 "Confusing mime headers" + } + } + -1,* { + if {[eof $sock]} { + srvLog $sock Error "Broken connection fetching request" + srvSockDone $sock 1 + } else { + puts stderr "Partial read, retrying" + } + } + default { + srvError $sock 404 "Invalid http state: $state,[eof $sock]" + } + } + + # Use counted mode to get the post data + + } elseif {![eof $sock]} { + append sockData(postdata) [read $sock $sockData(count)] + set sockData(count) [expr {$sockData(mime,content-length) - \ + [string length $sockData(postdata)]}] + if {$sockData(count) == 0} { + srvRespond $sock + } + } else { + srvLog $sock Error "Broken connection reading POST data" + srvSockDone $sock 1 + } +} + +# Done with the socket, either close it, or set up for next fetch +# sock: The socket I'm done with +# doclose: If true, close the socket, otherwise set up for reuse + +proc ::pddp::srvSockDone {sock doclose} { + variable theState + upvar 0 ::pddp::theSockData$sock sockData + + after cancel $sockData(cancel) + set nleft [incr sockData(nleft) -1] + unset sockData + if {$doclose} { + close $sock + } else { + srvReset $sock $nleft + } + return "" +} + +# A timeout happened + +proc ::pddp::srvTimeout {sock} { + srvError $sock 408 +} + +# FIXME test if "path" has its patch window already open... +proc ::pddp::srvPdHandler {sock path} { + set dir [file dirname $path] + set tail [file tail $path] + if {[catch {pd [concat pd open $tail $dir \;]}]} { + srvError $sock 504 + } else { + srvError $sock 204 + # FIXME raise; focus (test on windows) + } +} + +# Handle file system queries. This is a place holder for a more +# generic dispatch mechanism. + +proc ::pddp::srvRespond {sock} { + variable theState + variable theUrlCache + upvar 0 ::pddp::theSockData$sock sockData + + regsub {(^http://[^/]+)?} $sockData(url) {} url + if {[info exists theUrlCache($url)]} { + set mypath $theUrlCache($url) + } else { + set mypath [srvUrl2File $theState(root) $url] + if {[file isdirectory $mypath]} { + append mypath / $theState(default) + } + set theUrlCache($url) $mypath + } + if {[string length $mypath] == 0} { + srvError $sock 400 + } elseif {![file readable $mypath]} { + if {[string equal [file tail $mypath] "favicon.ico"]} { + srvError $sock 204 ;# FIXME design something + } else { + srvError $sock 404 $mypath + } + } else { + set ext [file extension $mypath] + + if {[string equal $ext ".pd"]} { + srvPdHandler $sock $mypath + return + } + + puts $sock "HTTP/1.$sockData(version) 200 Data follows" + puts $sock "Date: [srvGetDate [clock seconds]]" + puts $sock "Last-Modified: [srvGetDate [file mtime $mypath]]" + puts $sock "Content-Type: [srvContentType $ext]" + puts $sock "Content-Length: [file size $mypath]" + + ## Should also close socket if recvd connection close header + set doclose [expr {$sockData(nleft) == 0}] + + if {$doclose} { + puts $sock "Connection close:" + } elseif {$sockData(version) == 0 && \ + [info exists sockData(mime,connection)]} { + if {$sockData(mime,connection) == "Keep-Alive"} { + set doclose 0 + puts $sock "Connection: Keep-Alive" + } + } + puts $sock "" + flush $sock + + if {$sockData(proto) != "HEAD"} { + set in [open $mypath] + fconfigure $sock -translation binary + fconfigure $in -translation binary + fcopy $in $sock -command \ + [list ::pddp::srvCopyDone $in $sock $doclose] + } else { + srvSockDone $sock $doclose + } + } +} + +# Callback when file is done being output to client +# in: The fd for the file being copied +# sock: The client socket +# doclose: close the socket if true +# bytes: The # of bytes copied +# error: The error message (if any) + +proc ::pddp::srvCopyDone {in sock doclose bytes {error {}}} { + close $in + srvLog $sock Done $bytes bytes + srvSockDone $sock $doclose +} + +# Convert the file suffix into a mime type. + +proc ::pddp::srvContentType {ext} { + variable theMimeTypes + set type text/plain + catch {set type $theMimeTypes($ext)} + return $type +} + +# Respond with an error reply +# sock: The socket handle to the client +# code: The httpd error code +# args: Additional information for error logging + +proc ::pddp::srvError {sock code args} { + variable theState + variable theErrors + variable theErrorFormat + upvar 0 ::pddp::theSockData$sock sockData + + append sockData(url) "" + incr theState(nerrors) + set message [format $theErrorFormat $code $theErrors($code) $sockData(url)] + append head "HTTP/1.$sockData(version) $code $theErrors($code)" \n + append head "Date: [srvGetDate [clock seconds]]" \n + append head "Connection: close" \n + append head "Content-Length: [string length $message]" \n + + # Because there is an error condition, the socket may be "dead" + + catch { + fconfigure $sock -translation crlf + puts -nonewline $sock $head\n$message + flush $sock + } reason + srvSockDone $sock 1 + if {$code < 300} {set status Status} else {set status Error} + srvLog $sock $status $code $theErrors($code) $args $reason +} + +# Generate a date string in HTTP format. + +proc ::pddp::srvGetDate {seconds} { + return [clock format $seconds -format {%a, %d %b %Y %T %Z}] +} + +# Log an Httpd transaction. +# This should be replaced as needed. + +proc ::pddp::srvLog {sock args} { + puts stderr "pddp log ($sock): $args" +} + +# Convert a url into a pathname. (UNIX version only) +# This is probably not right, and belongs somewhere else. +# - Remove leading http://... if any +# - Collapse all /./ and /../ constructs +# - expand %xx sequences -> disallow "/"'s and "."'s due to expansions + +proc ::pddp::srvUrl2File {root url} { + regsub -all {//+} $url / url ;# collapse multiple /'s + while {[regsub -all {/\./} $url / url]} {} ;# collapse /./ + while {[regsub -all {/\.\.(/|$)} $url /\x81\\1 url]} {} ;# mark /../ + while {[regsub "/\[^/\x81]+/\x81/" $url / url]} {} ;# collapse /../ + if {![regexp "\x81|%2\[eEfF]" $url]} { ;# invalid /../, / or . ? + return $root[srvCgiMap $url] + } else { + return "" + } +} + +# Decode url-encoded strings. + +proc ::pddp::srvCgiMap {data} { + regsub -all {([][$\\])} $data {\\\1} data + regsub -all {%([0-9a-fA-F][0-9a-fA-F])} $data {[format %c 0x\1]} data + return [subst $data] +} + +if {$::pddp::testrun} { ;# true if tested as a standalone script + if {$argc > 1} { + set root [lindex $argv 1] + set port [lindex $argv 2] + if {![string is integer -strict $port]} { + set port 8080 + } + } else { + set root $env(HOME) + set port 8080 + } + ::pddp::srvStart $root $port + vwait forever +} diff --git a/test/pddp/pddp.css b/test/pddp/pddp.css new file mode 100644 index 0000000..40042d1 --- /dev/null +++ b/test/pddp/pddp.css @@ -0,0 +1,18 @@ +body { + color: brown ; + background: gray url(test.bg.jpg) ; + margin-top: 10 ; + font-family: Helvetica, sans-serif ; + font-size: larger ; +} + +pre.programlisting { + display: table ; + background-color: #EEEEEE ; + padding: 2pt ; + border-style: solid ; + border-width: 1pt ; + border-color: #000000 ; + border-spacing: 3pt ; + margin: 3pt ; +} diff --git a/test/pddp/pddplink-test-01.html b/test/pddp/pddplink-test-01.html new file mode 100644 index 0000000..b73ea54 --- /dev/null +++ b/test/pddp/pddplink-test-01.html @@ -0,0 +1,5 @@ +<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>first simple test</title><link rel="stylesheet" href="pddp.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.66.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id2447708"></a>first simple test</h2></div></div><hr></div><p>test patch is here: <a href="pddplink-test-01.pd" target="_top">pddplink-test-01.pd</a></p><p>The above link works if you opened this page from Pd, + for example, by clicking on an object + </p><pre class="programlisting"><span><b class="command">pddplink</b></span> pddplink-test-01.html</pre><p> + </p><p>Pd links should also work in all pages opened through any other, + initiating, Pd-opened page.</p><p>Another simple test page is here: <a href="pddplink-test-02.html" target="_top">pddplink-test-02.html</a></p></div></body></html> diff --git a/test/pddp/pddplink-test-01.pd b/test/pddp/pddplink-test-01.pd new file mode 100644 index 0000000..f5f1403 --- /dev/null +++ b/test/pddp/pddplink-test-01.pd @@ -0,0 +1,8 @@ +#N canvas 232 221 450 300 12; +#X obj 61 83 pddplink pddplink-test-01.html; +#X text 21 12 first entry; +#X text 59 47 linking to a local html page:; +#X text 58 130 linking to a remote html page:; +#X obj 60 166 pddplink http://puredata.info; +#X obj 59 254 pddplink pddplink-test-02.pd; +#X text 57 218 linking to another local patch:; diff --git a/test/pddp/pddplink-test-01.xml b/test/pddp/pddplink-test-01.xml new file mode 100644 index 0000000..0282ea6 --- /dev/null +++ b/test/pddp/pddplink-test-01.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!DOCTYPE sect1 PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<sect1> + <title>first simple test</title> + + <para>test patch is here: <ulink url="pddplink-test-01.pd"/></para> + + <para>The above link works if you opened this page from Pd, + for example, by clicking on an object + <programlisting><command>pddplink</command> pddplink-test-01.html</programlisting> + </para> + + <para>Pd links should also work in all pages opened through any other, + initiating, Pd-opened page.</para> + + <para>Another simple test page is here: <ulink url="pddplink-test-02.html"/></para> +</sect1> diff --git a/test/pddp/pddplink-test-02.html b/test/pddp/pddplink-test-02.html new file mode 100644 index 0000000..aed49db --- /dev/null +++ b/test/pddp/pddplink-test-02.html @@ -0,0 +1,5 @@ +<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>second simple test</title><link rel="stylesheet" href="pddp.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.66.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id2447708"></a>second simple test</h2></div></div><hr></div><p>test patch is here: <a href="pddplink-test-02.pd" target="_top">pddplink-test-02.pd</a></p><p>The above link works if you opened this page from Pd, + for example, by clicking on an object + </p><pre class="programlisting"><span><b class="command">pddplink</b></span> pddplink-test-02.html</pre><p> + </p><p>Pd links should also work in all pages opened through any other, + initiating, Pd-opened page.</p><p>Another simple test page is here: <a href="pddplink-test-01.html" target="_top">pddplink-test-01.html</a></p></div></body></html> diff --git a/test/pddp/pddplink-test-02.pd b/test/pddp/pddplink-test-02.pd new file mode 100644 index 0000000..eb67411 --- /dev/null +++ b/test/pddp/pddplink-test-02.pd @@ -0,0 +1,8 @@ +#N canvas 332 321 450 300 12; +#X obj 61 83 pddplink pddplink-test-02.html; +#X text 21 12 second entry; +#X text 59 47 linking to a local html page:; +#X text 58 130 linking to a remote html page:; +#X obj 60 166 pddplink http://puredata.info; +#X obj 59 254 pddplink pddplink-test-01.pd; +#X text 57 218 linking to another local patch:; diff --git a/test/pddp/pddplink-test-02.xml b/test/pddp/pddplink-test-02.xml new file mode 100644 index 0000000..349f95e --- /dev/null +++ b/test/pddp/pddplink-test-02.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!DOCTYPE sect1 PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<sect1> + <title>second simple test</title> + + <para>test patch is here: <ulink url="pddplink-test-02.pd"/></para> + + <para>The above link works if you opened this page from Pd, + for example, by clicking on an object + <programlisting><command>pddplink</command> pddplink-test-02.html</programlisting> + </para> + + <para>Pd links should also work in all pages opened through any other, + initiating, Pd-opened page.</para> + + <para>Another simple test page is here: <ulink url="pddplink-test-01.html"/></para> +</sect1> diff --git a/test/pddp/test.bg.jpg b/test/pddp/test.bg.jpg Binary files differnew file mode 100644 index 0000000..8224bbc --- /dev/null +++ b/test/pddp/test.bg.jpg diff --git a/test/pddp/toonepage b/test/pddp/toonepage new file mode 100755 index 0000000..f59273a --- /dev/null +++ b/test/pddp/toonepage @@ -0,0 +1,21 @@ +#!/bin/bash + +usage () { + cat << EOF +usage: toonepage <pddpsource>.xml +EOF +} + +[ "$#" == 0 ] && { usage; exit 1; } + +SOURCE_NAME="$(basename $1)" +SOURCE_STUB="$(basename $SOURCE_NAME .xml)" + +[ $SOURCE_STUB == $SOURCE_NAME ] && { usage; exit 1; } + +xmllint >/dev/null --xinclude --postvalid $1 +xsltproc --nonet --xinclude \ + --stringparam html.stylesheet pddp.css \ + -o "$SOURCE_STUB.html" \ + /usr/share/xml/docbook/stylesheet/nwalsh/html/docbook.xsl \ + $1 diff --git a/test/pddp/topdf b/test/pddp/topdf new file mode 100755 index 0000000..adf4b43 --- /dev/null +++ b/test/pddp/topdf @@ -0,0 +1,23 @@ +#!/bin/bash + +usage () { + cat << EOF +usage: topdf <pddpsource>.xml +EOF +} + +[ "$#" == 0 ] && { usage; exit 1; } + +SOURCE_NAME="$(basename $1)" +SOURCE_STUB="$(basename $SOURCE_NAME .xml)" + +[ $SOURCE_STUB == $SOURCE_NAME ] && { usage; exit 1; } + +xmllint >/dev/null --xinclude --postvalid $1 +xsltproc --nonet --xinclude \ + -o "$SOURCE_STUB.fo" \ + /usr/share/xml/docbook/stylesheet/nwalsh/fo/docbook.xsl \ + $1 + +# FIXME need to run this twice, it should be check-wrapped too +pdfxmltex "$SOURCE_STUB.fo" |