From c2645dc4003b1391aba9b387a79a66cff1e63d3e Mon Sep 17 00:00:00 2001
From: Thomas Grill <xovo@users.sourceforge.net>
Date: Tue, 22 Oct 2002 23:16:30 +0000
Subject: This commit was generated by cvs2svn to compensate for changes in
 r189, which included commits to RCS files with non-trunk default branches.

svn path=/trunk/; revision=190
---
 externals/grill/pool/main.cpp  | 905 +++++++++++++++++++++++++++++++++++++++++
 externals/grill/pool/pool.cpp  | 645 +++++++++++++++++++++++++++++
 externals/grill/pool/pool.cw   | Bin 0 -> 58761 bytes
 externals/grill/pool/pool.dsp  | 103 +++++
 externals/grill/pool/pool.h    | 124 ++++++
 externals/grill/pool/pool.help | Bin 0 -> 5345 bytes
 externals/grill/pool/pool.pd   | 201 +++++++++
 7 files changed, 1978 insertions(+)
 create mode 100644 externals/grill/pool/main.cpp
 create mode 100644 externals/grill/pool/pool.cpp
 create mode 100755 externals/grill/pool/pool.cw
 create mode 100644 externals/grill/pool/pool.dsp
 create mode 100644 externals/grill/pool/pool.h
 create mode 100755 externals/grill/pool/pool.help
 create mode 100644 externals/grill/pool/pool.pd

(limited to 'externals/grill/pool')

diff --git a/externals/grill/pool/main.cpp b/externals/grill/pool/main.cpp
new file mode 100644
index 00000000..5d0cef10
--- /dev/null
+++ b/externals/grill/pool/main.cpp
@@ -0,0 +1,905 @@
+/* 
+
+pool - hierarchical storage object for PD and Max/MSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.  
+
+*/
+
+#include "pool.h"
+
+class pool:
+	public flext_base
+{
+	FLEXT_HEADER_S(pool,flext_base,setup)
+
+public:
+	pool(I argc,const A *argv);
+	~pool();
+
+	static V setup(t_class *);
+
+	pooldata *Pool() { return pl; }
+
+protected:
+
+	// switch to other pool
+	V m_pool(I argc,const A *argv);
+
+	// clear all data in pool
+	V m_reset();
+
+	// output absolute directory paths?
+	V m_absdir(BL abs) { absdir = abs; }
+	// always output current directory
+	V m_echodir(BL e) { echo = e; }
+
+	// handle directories
+	V m_getdir();
+
+	V m_mkdir(I argc,const A *argv,BL abs = true);		// make and change to dir
+	V m_chdir(I argc,const A *argv,BL abs = true);		// change to dir
+	V m_rmdir(I argc,const A *argv,BL abs = true);		// remove dir
+	V m_updir(I argc,const A *argv);		// one or more levels up
+
+	V m_mksub(I argc,const A *argv) { m_mkdir(argc,argv,false); }
+	V m_chsub(I argc,const A *argv) { m_chdir(argc,argv,false); }
+	V m_rmsub(I argc,const A *argv) { m_rmdir(argc,argv,false); }
+
+	// handle data
+	V m_set(I argc,const A *argv) { set(MakeSymbol("set"),argc,argv,true); }
+	V m_add(I argc,const A *argv) { set(MakeSymbol("add"),argc,argv,false); }
+	V m_clr(I argc,const A *argv);
+	V m_clrall();	// only values
+	V m_clrrec();	// also subdirectories
+	V m_clrsub();	// only subdirectories
+	V m_get(I argc,const A *argv);
+	V m_getall();	// only values
+	V m_getrec(I argc,const A *argv);	// also subdirectories
+	V m_getsub(I argc,const A *argv);	// only subdirectories
+	V m_cntall();	// only values
+	V m_cntrec(I argc,const A *argv);	// also subdirectories
+	V m_cntsub(I argc,const A *argv);	// only subdirectories
+
+	// cut/copy/paste
+	V m_paste(I argc,const A *argv) { paste(MakeSymbol("paste"),argc,argv,true); } // paste contents of clipboard
+	V m_pasteadd(I argc,const A *argv) { paste(MakeSymbol("pasteadd"),argc,argv,false); } // paste but don't replace
+	V m_clrclip();  // clear clipboard
+	V m_cut(I argc,const A *argv) { copy(MakeSymbol("cut"),argc,argv,true); } // cut value into clipboard
+	V m_copy(I argc,const A *argv) { copy(MakeSymbol("copy"),argc,argv,false); } 	// copy value into clipboard
+	V m_cutall() { copyall(MakeSymbol("cutall"),true,0); }   // cut all values in current directory into clipboard
+	V m_copyall() { copyall(MakeSymbol("copyall"),false,0); }   // copy all values in current directory into clipboard
+	V m_cutrec(I argc,const A *argv) { copyrec(MakeSymbol("cutrec"),argc,argv,true); }   // cut directory (and subdirs) into clipboard
+	V m_copyrec(I argc,const A *argv) { copyrec(MakeSymbol("copyrec"),argc,argv,false); }   // cut directory (and subdirs) into clipboard
+
+	// load/save from/to file
+	V m_load(I argc,const A *argv);
+	V m_save(I argc,const A *argv);
+
+	// load directories
+	V m_lddir(I argc,const A *argv);   // load values into current dir
+	V m_ldrec(I argc,const A *argv);   // load values recursively
+
+	// save directories
+	V m_svdir(I argc,const A *argv);   // save values in current dir
+	V m_svrec(I argc,const A *argv);   // save values recursively
+
+private:
+	static BL KeyChk(const A &a);
+	static BL ValChk(I argc,const A *argv);
+	static BL ValChk(const AtomList &l) { return ValChk(l.Count(),l.Atoms()); }
+	V ToOutAtom(I ix,const A &a);
+
+	V set(const S *tag,I argc,const A *argv,BL over);
+	V getdir(const S *tag);
+	I getrec(const S *tag,I level,BL cntonly = false,const AtomList &rdir = AtomList());
+	I getsub(const S *tag,I level,BL cntonly = false,const AtomList &rdir = AtomList());
+
+	V paste(const S *tag,I argc,const A *argv,BL repl);
+	V copy(const S *tag,I argc,const A *argv,BL cut);
+	V copyall(const S *tag,BL cut,I lvls);
+	V copyrec(const S *tag,I argc,const A *argv,BL cut);
+
+	V echodir() { if(echo) getdir(MakeSymbol("echo")); }
+
+	BL priv,absdir,echo;
+	pooldata *pl;
+	AtomList curdir;
+	pooldir *clip;
+
+	static pooldata *head,*tail;
+
+	V SetPool(const S *s);
+	V FreePool();
+
+	static pooldata *GetPool(const S *s);
+	static V RmvPool(pooldata *p);
+
+	FLEXT_CALLBACK_V(m_pool)
+	FLEXT_CALLBACK(m_reset)
+	FLEXT_CALLBACK_B(m_absdir)
+	FLEXT_CALLBACK_B(m_echodir)
+	FLEXT_CALLBACK(m_getdir)
+	FLEXT_CALLBACK_V(m_mkdir)
+	FLEXT_CALLBACK_V(m_chdir)
+	FLEXT_CALLBACK_V(m_updir)
+	FLEXT_CALLBACK_V(m_rmdir)
+	FLEXT_CALLBACK_V(m_mksub)
+	FLEXT_CALLBACK_V(m_chsub)
+	FLEXT_CALLBACK_V(m_rmsub)
+
+	FLEXT_CALLBACK_V(m_set)
+	FLEXT_CALLBACK_V(m_add)
+	FLEXT_CALLBACK_V(m_clr)
+	FLEXT_CALLBACK(m_clrall)
+	FLEXT_CALLBACK(m_clrrec)
+	FLEXT_CALLBACK(m_clrsub)
+	FLEXT_CALLBACK_V(m_get)
+	FLEXT_CALLBACK(m_getall)
+	FLEXT_CALLBACK_V(m_getrec)
+	FLEXT_CALLBACK_V(m_getsub)
+	FLEXT_CALLBACK(m_cntall)
+	FLEXT_CALLBACK_V(m_cntrec)
+	FLEXT_CALLBACK_V(m_cntsub)
+
+	FLEXT_CALLBACK_V(m_paste)
+	FLEXT_CALLBACK_V(m_pasteadd)
+	FLEXT_CALLBACK(m_clrclip)
+	FLEXT_CALLBACK_V(m_copy)
+	FLEXT_CALLBACK_V(m_cut)
+	FLEXT_CALLBACK(m_copyall)
+	FLEXT_CALLBACK(m_cutall)
+	FLEXT_CALLBACK_V(m_copyrec)
+	FLEXT_CALLBACK_V(m_cutrec)
+
+	FLEXT_CALLBACK_V(m_load)
+	FLEXT_CALLBACK_V(m_save)
+	FLEXT_CALLBACK_V(m_lddir)
+	FLEXT_CALLBACK_V(m_ldrec)
+	FLEXT_CALLBACK_V(m_svdir)
+	FLEXT_CALLBACK_V(m_svrec)
+};
+
+FLEXT_NEW_V("pool",pool);
+
+
+pooldata *pool::head,*pool::tail;
+
+
+V pool::setup(t_class *)
+{
+	post("");
+	post("pool %s - hierarchical storage object, (C)2002 Thomas Grill",POOL_VERSION);
+	post("");
+
+	head = tail = NULL;
+}
+
+pool::pool(I argc,const A *argv):
+	absdir(true),echo(false),pl(NULL),
+	clip(NULL)
+{
+	SetPool(argc >= 1 && IsSymbol(argv[0])?GetSymbol(argv[0]):NULL);
+
+	AddInAnything();
+	AddOutList();
+	AddOutAnything();
+	AddOutList();
+	AddOutAnything();
+
+	FLEXT_ADDMETHOD_(0,"set",m_set);
+	FLEXT_ADDMETHOD_(0,"add",m_add);
+	FLEXT_ADDMETHOD_(0,"reset",m_reset);
+	FLEXT_ADDMETHOD_(0,"absdir",m_absdir);
+	FLEXT_ADDMETHOD_(0,"echodir",m_echodir);
+	FLEXT_ADDMETHOD_(0,"getdir",m_getdir);
+	FLEXT_ADDMETHOD_(0,"mkdir",m_mkdir);
+	FLEXT_ADDMETHOD_(0,"chdir",m_chdir);
+	FLEXT_ADDMETHOD_(0,"rmdir",m_rmdir);
+	FLEXT_ADDMETHOD_(0,"updir",m_updir);
+	FLEXT_ADDMETHOD_(0,"mksub",m_mksub);
+	FLEXT_ADDMETHOD_(0,"chsub",m_chsub);
+	FLEXT_ADDMETHOD_(0,"rmsub",m_rmsub);
+
+	FLEXT_ADDMETHOD_(0,"set",m_set);
+	FLEXT_ADDMETHOD_(0,"clr",m_clr);
+	FLEXT_ADDMETHOD_(0,"clrall",m_clrall);
+	FLEXT_ADDMETHOD_(0,"clrrec",m_clrrec);
+	FLEXT_ADDMETHOD_(0,"clrsub",m_clrsub);
+	FLEXT_ADDMETHOD_(0,"get",m_get);
+	FLEXT_ADDMETHOD_(0,"getall",m_getall);
+	FLEXT_ADDMETHOD_(0,"getrec",m_getrec);
+	FLEXT_ADDMETHOD_(0,"getsub",m_getsub);
+	FLEXT_ADDMETHOD_(0,"cntall",m_cntall);
+	FLEXT_ADDMETHOD_(0,"cntrec",m_cntrec);
+	FLEXT_ADDMETHOD_(0,"cntsub",m_cntsub);
+
+	FLEXT_ADDMETHOD_(0,"paste",m_paste);
+	FLEXT_ADDMETHOD_(0,"pasteadd",m_pasteadd);
+	FLEXT_ADDMETHOD_(0,"clrclip",m_clrclip);
+	FLEXT_ADDMETHOD_(0,"cut",m_cut);
+	FLEXT_ADDMETHOD_(0,"copy",m_copy);
+	FLEXT_ADDMETHOD_(0,"cutall",m_cutall);
+	FLEXT_ADDMETHOD_(0,"copyall",m_copyall);
+	FLEXT_ADDMETHOD_(0,"cutrec",m_cutrec);
+	FLEXT_ADDMETHOD_(0,"copyrec",m_copyrec);
+
+	FLEXT_ADDMETHOD_(0,"load",m_load);
+	FLEXT_ADDMETHOD_(0,"save",m_save);
+	FLEXT_ADDMETHOD_(0,"lddir",m_lddir);
+	FLEXT_ADDMETHOD_(0,"ldrec",m_ldrec);
+	FLEXT_ADDMETHOD_(0,"svdir",m_svdir);
+	FLEXT_ADDMETHOD_(0,"svrec",m_svrec);
+}
+
+pool::~pool()
+{
+	FreePool();
+}
+
+V pool::SetPool(const S *s)
+{
+	if(pl) FreePool();
+
+	if(s) {
+		priv = false;
+		pl = GetPool(s);
+	}
+	else {
+		priv = true;
+		pl = new pooldata;
+	}
+}
+
+V pool::FreePool()
+{
+	curdir(); // reset current directory
+
+	if(pl) {
+		if(!priv) 
+			RmvPool(pl);
+		else
+			delete pl;
+		pl = NULL;
+	}
+
+	if(clip) { delete clip; clip = NULL; }
+}
+
+V pool::m_pool(I argc,const A *argv) 
+{
+	const S *s = NULL;
+	if(argc > 0) {
+		if(argc > 1) post("%s - pool: superfluous arguments ignored",thisName());
+		s = GetASymbol(argv[0]);
+		if(!s) post("%s - pool: invalid pool name, pool set to private",thisName());
+	}
+
+	SetPool(s);
+}
+
+V pool::m_reset() 
+{
+	pl->Reset();
+}
+
+
+V pool::getdir(const S *tag)
+{
+	ToOutAnything(3,tag,0,NULL);
+	ToOutList(2,curdir);
+}
+
+V pool::m_getdir() { getdir(MakeSymbol("getdir")); }
+
+V pool::m_mkdir(I argc,const A *argv,BL abs)
+{
+	if(!ValChk(argc,argv))
+		post("%s - mkdir: invalid directory name",thisName());
+	else {
+		AtomList ndir;
+		if(abs) ndir(argc,argv);
+		else (ndir = curdir).Append(argc,argv);
+		if(!pl->MkDir(ndir)) {
+			post("%s - mkdir: directory couldn't be created",thisName());
+		}
+	}
+
+	echodir();
+}
+
+V pool::m_chdir(I argc,const A *argv,BL abs)
+{
+	if(!ValChk(argc,argv)) 
+		post("%s - chdir: invalid directory name",thisName());
+	else {
+		AtomList prv(curdir);
+		if(abs) curdir(argc,argv);
+		else curdir.Append(argc,argv);
+		if(!pl->ChkDir(curdir)) {
+			post("%s - chdir: directory couldn't be changed",thisName());
+			curdir = prv;
+		}
+	}
+
+	echodir();
+}
+
+V pool::m_updir(I argc,const A *argv)
+{
+	I lvls = 1;
+	if(argc > 0) {
+		if(CanbeInt(argv[0])) {
+			if(argc > 1)
+				post("%s - updir: superfluous arguments ignored",thisName());
+			lvls = GetAInt(argv[0]);
+			if(lvls < 0)
+				post("%s - updir: invalid level specification - set to 1",thisName());
+		}
+		else
+			post("%s - updir: invalid level specification - set to 1",thisName());
+	}
+
+	AtomList prv(curdir);
+
+	if(lvls > curdir.Count()) {
+		post("%s - updir: level exceeds directory depth - corrected",thisName());
+		curdir();
+	}
+	else
+		curdir.Part(0,curdir.Count()-lvls);
+
+	if(!pl->ChkDir(curdir)) {
+		post("%s - updir: directory couldn't be changed",thisName());
+		curdir = prv;
+	}
+
+	echodir();
+}
+
+V pool::m_rmdir(I argc,const A *argv,BL abs)
+{
+	if(abs) curdir(argc,argv);
+	else curdir.Append(argc,argv);
+
+	if(!pl->RmDir(curdir)) 
+		post("%s - rmdir: directory couldn't be removed",thisName());
+	curdir();
+
+	echodir();
+}
+
+V pool::set(const S *tag,I argc,const A *argv,BL over)
+{
+	if(!argc || !KeyChk(argv[0])) 
+		post("%s - %s: invalid key",thisName(),GetString(tag));
+	else if(!ValChk(argc-1,argv+1)) {
+		post("%s - %s: invalid data values",thisName(),GetString(tag));
+	}
+	else 
+		if(!pl->Set(curdir,argv[0],new AtomList(argc-1,argv+1),over))
+			post("%s - %s: value couldn't be set",thisName(),GetString(tag));
+
+	echodir();
+}
+
+V pool::m_clr(I argc,const A *argv)
+{
+	if(!argc || !KeyChk(argv[0]))
+		post("%s - clr: invalid key",thisName());
+	else {
+		if(argc > 1) 
+			post("%s - clr: superfluous arguments ignored",thisName());
+
+		if(!pl->Clr(curdir,argv[0]))
+			post("%s - clr: value couldn't be cleared",thisName());
+	}
+
+	echodir();
+}
+
+V pool::m_clrall()
+{
+	if(!pl->ClrAll(curdir,false))
+		post("%s - clrall: values couldn't be cleared",thisName());
+
+	echodir();
+}
+
+V pool::m_clrrec()
+{
+	if(!pl->ClrAll(curdir,true))
+		post("%s - clrrec: values couldn't be cleared",thisName());
+
+	echodir();
+}
+
+V pool::m_clrsub()
+{
+	if(!pl->ClrAll(curdir,true,true))
+		post("%s - clrsub: directories couldn't be cleared",thisName());
+
+	echodir();
+}
+
+V pool::m_get(I argc,const A *argv)
+{
+	if(!argc || !KeyChk(argv[0]))
+		post("%s - get: invalid key",thisName());
+	else {
+		if(argc > 1) 
+			post("%s - get: superfluous arguments ignored",thisName());
+
+		AtomList *r = pl->Get(curdir,argv[0]);
+
+		ToOutAnything(3,MakeSymbol("get"),0,NULL);
+		if(absdir)
+			ToOutList(2,curdir);
+		else
+			ToOutList(2,0,NULL);
+		ToOutAtom(1,argv[0]);
+		if(r) {
+			ToOutList(0,*r);
+			delete r;
+		}
+		else
+			ToOutBang(0);
+	}
+
+	echodir();
+}
+
+I pool::getrec(const S *tag,I level,BL cntonly,const AtomList &rdir)
+{
+	AtomList gldir(curdir);
+	gldir.Append(rdir);
+
+	I ret = 0;
+
+	if(cntonly)
+		ret = pl->CntAll(gldir);
+	else {
+		A *k;
+		AtomList *r;
+		I cnt = pl->GetAll(gldir,k,r);
+		if(!k) 
+			post("%s - %s: error retrieving values",thisName(),GetString(tag));
+		else {
+			for(I i = 0; i < cnt; ++i) {
+				ToOutAnything(3,tag,0,NULL);
+				ToOutList(2,absdir?gldir:rdir);
+				ToOutAtom(1,k[i]);
+				ToOutList(0,r[i]);
+			}
+			delete[] k;
+			delete[] r;
+		}
+		ret = cnt;
+	}
+
+	if(level != 0) {
+		const A **r;
+		I cnt = pl->GetSub(gldir,r);
+		if(!r) 
+			post("%s - %s: error retrieving directories",thisName(),GetString(tag));
+		else {
+			I lv = level > 0?level-1:-1;
+			for(I i = 0; i < cnt; ++i) {
+				ret += getrec(tag,lv,cntonly,AtomList(rdir).Append(*r[i]));
+			}
+			delete[] r;
+		}
+	}
+	
+	return ret;
+}
+
+V pool::m_getall()
+{
+	getrec(MakeSymbol("getall"),0);
+	ToOutBang(3);
+
+	echodir();
+}
+
+V pool::m_getrec(I argc,const A *argv)
+{
+	I lvls = -1;
+	if(argc > 0) {
+		if(CanbeInt(argv[0])) {
+			if(argc > 1)
+				post("%s - getrec: superfluous arguments ignored",thisName());
+			lvls = GetAInt(argv[0]);
+		}
+		else 
+			post("%s - getrec: invalid level specification - set to infinite",thisName());
+	}
+	getrec(MakeSymbol("getrec"),lvls);
+	ToOutBang(3);
+
+	echodir();
+}
+
+
+I pool::getsub(const S *tag,I level,BL cntonly,const AtomList &rdir)
+{
+	AtomList gldir(curdir);
+	gldir.Append(rdir);
+	
+	I ret = 0;
+
+	const A **r;
+	I cnt = pl->GetSub(gldir,r);
+	if(!r) 
+		post("%s - %s: error retrieving directories",thisName(),GetString(tag));
+	else {
+		I lv = level > 0?level-1:-1;
+		for(I i = 0; i < cnt; ++i) {
+			AtomList ndir(absdir?gldir:rdir);
+			ndir.Append(*r[i]);
+
+			if(!cntonly) {
+				ToOutAnything(3,tag,0,NULL);
+				ToOutList(2,curdir);
+				ToOutList(1,ndir);
+			}
+
+			if(level != 0)
+				ret += getsub(tag,lv,cntonly,AtomList(rdir).Append(*r[i]));
+		}
+		delete[] r;
+	}
+	
+	return ret;
+}
+
+V pool::m_getsub(I argc,const A *argv)
+{
+	I lvls = 0;
+	if(argc > 0) {
+		if(CanbeInt(argv[0])) {
+			if(argc > 1)
+				post("%s - getsub: superfluous arguments ignored",thisName());
+			lvls = GetAInt(argv[0]);
+		}
+		else 
+			post("%s - getsub: invalid level specification - set to 0",thisName());
+	}
+
+	getsub(MakeSymbol("getsub"),lvls);
+	ToOutBang(3);
+
+	echodir();
+}
+
+
+V pool::m_cntall()
+{
+	const S *tag = MakeSymbol("cntall");
+	I cnt = getrec(tag,0,true);
+	ToOutSymbol(3,tag);
+	ToOutBang(2);
+	ToOutBang(1);
+	ToOutInt(0,cnt);
+
+	echodir();
+}
+
+V pool::m_cntrec(I argc,const A *argv)
+{
+	const S *tag = MakeSymbol("cntrec");
+
+	I lvls = -1;
+	if(argc > 0) {
+		if(CanbeInt(argv[0])) {
+			if(argc > 1)
+				post("%s - %s: superfluous arguments ignored",thisName(),GetString(tag));
+			lvls = GetAInt(argv[0]);
+		}
+		else 
+			post("%s - %s: invalid level specification - set to infinite",thisName(),GetString(tag));
+	}
+	
+	I cnt = getrec(tag,lvls,true);
+	ToOutSymbol(3,tag);
+	ToOutBang(2);
+	ToOutBang(1);
+	ToOutInt(0,cnt);
+
+	echodir();
+}
+
+
+V pool::m_cntsub(I argc,const A *argv)
+{
+	const S *tag = MakeSymbol("cntsub");
+
+	I lvls = 0;
+	if(argc > 0) {
+		if(CanbeInt(argv[0])) {
+			if(argc > 1)
+				post("%s - %s: superfluous arguments ignored",thisName(),GetString(tag));
+			lvls = GetAInt(argv[0]);
+		}
+		else 
+			post("%s - %s: invalid level specification - set to 0",thisName(),GetString(tag));
+	}
+
+	I cnt = getsub(tag,lvls,true);
+	ToOutSymbol(3,tag);
+	ToOutBang(2);
+	ToOutBang(1);
+	ToOutInt(0,cnt);
+
+	echodir();
+}
+
+
+V pool::paste(const S *tag,I argc,const A *argv,BL repl)
+{
+	if(clip) {
+		BL mkdir = true;
+		I depth = -1;
+
+		if(argc >= 1) {
+			if(CanbeInt(argv[0])) depth = GetAInt(argv[1]);
+			else
+				post("%s - %s: invalid depth argument - set to -1",thisName(),GetString(tag));
+
+			if(argc >= 2) {
+				if(CanbeBool(argv[1])) mkdir = GetABool(argv[1]);
+				else
+					post("%s - %s: invalid mkdir argument - set to true",thisName(),GetString(tag));
+
+				if(argc > 2) post("%s - %s: superfluous arguments ignored",thisName(),GetString(tag));
+			}
+		}
+		
+		pl->Paste(curdir,clip,depth,repl,mkdir);
+	}
+	else
+		post("%s - %s: clipboard is empty",thisName(),GetString(tag));
+
+	echodir();
+}
+
+
+V pool::m_clrclip()
+{
+	if(clip) { delete clip; clip = NULL; }
+}
+
+
+V pool::copy(const S *tag,I argc,const A *argv,BL cut)
+{
+	if(!argc || !KeyChk(argv[0]))
+		post("%s - %s: invalid key",thisName(),GetString(tag));
+	else {
+		if(argc > 1) 
+			post("%s - %s: superfluous arguments ignored",thisName(),GetString(tag));
+
+		m_clrclip();
+		clip = pl->Copy(curdir,argv[0],cut);
+
+		if(!clip)
+			post("%s - %s: Copying into clipboard failed",thisName(),GetString(tag));
+	}
+
+	echodir();
+}
+
+
+V pool::copyall(const S *tag,BL cut,I depth)
+{
+	m_clrclip();
+	clip = pl->CopyAll(curdir,depth,cut);
+
+	if(!clip)
+		post("%s - %s: Copying into clipboard failed",thisName(),GetString(tag));
+
+	echodir();
+}
+
+
+V pool::copyrec(const S *tag,I argc,const A *argv,BL cut) 
+{
+	I lvls = -1;
+	if(argc > 0) {
+		if(CanbeInt(argv[0])) {
+			if(argc > 1)
+				post("%s - %s: superfluous arguments ignored",thisName(),GetString(tag));
+			lvls = GetAInt(argv[0]);
+		}
+		else 
+			post("%s - %s: invalid level specification - set to infinite",thisName(),GetString(tag));
+	}
+
+	copyall(tag,cut,lvls);
+}
+
+V pool::m_load(I argc,const A *argv)
+{
+	const C *flnm = NULL;
+	if(argc > 0) {
+		if(argc > 1) post("%s - load: superfluous arguments ignored",thisName());
+		if(IsString(argv[0])) flnm = GetString(argv[0]);
+	}
+
+	if(!flnm) 
+		post("%s - load: no filename given",thisName());
+	else if(!pl->Load(flnm))
+		post("%s - load: error loading data",thisName());
+
+	echodir();
+}
+
+V pool::m_save(I argc,const A *argv)
+{
+	const C *flnm = NULL;
+	if(argc > 0) {
+		if(argc > 1) post("%s - save: superfluous arguments ignored",thisName());
+		if(IsString(argv[0])) flnm = GetString(argv[0]);
+	}
+
+	if(!flnm) 
+		post("%s - save: no filename given",thisName());
+	else if(!pl->Save(flnm))
+		post("%s - save: error saving data",thisName());
+
+	echodir();
+}
+
+V pool::m_lddir(I argc,const A *argv)
+{
+	const C *flnm = NULL;
+	if(argc > 0) {
+		if(argc > 1) post("%s - lddir: superfluous arguments ignored",thisName());
+		if(IsString(argv[0])) flnm = GetString(argv[0]);
+	}
+
+	if(!flnm)
+		post("%s - lddir: invalid filename",thisName());
+	else {
+		if(!pl->LdDir(curdir,flnm,0)) 
+		post("%s - lddir: directory couldn't be loaded",thisName());
+	}
+
+	echodir();
+}
+
+V pool::m_ldrec(I argc,const A *argv)
+{
+	const C *flnm = NULL;
+	I depth = -1;
+	BL mkdir = true;
+	if(argc >= 1) {
+		if(IsString(argv[0])) flnm = GetString(argv[0]);
+
+		if(argc >= 2) {
+			if(CanbeInt(argv[1])) depth = GetAInt(argv[1]);
+			else
+				post("%s - ldrec: invalid depth argument - set to -1",thisName());
+
+			if(argc >= 3) {
+				if(CanbeBool(argv[2])) mkdir = GetABool(argv[2]);
+				else
+					post("%s - ldrec: invalid mkdir argument - set to true",thisName());
+
+				if(argc > 3) post("%s - ldrec: superfluous arguments ignored",thisName());
+			}
+		}
+	}
+
+	if(!flnm)
+		post("%s - ldrec: invalid filename",thisName());
+	else {
+		if(!pl->LdDir(curdir,flnm,depth,mkdir)) 
+		post("%s - ldrec: directory couldn't be saved",thisName());
+	}
+
+	echodir();
+}
+
+V pool::m_svdir(I argc,const A *argv)
+{
+	const C *flnm = NULL;
+	if(argc > 0) {
+		if(argc > 1) post("%s - svdir: superfluous arguments ignored",thisName());
+		if(IsString(argv[0])) flnm = GetString(argv[0]);
+	}
+
+	if(!flnm)
+		post("%s - svdir: invalid filename",thisName());
+	else {
+		if(!pl->SvDir(curdir,flnm,0,absdir)) 
+		post("%s - svdir: directory couldn't be saved",thisName());
+	}
+
+	echodir();
+}
+
+V pool::m_svrec(I argc,const A *argv)
+{
+	const C *flnm = NULL;
+	if(argc > 0) {
+		if(argc > 1) post("%s - svrec: superfluous arguments ignored",thisName());
+		if(IsString(argv[0])) flnm = GetString(argv[0]);
+	}
+
+	if(!flnm)
+		post("%s - svrec: invalid filename",thisName());
+	else {
+		if(!pl->SvDir(curdir,flnm,-1,absdir)) 
+		post("%s - svrec: directory couldn't be saved",thisName());
+	}
+
+	echodir();
+}
+
+
+
+BL pool::KeyChk(const t_atom &a)
+{
+	return IsSymbol(a) || IsFloat(a) || IsInt(a);
+}
+
+BL pool::ValChk(I argc,const t_atom *argv)
+{
+	for(I i = 0; i < argc; ++i) {
+		const t_atom &a = argv[i];
+		if(!IsSymbol(a) && !IsFloat(a) && !IsInt(a)) return false;
+	}
+	return true;
+}
+
+V pool::ToOutAtom(I ix,const t_atom &a)
+{
+	if(IsSymbol(a))
+		ToOutSymbol(ix,GetSymbol(a));
+	else if(IsFloat(a))
+		ToOutFloat(ix,GetFloat(a));
+	else if(IsInt(a))
+		ToOutInt(ix,GetInt(a));
+	else
+		post("%s - output atom: type not supported!",thisName());
+}
+
+
+
+pooldata *pool::GetPool(const S *s)
+{
+	pooldata *pi = head;
+	for(; pi && pi->sym != s; pi = pi->nxt) (V)0;
+
+	if(pi) {
+		pi->Push();
+		return pi;
+	}
+	else {
+		pooldata *p = new pooldata(s);
+		p->Push();
+
+		// now add to chain
+		if(head) head->nxt = p;
+		else head = p;
+		tail = p;
+		return p;
+	}
+}
+
+V pool::RmvPool(pooldata *p)
+{
+	pooldata *prv = NULL,*pi = head;
+	for(; pi && pi != p; prv = pi,pi = pi->nxt) (V)0;
+
+	if(pi && !pi->Pop()) {
+		if(prv) prv->nxt = pi->nxt;
+		else head = pi->nxt;
+		if(!pi->nxt) tail = pi;
+
+		delete pi;
+	}
+}
+
diff --git a/externals/grill/pool/pool.cpp b/externals/grill/pool/pool.cpp
new file mode 100644
index 00000000..629ab7fb
--- /dev/null
+++ b/externals/grill/pool/pool.cpp
@@ -0,0 +1,645 @@
+/* 
+
+pool - hierarchical storage object for PD and Max/MSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.  
+
+*/
+
+#include "pool.h"
+
+#include <string.h>
+#include <fstream.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+
+inline I compare(I a,I b) { return a == b?0:(a < b?-1:1); }
+inline I compare(F a,F b) { return a == b?0:(a < b?-1:1); }
+
+static I compare(const S *a,const S *b) 
+{
+	if(a == b)
+		return 0;
+	else
+		return strcmp(flext::GetString(a),flext::GetString(b));
+}
+
+static I compare(const A &a,const A &b) 
+{
+	if(a.a_type == b.a_type) {
+		switch(a.a_type) {
+		case A_FLOAT:
+			return compare(a.a_w.w_float,b.a_w.w_float);
+#ifdef MAXMSP
+		case A_LONG:
+			return compare((I)a.a_w.w_long,(I)b.a_w.w_long);
+#endif
+		case A_SYMBOL:
+			return compare(a.a_w.w_symbol,b.a_w.w_symbol);
+#ifdef PD
+		case A_POINTER:
+			return a.a_w.w_gpointer == b.a_w.w_gpointer?0:(a.a_w.w_gpointer < b.a_w.w_gpointer?-1:1);
+#endif
+		default:
+			LOG("pool - atom comparison: type not handled");
+			return -1;
+		}
+	}
+	else
+		return a.a_type < b.a_type?-1:1;
+}
+
+
+poolval::poolval(const A &k,AtomList *d):
+	data(d),nxt(NULL)
+{
+	SetAtom(key,k);
+}
+
+poolval::~poolval()
+{
+	if(data) delete data;
+	if(nxt) delete nxt;
+}
+
+poolval &poolval::Set(AtomList *d)
+{
+	if(data) delete data;
+	data = d;
+	return *this;
+}
+
+poolval *poolval::Dup() const
+{
+	return new poolval(key,data?new AtomList(*data):NULL); 
+}
+
+
+pooldir::pooldir(const A &d):
+	dirs(NULL),vals(NULL),nxt(NULL)
+{
+	CopyAtom(&dir,&d);
+}
+
+pooldir::~pooldir()
+{
+	Clear(true);
+	if(nxt) delete nxt;
+}
+
+V pooldir::Clear(BL rec,BL dironly)
+{
+	if(rec && dirs) { delete dirs; dirs = NULL; }
+	if(!dironly && vals) { delete vals; vals = NULL; }
+}
+
+pooldir *pooldir::AddDir(I argc,const A *argv)
+{
+	if(!argc) return this;
+
+	I c = 1;
+	pooldir *prv = NULL,*ix = dirs;
+	for(; ix; prv = ix,ix = ix->nxt) {
+		c = compare(argv[0],ix->dir);
+		if(c <= 0) break;
+	}
+
+	if(c || !ix) {
+		pooldir *nd = new pooldir(argv[0]);
+		nd->nxt = ix;
+
+		if(prv) prv->nxt = nd;
+		else dirs = nd;
+		ix = nd;
+	}
+
+	return ix->AddDir(argc-1,argv+1);
+}
+
+pooldir *pooldir::GetDir(I argc,const A *argv,BL rmv)
+{
+	if(!argc) return this;
+
+	I c = 1;
+	pooldir *prv = NULL,*ix = dirs;
+	for(; ix; prv = ix,ix = ix->nxt) {
+		c = compare(argv[0],ix->dir);
+		if(c <= 0) break;
+	}
+
+	if(c || !ix) 
+		return NULL;
+	else {
+		if(argc > 1)
+			return ix->GetDir(argc-1,argv+1,rmv);
+		else if(rmv) {
+			pooldir *nd = ix->nxt;
+			if(prv) prv->nxt = nd;
+			else dirs = nd;
+			ix->nxt = NULL;
+			return ix;
+		}
+		else 
+			return ix;
+	}
+}
+
+BL pooldir::DelDir(const AtomList &d)
+{
+	pooldir *pd = GetDir(d,true);
+	if(pd && pd != this) {
+		delete pd;
+		return true;
+	}
+	else 
+		return false;
+}
+
+V pooldir::SetVal(const A &key,AtomList *data,BL over)
+{
+	I c = 1;
+	poolval *prv = NULL,*ix = vals;
+	for(; ix; prv = ix,ix = ix->nxt) {
+		c = compare(key,ix->key);
+		if(c <= 0) break;
+	}
+
+	if(c || !ix) {
+		// no existing data found
+	
+		if(data) {
+			poolval *nv = new poolval(key,data);
+			nv->nxt = ix;
+
+			if(prv) prv->nxt = nv;
+			else vals = nv;
+		}
+	}
+	else if(over) { 
+		// data exists... only set if overwriting enabled
+		
+		if(data)
+			ix->Set(data);
+		else {
+			poolval *nv = ix->nxt;
+			if(prv) prv->nxt = nv;
+			else vals = nv;
+			ix->nxt = NULL;
+			delete ix;
+		}
+	}
+}
+
+flext::AtomList *pooldir::GetVal(const A &key,BL cut)
+{
+	I c = 1;
+	poolval *prv = NULL,*ix = vals;
+	for(; ix; prv = ix,ix = ix->nxt) {
+		c = compare(key,ix->key);
+		if(c <= 0) break;
+	}
+
+	if(c || !ix) 
+		return NULL;
+	else {
+		AtomList *ret;
+		if(cut) {
+			poolval *nv = ix->nxt;
+			if(prv) prv->nxt = nv;
+			else vals = nv;
+			ix->nxt = NULL;
+			ret = ix->data; ix->data = NULL;
+			delete ix;
+		}
+		else
+			ret = new AtomList(*ix->data);
+		return ret;
+	}
+}
+
+I pooldir::CntAll()
+{
+	I cnt = 0;
+	poolval *ix = vals;
+	for(; ix; ix = ix->nxt,++cnt) {}
+	return cnt;
+}
+
+I pooldir::GetAll(A *&keys,AtomList *&lst,BL cut)
+{
+	I cnt = CntAll();
+	keys = new A[cnt];
+	lst = new AtomList[cnt];
+
+	poolval *ix = vals;
+	for(I i = 0; ix; ++i) {
+		SetAtom(keys[i],ix->key);
+		lst[i] = *ix->data;
+
+		if(cut) {
+			poolval *t = ix;
+			vals = ix = ix->nxt;
+			t->nxt = NULL; delete t;
+		}
+		else
+			ix = ix->nxt;
+	}
+
+	return cnt;
+}
+
+I pooldir::GetSub(const A **&lst)
+{
+	I cnt = 0;
+	pooldir *ix = dirs;
+	for(; ix; ix = ix->nxt,++cnt) {}
+	lst = new const A *[cnt];
+
+	ix = dirs;
+	for(I i = 0; ix; ix = ix->nxt,++i) {
+		lst[i] = &ix->dir;
+	}
+
+	return cnt;
+}
+
+
+BL pooldir::Paste(const pooldir *p,I depth,BL repl,BL mkdir)
+{
+	BL ok = true;
+
+	for(poolval *ix = p->vals; ix; ix = ix->nxt) {
+		SetVal(ix->key,new AtomList(*ix->data),repl);
+	}
+
+	if(ok && depth) {
+		for(pooldir *dix = p->dirs; ok && dix; dix = dix->nxt) {
+			pooldir *ndir = mkdir?AddDir(1,&dix->dir):GetDir(1,&dix->dir);
+			if(ndir) { 
+				ok = ndir->Paste(dix,depth > 0?depth-1:depth,repl,mkdir);
+			}
+		}
+	}
+
+	return ok;
+}
+
+BL pooldir::Copy(pooldir *p,I depth,BL cut)
+{
+	BL ok = true;
+
+	if(cut) {
+		if(p->vals) 
+			ok = false;
+		else
+			p->vals = vals, vals = NULL;
+	}
+	else {
+		// inefficient!! p->SetVal has to search through list unnecessarily!!
+		for(poolval *ix = vals; ix; ix = ix->nxt) {
+			p->SetVal(ix->key,new AtomList(*ix->data));
+		}
+	}
+
+	if(ok && depth) {
+		// also quite inefficient for cut 
+		for(pooldir *dix = dirs; ok && dix; dix = dix->nxt) {
+			pooldir *ndir = p->AddDir(1,&dix->dir);
+			if(ndir)
+				ok = ndir->Copy(dix,depth > 0?depth-1:depth,cut);
+			else
+				ok = false;
+		}
+	}
+
+	return ok;
+}
+
+
+static C *ReadAtom(C *c,A *a)
+{
+	// skip whitespace
+	while(*c && isspace(*c)) ++c;
+	if(!*c) return NULL;
+
+	const C *m = c; // remember position
+
+	// check for word type (s = 0,1,2 ... int,float,symbol)
+	I s = 0;
+	for(; *c && !isspace(*c); ++c) {
+		if(!isdigit(*c)) 
+			s = (*c != '.' || s == 1)?2:1;
+	}
+
+	if(a) {
+		switch(s) {
+		case 0: // integer
+#ifdef MAXMSP
+			a->a_type = A_LONG;
+			a->a_w.w_long = atol(m);
+			break;
+#endif
+		case 1: // float
+			a->a_type = A_FLOAT;
+			a->a_w.w_float = (F)atof(m);
+			break;
+		default: { // anything else is a symbol
+			C t = *c; *c = 0;
+			a->a_type = A_SYMBOL;
+			a->a_w.w_symbol = (S *)flext::MakeSymbol(m);
+			*c = t;
+			break;
+		}
+		}
+	}
+
+	return c;
+}
+
+static BL ReadAtoms(istream &is,flext::AtomList &l,C del)
+{
+	C tmp[1024];
+	is.getline(tmp,sizeof tmp,del); 
+	if(is.eof() || !is.good()) return false;
+
+	I i,cnt;
+	C *t = tmp;
+	for(cnt = 0; ; ++cnt) {
+		t = ReadAtom(t,NULL);
+		if(!t) break;
+	}
+
+	l(cnt);
+	if(cnt) {
+		for(i = 0,t = tmp; i < cnt; ++i)
+			t = ReadAtom(t,&l[i]);
+	}
+	return true;
+}
+
+static V WriteAtom(ostream &os,const A &a)
+{
+	switch(a.a_type) {
+	case A_FLOAT:
+		os << a.a_w.w_float;
+		break;
+#ifdef MAXMSP
+	case A_LONG:
+		os << a.a_w.w_long;
+		break;
+#endif
+	case A_SYMBOL:
+		os << flext::GetString(a.a_w.w_symbol);
+		break;
+	}
+}
+
+static V WriteAtoms(ostream &os,const flext::AtomList &l)
+{
+	for(I i = 0; i < l.Count(); ++i) {
+		WriteAtom(os,l[i]);
+		os << ' ';
+	}
+}
+
+BL pooldir::LdDir(istream &is,I depth,BL mkdir)
+{
+	BL r;
+	for(I i = 1; !is.eof(); ++i) {
+		AtomList d,k,*v = new AtomList;
+		r = ReadAtoms(is,d,',');
+		r = r && ReadAtoms(is,k,',') && k.Count() == 1;
+		r = r && ReadAtoms(is,*v,'\n') && v->Count();
+
+		if(r) {
+			if(depth < 0 || d.Count() <= depth) {
+				pooldir *nd = mkdir?AddDir(d):GetDir(d);
+				if(nd) {
+					nd->SetVal(k[0],v); v = NULL;
+				}
+	#ifdef _DEBUG
+				else
+					post("pool - directory was not found",i);
+	#endif
+			}
+		}
+		else if(!is.eof())
+			post("pool - format mismatch encountered, skipped line %i",i);
+
+		if(v) delete v;
+	}
+	return true;
+}
+
+BL pooldir::SvDir(ostream &os,I depth,const AtomList &dir)
+{
+	{
+		for(poolval *ix = vals; ix; ix = ix->nxt) {
+			WriteAtoms(os,dir);
+			os << ", ";
+			WriteAtom(os,ix->key);
+			os << " , ";
+			WriteAtoms(os,*ix->data);
+			os << endl;
+		}
+	}
+	if(depth) {
+		I nd = depth > 0?depth-1:-1;
+		for(pooldir *ix = dirs; ix; ix = ix->nxt) {
+			ix->SvDir(os,nd,AtomList(dir).Append(ix->dir));
+		}
+	}
+	return true;
+}
+
+
+
+
+pooldata::pooldata(const S *s):
+	sym(s),nxt(NULL),refs(0),
+	root(nullatom)
+{
+	LOG1("new pool %s",sym?flext_base::GetString(sym):"<private>");
+}
+
+pooldata::~pooldata()
+{
+	LOG1("free pool %s",sym?flext_base::GetString(sym):"<private>");
+}
+
+t_atom pooldata::nullatom = { A_NULL };
+
+
+V pooldata::Reset()
+{
+	root.Clear(true);
+}
+
+BL pooldata::MkDir(const AtomList &d)
+{
+	root.AddDir(d);
+	return true;
+}
+
+BL pooldata::ChkDir(const AtomList &d)
+{
+	return root.GetDir(d) != NULL;
+}
+
+BL pooldata::RmDir(const AtomList &d)
+{
+	return root.DelDir(d);
+}
+
+BL pooldata::Set(const AtomList &d,const A &key,AtomList *data,BL over)
+{
+	pooldir *pd = root.GetDir(d);
+	if(!pd) return false;
+	pd->SetVal(key,data,over);
+	return true;
+}
+
+BL pooldata::Clr(const AtomList &d,const A &key)
+{
+	pooldir *pd = root.GetDir(d);
+	if(!pd) return false;
+	pd->ClrVal(key);
+	return true;
+}
+
+BL pooldata::ClrAll(const AtomList &d,BL rec,BL dironly)
+{
+	pooldir *pd = root.GetDir(d);
+	if(!pd) return false;
+	pd->Clear(rec,dironly);
+	return true;
+}
+
+flext::AtomList *pooldata::Get(const AtomList &d,const A &key)
+{
+	pooldir *pd = root.GetDir(d);
+	return pd?pd->GetVal(key):NULL;
+}
+
+I pooldata::CntAll(const AtomList &d)
+{
+	pooldir *pd = root.GetDir(d);
+	return pd?pd->CntAll():0;
+}
+
+I pooldata::GetAll(const AtomList &d,A *&keys,AtomList *&lst)
+{
+	pooldir *pd = root.GetDir(d);
+	if(pd)
+		return pd->GetAll(keys,lst);
+	else {
+		keys = NULL; lst = NULL;
+		return 0;
+	}
+}
+
+I pooldata::GetSub(const AtomList &d,const t_atom **&dirs)
+{
+	pooldir *pd = root.GetDir(d);
+	if(pd)
+		return pd->GetSub(dirs);
+	else {
+		dirs = NULL;
+		return 0;
+	}
+}
+
+
+BL pooldata::Paste(const AtomList &d,const pooldir *clip,I depth,BL repl,BL mkdir)
+{
+	pooldir *pd = root.GetDir(d);
+	if(pd)
+		return pd->Paste(clip,depth,repl,mkdir);
+	else
+		return false;
+}
+
+pooldir *pooldata::Copy(const AtomList &d,const A &key,BL cut)
+{
+	pooldir *pd = root.GetDir(d);
+	if(pd) {
+		AtomList *val = pd->GetVal(key,cut);
+		if(val) {
+			pooldir *ret = new pooldir(nullatom);
+			ret->SetVal(key,val);
+			return ret;
+		}
+		else
+			return NULL;
+	}
+	else
+		return NULL;
+}
+
+pooldir *pooldata::CopyAll(const AtomList &d,I depth,BL cut)
+{
+	pooldir *pd = root.GetDir(d);
+	if(pd) {
+		pooldir *ret = new pooldir(nullatom);
+		if(pd->Copy(ret,depth,cut))
+			return ret;
+		else {
+			delete ret;
+			return NULL;
+		}
+	}
+	else
+		return NULL;
+}
+
+
+static const C *CnvFlnm(C *dst,const C *src,I sz)
+{
+#if defined(PD) && defined(NT)
+	I cnt = strlen(src);
+	if(cnt >= sz-1) return NULL;
+	for(I i = 0; i < cnt; ++i)
+		dst[i] = src[i] != '/'?src[i]:'\\';
+	dst[i] = 0;
+	return dst;
+#else
+	return src;
+#endif
+}
+
+BL pooldata::LdDir(const AtomList &d,const C *flnm,I depth,BL mkdir)
+{
+	pooldir *pd = root.GetDir(d);
+	if(pd) {
+		C tmp[1024];
+		const C *t = CnvFlnm(tmp,flnm,sizeof tmp);
+		if(t) {
+			ifstream fl(t);
+			return fl.good() && pd->LdDir(fl,depth,mkdir);
+		}
+		else return false;
+	}
+	else
+		return false;
+}
+
+BL pooldata::SvDir(const AtomList &d,const C *flnm,I depth,BL absdir)
+{
+	pooldir *pd = root.GetDir(d);
+	if(pd) {
+		C tmp[1024];
+		const C *t = CnvFlnm(tmp,flnm,sizeof tmp);
+		if(t) {
+			ofstream fl(t);
+			return fl.good() && pd->SvDir(fl,depth,absdir?d:AtomList());
+		}
+		else return false;
+	}
+	else
+		return false;
+}
+
+
diff --git a/externals/grill/pool/pool.cw b/externals/grill/pool/pool.cw
new file mode 100755
index 00000000..66e93306
Binary files /dev/null and b/externals/grill/pool/pool.cw differ
diff --git a/externals/grill/pool/pool.dsp b/externals/grill/pool/pool.dsp
new file mode 100644
index 00000000..dbe09386
--- /dev/null
+++ b/externals/grill/pool/pool.dsp
@@ -0,0 +1,103 @@
+# Microsoft Developer Studio Project File - Name="pool" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** NICHT BEARBEITEN **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=pool - Win32 Debug
+!MESSAGE Dies ist kein g�ltiges Makefile. Zum Erstellen dieses Projekts mit NMAKE
+!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und f�hren Sie den Befehl
+!MESSAGE 
+!MESSAGE NMAKE /f "pool.mak".
+!MESSAGE 
+!MESSAGE Sie k�nnen beim Ausf�hren von NMAKE eine Konfiguration angeben
+!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:
+!MESSAGE 
+!MESSAGE NMAKE /f "pool.mak" CFG="pool - Win32 Debug"
+!MESSAGE 
+!MESSAGE F�r die Konfiguration stehen zur Auswahl:
+!MESSAGE 
+!MESSAGE "pool - Win32 Release" (basierend auf  "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "pool - Win32 Debug" (basierend auf  "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName "max/pool"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pool - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "pd-msvc/r"
+# PROP Intermediate_Dir "pd-msvc/r"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "POOL_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "c:\programme\audio\pd\src" /I "f:\prog\max\flext\source" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NT" /D "PD" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc07 /d "NDEBUG"
+# ADD RSC /l 0xc07 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib pd.lib flext-pdwin.lib /nologo /dll /machine:I386 /libpath:"c:\programme\audio\pd\bin" /libpath:"f:\prog\max\flext\pd-msvc"
+
+!ELSEIF  "$(CFG)" == "pool - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "pd-msvc/d"
+# PROP Intermediate_Dir "pd-msvc/d"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "POOL_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "c:\programme\audio\pd\src" /I "f:\prog\max\flext\source" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NT" /D "PD" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc07 /d "_DEBUG"
+# ADD RSC /l 0xc07 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib pd.lib flext_d-pdwin.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"c:\programme\audio\pd\bin" /libpath:"f:\prog\max\flext\pd-msvc"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pool - Win32 Release"
+# Name "pool - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\main.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\pool.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\pool.h
+# End Source File
+# End Target
+# End Project
diff --git a/externals/grill/pool/pool.h b/externals/grill/pool/pool.h
new file mode 100644
index 00000000..21fe4eb3
--- /dev/null
+++ b/externals/grill/pool/pool.h
@@ -0,0 +1,124 @@
+/* 
+
+pool - hierarchical storage object for PD and Max/MSP
+
+Copyright (c) 2002 Thomas Grill (xovo@gmx.net)
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see the file, "license.txt," in this distribution.  
+
+*/
+
+#ifndef __POOL_H
+#define __POOL_H
+
+#include <flext.h>
+
+#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400)
+#error You need at least flext version 0.4.0
+#endif
+
+#define POOL_VERSION "0.0.5"
+
+#include <iostream.h>
+
+typedef void V;
+typedef int I;
+typedef float F;
+typedef char C;
+typedef bool BL;
+typedef t_atom A;
+typedef t_symbol S;
+
+class poolval:
+	public flext
+{
+public:
+	poolval(const A &key,AtomList *data);
+	~poolval();
+
+	poolval &Set(AtomList *data);
+	poolval *Dup() const;
+
+	A key;
+	AtomList *data;
+	poolval *nxt;
+};
+
+	class pooldir:
+	public flext
+{
+public:
+	pooldir(const A &dir);
+	~pooldir();
+
+	V Clear(BL rec,BL dironly = false);
+
+	pooldir *GetDir(I argc,const A *argv,BL cut = false);
+	pooldir *GetDir(const AtomList &d,BL cut = false) { return GetDir(d.Count(),d.Atoms(),cut); }
+	BL DelDir(const AtomList &d);
+	pooldir *AddDir(I argc,const A *argv);
+	pooldir *AddDir(const AtomList &d) { return AddDir(d.Count(),d.Atoms()); }
+
+	V SetVal(const A &key,AtomList *data,BL over = true);
+	V ClrVal(const A &key) { SetVal(key,NULL); }
+	AtomList *GetVal(const A &key,BL cut = false);
+	I CntAll();
+	I GetAll(A *&keys,AtomList *&lst,BL cut = false);
+	I GetSub(const t_atom **&dirs);
+
+	BL Paste(const pooldir *p,I depth,BL repl,BL mkdir);
+	BL Copy(pooldir *p,I depth,BL cur);
+
+	BL LdDir(istream &is,I depth,BL mkdir);
+	BL SvDir(ostream &os,I depth,const AtomList &dir = AtomList());
+
+	A dir;
+	pooldir *nxt;
+
+	pooldir *dirs;
+	poolval *vals;
+};
+
+	class pooldata:
+	public flext
+{
+public:
+	pooldata(const S *s = NULL);
+	~pooldata();
+
+	V Push() { ++refs; }
+	BL Pop() { return --refs > 0; }
+
+	V Reset();
+	BL MkDir(const AtomList &d); 
+	BL ChkDir(const AtomList &d);
+	BL RmDir(const AtomList &d);
+
+	BL Set(const AtomList &d,const A &key,AtomList *data,BL over = true);
+	BL Clr(const AtomList &d,const A &key);
+	BL ClrAll(const AtomList &d,BL rec,BL dironly = false);
+	AtomList *Get(const AtomList &d,const A &key);
+	I CntAll(const AtomList &d);
+	I GetAll(const AtomList &d,A *&keys,AtomList *&lst);
+	I GetSub(const AtomList &d,const t_atom **&dirs);
+
+	BL Paste(const AtomList &d,const pooldir *clip,I depth = -1,BL repl = true,BL mkdir = true);
+	pooldir *Copy(const AtomList &d,const A &key,BL cut);
+	pooldir *CopyAll(const AtomList &d,I depth,BL cut);
+
+	BL LdDir(const AtomList &d,const C *flnm,I depth,BL mkdir = true);
+	BL SvDir(const AtomList &d,const C *flnm,I depth,BL absdir);
+	BL Load(const C *flnm) { return LdDir(AtomList(),flnm,-1); }
+	BL Save(const C *flnm) { return SvDir(AtomList(),flnm,-1,true); }
+
+	I refs;
+	const S *sym;
+	pooldata *nxt;
+
+	pooldir root;
+
+private:
+	static t_atom nullatom;
+};
+
+#endif
diff --git a/externals/grill/pool/pool.help b/externals/grill/pool/pool.help
new file mode 100755
index 00000000..24d189b7
Binary files /dev/null and b/externals/grill/pool/pool.help differ
diff --git a/externals/grill/pool/pool.pd b/externals/grill/pool/pool.pd
new file mode 100644
index 00000000..ffd49615
--- /dev/null
+++ b/externals/grill/pool/pool.pd
@@ -0,0 +1,201 @@
+#N canvas 25 23 966 669 12;
+#X obj 273 441 pool;
+#X msg 236 52 set 1 2 3;
+#X obj 272 563 print K;
+#X msg 602 156 getall;
+#X msg 236 81 set A k g;
+#X obj 251 594 print V;
+#X obj 290 532 print D;
+#X msg 236 111 set A l m;
+#X msg 239 140 set 2 34;
+#X msg 238 171 set 3 17;
+#X msg 423 50 clr A;
+#X msg 427 126 get A;
+#X msg 427 158 get 2;
+#X msg 20 79 echodir \$1;
+#X obj 20 58 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X obj 20 160 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X msg 20 181 absdir \$1;
+#X text 422 30 clear value;
+#X text 423 104 get value;
+#X obj 309 505 print C;
+#X text 375 504 command;
+#X text 357 531 directory (abs or rel to current);
+#X text 320 594 data value;
+#X text 338 562 data key;
+#X msg 26 279 pool pool1;
+#X msg 27 308 pool;
+#X text 69 308 set to private;
+#X msg 27 370 reset;
+#X text 26 349 clear all pool data;
+#X msg 608 56 clrall;
+#X text 599 34 clear all values in dir;
+#X text 602 135 get all values in dir;
+#X text 314 442 pool name can be given as argument;
+#X text 598 81 clear all values and dirs;
+#X msg 606 103 clrrec;
+#X msg 600 203 getrec;
+#X text 600 181 get all values in dir and subdirs;
+#X text 652 232 bang at EOL;
+#X text 661 150 bang at EOL;
+#X text 655 202 depth may be given;
+#X text 22 258 data is shared among pool objects;
+#X text 23 237 set pool name;
+#X text 20 36 at each command;
+#X text 20 20 echo current dir;
+#X text 233 27 set values;
+#X text 15 141 (default on);
+#X text 7 125 report absolute dirs;
+#X msg 28 417 help;
+#X text 70 418 get some info;
+#X text 658 219 default=-1 (= infinite);
+#X msg 604 283 cntall;
+#X text 663 281 count all values in dir;
+#X msg 604 309 cntrec;
+#X text 665 307 ... and subdirs;
+#X text 661 321 (depth may be given);
+#X msg 237 226 add 3 14;
+#X text 235 205 set but don't replace;
+#N canvas 0 0 421 528 dirs 0;
+#X msg 109 27 mkdir fld1;
+#X msg 111 122 chdir;
+#X msg 110 217 updir;
+#X msg 111 354 getsub -1;
+#X text 110 7 make absolute dir;
+#X text 109 51 make relative dir;
+#X msg 110 72 mksub fld2;
+#X text 109 104 change to absolute dir;
+#X msg 110 165 chsub fld2;
+#X text 108 146 change to relative dir;
+#X text 106 198 change to upper dir;
+#X text 107 250 remove absolute dir;
+#X msg 108 269 rmdir fld1;
+#X msg 110 308 rmsub fld2;
+#X text 109 289 remove relative dir;
+#X text 109 336 get subdirs;
+#X text 239 379 -1 ... infinite;
+#X text 161 213 depth may be given;
+#X text 162 229 default=1;
+#X text 192 348 depth may be given;
+#X text 192 365 default=1;
+#X text 108 377 count subdirs;
+#X msg 111 398 cntsub -1;
+#X obj 11 239 outlet;
+#X msg 111 458 getdir;
+#X text 110 438 get current dir;
+#X text 172 457 always absolute;
+#X connect 0 0 23 0;
+#X connect 1 0 23 0;
+#X connect 2 0 23 0;
+#X connect 3 0 23 0;
+#X connect 6 0 23 0;
+#X connect 8 0 23 0;
+#X connect 12 0 23 0;
+#X connect 13 0 23 0;
+#X connect 22 0 23 0;
+#X connect 24 0 23 0;
+#X restore 713 494 pd dirs;
+#X text 710 468 directory operations;
+#N canvas 0 0 467 434 file 0;
+#X text 117 207 save dir and subdirs;
+#X text 117 165 save data in current dir;
+#X msg 117 184 svdir c:/temp/pool.dat;
+#X msg 117 226 svrec c:/temp/pool.dat;
+#X msg 116 272 lddir c:/temp/pool.dat;
+#X msg 116 319 ldrec c:/temp/pool.dat;
+#X text 117 253 load data into current dir;
+#X text 115 300 load data into current dir and below;
+#X text 132 340 depth (default -1) and;
+#X text 134 356 mkdir flag (default 1) can be given;
+#X obj 22 188 outlet;
+#X text 117 37 save all;
+#X text 117 81 load all (add to existing data);
+#X msg 118 100 load c:/temp/pool.dat;
+#X msg 120 54 save c:/temp/pool.dat;
+#X text 22 12 file operations;
+#X connect 2 0 10 0;
+#X connect 3 0 10 0;
+#X connect 4 0 10 0;
+#X connect 5 0 10 0;
+#X connect 13 0 10 0;
+#X connect 14 0 10 0;
+#X restore 714 549 pd file;
+#X text 712 526 file operations;
+#X text 712 583 clipboard operations;
+#N canvas 0 0 529 577 clip 0;
+#X obj 17 183 outlet;
+#X text 97 56 copy value associated to key into clipboard;
+#X msg 100 77 copy A;
+#X msg 98 119 cut B;
+#X text 96 101 cut value associated to key into clipboard;
+#X msg 96 401 paste;
+#X msg 98 179 copyall;
+#X text 95 158 copy all values in current dir into clipboard;
+#X msg 97 221 cutall;
+#X text 95 201 cut all values in current dir into clipboard;
+#X text 94 263 copy all values in current dir into clipboard;
+#X text 94 306 cut all values in current dir into clipboard;
+#X msg 97 284 copyrec;
+#X text 194 285 depth may be given (default=-1);
+#X text 193 326 depth may be given (default=-1);
+#X msg 96 326 cutrec 1;
+#X text 194 345 1..only with first level subdirs;
+#X text 96 379 paste clipboard contents into current directory;
+#X text 167 397 depth (default -1) and;
+#X text 169 413 mkdir flag (default 1) can be given;
+#X text 171 448 depth (default -1) and;
+#X text 173 466 mkdir flag (default 1) can be given;
+#X msg 95 453 pasteadd;
+#X text 95 431 paste but don't replace;
+#X msg 94 521 clrclip;
+#X text 160 523 clear clipboard (free memory);
+#X text 22 12 clipboard operations (this is an internal clipboard...)
+;
+#X connect 2 0 0 0;
+#X connect 3 0 0 0;
+#X connect 5 0 0 0;
+#X connect 6 0 0 0;
+#X connect 8 0 0 0;
+#X connect 12 0 0 0;
+#X connect 15 0 0 0;
+#X connect 22 0 0 0;
+#X connect 24 0 0 0;
+#X restore 714 606 pd clip;
+#X text 712 437 more commands:;
+#X obj 780 494 s \$0-pool;
+#X obj 778 549 s \$0-pool;
+#X obj 778 606 s \$0-pool;
+#X obj 349 392 r \$0-pool;
+#X connect 0 0 5 0;
+#X connect 0 1 2 0;
+#X connect 0 2 6 0;
+#X connect 0 3 19 0;
+#X connect 1 0 0 0;
+#X connect 3 0 0 0;
+#X connect 4 0 0 0;
+#X connect 7 0 0 0;
+#X connect 8 0 0 0;
+#X connect 9 0 0 0;
+#X connect 10 0 0 0;
+#X connect 11 0 0 0;
+#X connect 12 0 0 0;
+#X connect 13 0 0 0;
+#X connect 14 0 13 0;
+#X connect 15 0 16 0;
+#X connect 16 0 0 0;
+#X connect 24 0 0 0;
+#X connect 25 0 0 0;
+#X connect 27 0 0 0;
+#X connect 29 0 0 0;
+#X connect 34 0 0 0;
+#X connect 35 0 0 0;
+#X connect 47 0 0 0;
+#X connect 50 0 0 0;
+#X connect 52 0 0 0;
+#X connect 55 0 0 0;
+#X connect 57 0 64 0;
+#X connect 59 0 65 0;
+#X connect 62 0 66 0;
+#X connect 67 0 0 0;
-- 
cgit v1.2.1