|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r59298 - in branches/quickbook-1.5-spirit2: . detail
From: daniel_james_at_[hidden]
Date: 2010-01-27 17:01:35
Author: danieljames
Date: 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
New Revision: 59298
URL: http://svn.boost.org/trac/boost/changeset/59298
Log:
Call template.
Added:
branches/quickbook-1.5-spirit2/phrase_template.cpp (contents, props changed)
Text files modified:
branches/quickbook-1.5-spirit2/Jamfile.v2 | 1
branches/quickbook-1.5-spirit2/detail/actions.cpp | 283 ----------------------------------------
branches/quickbook-1.5-spirit2/detail/actions.hpp | 15 --
branches/quickbook-1.5-spirit2/detail/actions_class.cpp | 1
branches/quickbook-1.5-spirit2/detail/actions_class.hpp | 1
branches/quickbook-1.5-spirit2/phrase.cpp | 37 +++-
branches/quickbook-1.5-spirit2/phrase.hpp | 9 +
7 files changed, 33 insertions(+), 314 deletions(-)
Modified: branches/quickbook-1.5-spirit2/Jamfile.v2
==============================================================================
--- branches/quickbook-1.5-spirit2/Jamfile.v2 (original)
+++ branches/quickbook-1.5-spirit2/Jamfile.v2 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
@@ -30,6 +30,7 @@
phrase.cpp
phrase_actions.cpp
phrase_image.cpp
+ phrase_template.cpp
block.cpp
doc_info.cpp
detail/syntax_highlight.cpp
Modified: branches/quickbook-1.5-spirit2/detail/actions.cpp
==============================================================================
--- branches/quickbook-1.5-spirit2/detail/actions.cpp (original)
+++ branches/quickbook-1.5-spirit2/detail/actions.cpp 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
@@ -387,289 +387,6 @@
actions.template_info.clear();
}
- namespace
- {
- std::string::size_type find_bracket_end(std::string const& str, std::string::size_type pos)
- {
- unsigned int depth = 1;
-
- while(depth > 0) {
- pos = str.find_first_of("[]\\", pos);
- if(pos == std::string::npos) return pos;
-
- if(str[pos] == '\\')
- {
- pos += 2;
- }
- else
- {
- depth += (str[pos] == '[') ? 1 : -1;
- ++pos;
- }
- }
-
- return pos;
- }
-
- std::string::size_type find_first_seperator(std::string const& str)
- {
- if(qbk_version_n < 105) {
- return str.find_first_of(" \t\r\n");
- }
- else {
- std::string::size_type pos = 0;
-
- while(true)
- {
- pos = str.find_first_of(" \t\r\n\\[", pos);
- if(pos == std::string::npos) return pos;
-
- switch(str[pos])
- {
- case '[':
- pos = find_bracket_end(str, pos + 1);
- break;
- case '\\':
- pos += 2;
- break;
- default:
- return pos;
- }
- }
- }
- }
-
- bool break_arguments(
- std::vector<std::string>& params
- , std::vector<std::string> const& template_
- , boost::spirit::classic::file_position const& pos
- )
- {
- // Quickbook 1.4-: If there aren't enough parameters seperated by
- // '..' then seperate the last parameter using
- // whitespace.
- // Quickbook 1.5+: If '..' isn't used to seperate the parameters
- // then use whitespace to separate them
- // (2 = template name + argument).
-
- if (qbk_version_n < 105 || params.size() == 1)
- {
- // template_.size() - 2 because template_ also includes the name and body.
- while (params.size() < template_.size() - 2 )
- {
- // Try to break the last argument at the first space found
- // and push it into the back of params. Do this
- // recursively until we have all the expected number of
- // arguments, or if there are no more spaces left.
-
- std::string& str = params.back();
- std::string::size_type l_pos = find_first_seperator(str);
- if (l_pos == std::string::npos)
- break;
- std::string first(str.begin(), str.begin()+l_pos);
- std::string::size_type r_pos = str.find_first_not_of(" \t\r\n", l_pos);
- if (r_pos == std::string::npos)
- break;
- std::string second(str.begin()+r_pos, str.end());
- str = first;
- params.push_back(second);
- }
- }
-
- if (params.size() != template_.size() - 2)
- {
- detail::outerr(pos.file, pos.line)
- << "Invalid number of arguments passed. Expecting: "
- << template_.size()-2
- << " argument(s), got: "
- << params.size()
- << " argument(s) instead."
- << std::endl;
- return false;
- }
- return true;
- }
-
- std::pair<bool, std::vector<std::string>::const_iterator>
- get_arguments(
- std::vector<std::string>& params
- , std::vector<std::string> const& template_
- , template_scope const& scope
- , boost::spirit::classic::file_position const& pos
- , quickbook::actions& actions
- )
- {
- std::vector<std::string>::const_iterator arg = params.begin();
- std::vector<std::string>::const_iterator tpl = template_.begin()+1;
-
- // Store each of the argument passed in as local templates:
- while (arg != params.end())
- {
- std::vector<std::string> tinfo;
- tinfo.push_back(*tpl);
- tinfo.push_back(*arg);
- template_symbol template_(tinfo, pos, &scope);
-
- if (actions.templates.find_top_scope(*tpl))
- {
- detail::outerr(pos.file,pos.line)
- << "Duplicate Symbol Found" << std::endl;
- ++actions.error_count;
- return std::make_pair(false, tpl);
- }
- else
- {
- actions.templates.add(*tpl, template_);
- }
- ++arg; ++tpl;
- }
- return std::make_pair(true, tpl);
- }
-
- bool parse_template(
- std::string& body
- , std::string& result
- , boost::spirit::classic::file_position const& template_pos
- , bool template_escape
- , quickbook::actions& actions
- )
- {
- // How do we know if we are to parse the template as a block or
- // a phrase? We apply a simple heuristic: if the body starts with
- // a newline, then we regard it as a block, otherwise, we parse
- // it as a phrase.
-
- std::string::const_iterator iter = body.begin();
- while (iter != body.end() && ((*iter == ' ') || (*iter == '\t')))
- ++iter; // skip spaces and tabs
- bool is_block = (iter != body.end()) && ((*iter == '\r') || (*iter == '\n'));
- bool r = false;
-
- if (template_escape)
- {
- // escape the body of the template
- // we just copy out the literal body
- result = body;
- r = true;
- }
- else if (!is_block)
- {
- simple_phrase_grammar phrase_p(actions);
-
- // do a phrase level parse
- iterator first(body.begin(), body.end(), actions.filename.native_file_string().c_str());
- first.set_position(template_pos);
- iterator last(body.end(), body.end());
- r = boost::spirit::qi::parse(first, last, phrase_p) && first == last;
- actions.phrase.swap(result);
- }
- else
- {
- block_grammar block_p(actions);
-
- // do a block level parse
- // ensure that we have enough trailing newlines to eliminate
- // the need to check for end of file in the grammar.
- body.push_back('\n');
- body.push_back('\n');
- while (iter != body.end() && ((*iter == '\r') || (*iter == '\n')))
- ++iter; // skip initial newlines
- iterator first(iter, body.end(), actions.filename.native_file_string().c_str());
- first.set_position(template_pos);
- iterator last(body.end(), body.end());
- r = boost::spirit::qi::parse(first, last, block_p) && first == last;
- actions.out.swap(result);
- }
- return r;
- }
- }
-
- void do_template_action::operator()(iterator p, bool template_escape,
- template_symbol const& symbol, std::vector<std::string> params) const
- {
- boost::spirit::classic::file_position const pos = p.get_position();
- ++actions.template_depth;
- if (actions.template_depth > actions.max_template_depth)
- {
- detail::outerr(pos.file,pos.line)
- << "Infinite loop detected" << std::endl;
- --actions.template_depth;
- ++actions.error_count;
- return;
- }
-
- // The template arguments should have the scope that the template was
- // called from, not the template's own scope.
- //
- // Note that for quickbook 1.4- this value is just ignored when the
- // arguments are expanded.
- template_scope const& call_scope = actions.templates.top_scope();
-
- std::string result;
- actions.push(); // scope the actions' states
- {
- // Quickbook 1.4-: When expanding the tempalte continue to use the
- // current scope (the dynamic scope).
- // Quickbook 1.5+: Use the scope the template was defined in
- // (the static scope).
- if (qbk_version_n >= 105)
- actions.templates.set_parent_scope(*boost::get<2>(symbol));
-
- std::vector<std::string> template_ = boost::get<0>(symbol);
- boost::spirit::classic::file_position template_pos = boost::get<1>(symbol);
-
- ///////////////////////////////////
- // Break the arguments
- if (!break_arguments(params, template_, pos))
- {
- actions.pop(); // restore the actions' states
- --actions.template_depth;
- ++actions.error_count;
- return;
- }
-
- ///////////////////////////////////
- // Prepare the arguments as local templates
- bool get_arg_result;
- std::vector<std::string>::const_iterator tpl;
- boost::tie(get_arg_result, tpl) =
- get_arguments(params, template_,
- call_scope, pos, actions);
-
- if (!get_arg_result)
- {
- actions.pop(); // restore the actions' states
- --actions.template_depth;
- return;
- }
-
- ///////////////////////////////////
- // parse the template body:
- std::string body;
- body.assign(tpl->begin(), tpl->end());
- body.reserve(body.size()+2); // reserve 2 more
-
- if (!parse_template(body, result, template_pos, template_escape, actions))
- {
- detail::outerr(pos.file,pos.line)
- //<< "Expanding template:" << template_info[0] << std::endl
- << std::endl
- << "------------------begin------------------" << std::endl
- << body
- << "------------------end--------------------" << std::endl
- << std::endl;
- actions.pop(); // restore the actions' states
- --actions.template_depth;
- ++actions.error_count;
- return;
- }
- }
-
- actions.pop(); // restore the actions' states
- actions.phrase << result; // print it!!!
- --actions.template_depth;
- }
-
void variablelist_action::operator()(std::string const& title) const
{
actions.out << "<variablelist>\n";
Modified: branches/quickbook-1.5-spirit2/detail/actions.hpp
==============================================================================
--- branches/quickbook-1.5-spirit2/detail/actions.hpp (original)
+++ branches/quickbook-1.5-spirit2/detail/actions.hpp 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
@@ -468,21 +468,6 @@
quickbook::actions& actions;
};
- struct do_template_action
- {
- template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- struct result { typedef void type; };
-
- // Handles template substitutions
-
- do_template_action(quickbook::actions& actions)
- : actions(actions) {}
-
- void operator()(iterator, bool, template_symbol const&, std::vector<std::string>) const;
-
- quickbook::actions& actions;
- };
-
struct variablelist_action
{
// Handles variable lists
Modified: branches/quickbook-1.5-spirit2/detail/actions_class.cpp
==============================================================================
--- branches/quickbook-1.5-spirit2/detail/actions_class.cpp (original)
+++ branches/quickbook-1.5-spirit2/detail/actions_class.cpp 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
@@ -117,7 +117,6 @@
, macro_definition(*this)
, do_macro(phrase)
, template_body(*this)
- , do_template(*this)
, url_pre(url_pre_)
, url_post(url_post_)
, link_pre(link_pre_)
Modified: branches/quickbook-1.5-spirit2/detail/actions_class.hpp
==============================================================================
--- branches/quickbook-1.5-spirit2/detail/actions_class.hpp (original)
+++ branches/quickbook-1.5-spirit2/detail/actions_class.hpp 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
@@ -139,7 +139,6 @@
macro_definition_action macro_definition;
do_macro_action do_macro;
template_body_action template_body;
- do_template_action do_template;
char const* url_pre;
char const* url_post;
char const* link_pre;
Modified: branches/quickbook-1.5-spirit2/phrase.cpp
==============================================================================
--- branches/quickbook-1.5-spirit2/phrase.cpp (original)
+++ branches/quickbook-1.5-spirit2/phrase.cpp 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
@@ -69,6 +69,14 @@
(std::string, content)
)
+BOOST_FUSION_ADAPT_STRUCT(
+ quickbook::template_,
+ (quickbook::file_position, position)
+ (bool, escape)
+ (quickbook::template_symbol, symbol)
+ (std::vector<std::string>, params)
+)
+
namespace quickbook
{
namespace qi = boost::spirit::qi;
@@ -94,7 +102,6 @@
phrase_end,
escape, common,
hard_space, eol, inline_code, simple_format,
- template_,
code_block, replaceable, macro,
dummy_block,
brackets_1_4, template_inner_arg_1_5, brackets_1_5
@@ -102,6 +109,7 @@
qi::rule<iterator, file_position()> position;
+ qi::rule<iterator, quickbook::template_()> template_;
qi::rule<iterator, std::string()> template_arg_1_4, template_arg_1_5;
qi::rule<iterator, std::vector<std::string>() > template_args;
@@ -192,15 +200,16 @@
// Template call
template_ =
- ( qi::raw[qi::eps] // For the position of the template
- >> -qi::char_('`') // Attribute implicitly cast to bool
+ position
+ >> ( '`' >> qi::attr(true)
+ | qi::attr(false)
+ )
>> ( // Lookup the template name
(&qi::punct >> actions.templates.scope)
| (actions.templates.scope >> hard_space)
)
>> template_args
>> &qi::lit(']')
- ) [ph::bind(actions.do_template, ph::begin(qi::_1), qi::_2, qi::_3, qi::_4)]
;
template_args =
@@ -306,17 +315,17 @@
phrase_markup =
'['
- >> ( cond_phrase [actions.process]
- | image [actions.process]
- | url [actions.process]
- | link [actions.process]
- | anchor [actions.process]
- | source_mode [actions.process]
- | formatted [actions.process]
- | footnote [actions.process]
+ >> ( cond_phrase
+ | image
+ | url
+ | link
+ | anchor
+ | source_mode
+ | formatted
+ | footnote
| template_
- | break_ [actions.process]
- )
+ | break_
+ ) [actions.process]
>> ']'
;
Modified: branches/quickbook-1.5-spirit2/phrase.hpp
==============================================================================
--- branches/quickbook-1.5-spirit2/phrase.hpp (original)
+++ branches/quickbook-1.5-spirit2/phrase.hpp 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
@@ -13,6 +13,7 @@
#include <string>
#include <map>
#include <boost/spirit/include/classic_position_iterator.hpp>
+#include "detail/template_stack.hpp"
namespace quickbook
{
@@ -36,6 +37,13 @@
char const* pre;
char const* post;
};
+
+ struct template_ {
+ file_position position;
+ bool escape;
+ template_symbol symbol;
+ std::vector<std::string> params;
+ };
struct anchor {
char const* dummy;
@@ -72,6 +80,7 @@
};
void process(quickbook::actions&, source_mode const&);
+ void process(quickbook::actions&, template_ const&);
void process(quickbook::actions&, anchor const&);
void process(quickbook::actions&, link const&);
void process(quickbook::actions&, formatted const&);
Added: branches/quickbook-1.5-spirit2/phrase_template.cpp
==============================================================================
--- (empty file)
+++ branches/quickbook-1.5-spirit2/phrase_template.cpp 2010-01-27 17:01:34 EST (Wed, 27 Jan 2010)
@@ -0,0 +1,300 @@
+/*=============================================================================
+ Copyright (c) 2002 2004 2006 Joel de Guzman
+ Copyright (c) 2004 Eric Niebler
+ http://spirit.sourceforge.net/
+
+ Use, modification and distribution is subject to the Boost Software
+ License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================*/
+
+#include "./phrase.hpp"
+#include "./grammars.hpp"
+#include "./detail/actions_class.hpp"
+#include "./detail/quickbook.hpp"
+
+namespace quickbook
+{
+ namespace
+ {
+ std::string::size_type find_bracket_end(std::string const& str, std::string::size_type pos)
+ {
+ unsigned int depth = 1;
+
+ while(depth > 0) {
+ pos = str.find_first_of("[]\\", pos);
+ if(pos == std::string::npos) return pos;
+
+ if(str[pos] == '\\')
+ {
+ pos += 2;
+ }
+ else
+ {
+ depth += (str[pos] == '[') ? 1 : -1;
+ ++pos;
+ }
+ }
+
+ return pos;
+ }
+
+ std::string::size_type find_first_seperator(std::string const& str)
+ {
+ if(qbk_version_n < 105) {
+ return str.find_first_of(" \t\r\n");
+ }
+ else {
+ std::string::size_type pos = 0;
+
+ while(true)
+ {
+ pos = str.find_first_of(" \t\r\n\\[", pos);
+ if(pos == std::string::npos) return pos;
+
+ switch(str[pos])
+ {
+ case '[':
+ pos = find_bracket_end(str, pos + 1);
+ break;
+ case '\\':
+ pos += 2;
+ break;
+ default:
+ return pos;
+ }
+ }
+ }
+ }
+
+ bool break_arguments(
+ std::vector<std::string>& params
+ , std::vector<std::string> const& template_
+ , boost::spirit::classic::file_position const& pos
+ )
+ {
+ // Quickbook 1.4-: If there aren't enough parameters seperated by
+ // '..' then seperate the last parameter using
+ // whitespace.
+ // Quickbook 1.5+: If '..' isn't used to seperate the parameters
+ // then use whitespace to separate them
+ // (2 = template name + argument).
+
+ if (qbk_version_n < 105 || params.size() == 1)
+ {
+ // template_.size() - 2 because template_ also includes the name and body.
+ while (params.size() < template_.size() - 2 )
+ {
+ // Try to break the last argument at the first space found
+ // and push it into the back of params. Do this
+ // recursively until we have all the expected number of
+ // arguments, or if there are no more spaces left.
+
+ std::string& str = params.back();
+ std::string::size_type l_pos = find_first_seperator(str);
+ if (l_pos == std::string::npos)
+ break;
+ std::string first(str.begin(), str.begin()+l_pos);
+ std::string::size_type r_pos = str.find_first_not_of(" \t\r\n", l_pos);
+ if (r_pos == std::string::npos)
+ break;
+ std::string second(str.begin()+r_pos, str.end());
+ str = first;
+ params.push_back(second);
+ }
+ }
+
+ if (params.size() != template_.size() - 2)
+ {
+ detail::outerr(pos.file, pos.line)
+ << "Invalid number of arguments passed. Expecting: "
+ << template_.size()-2
+ << " argument(s), got: "
+ << params.size()
+ << " argument(s) instead."
+ << std::endl;
+ return false;
+ }
+ return true;
+ }
+
+ std::pair<bool, std::vector<std::string>::const_iterator>
+ get_arguments(
+ std::vector<std::string>& params
+ , std::vector<std::string> const& template_
+ , template_scope const& scope
+ , boost::spirit::classic::file_position const& pos
+ , quickbook::actions& actions
+ )
+ {
+ std::vector<std::string>::const_iterator arg = params.begin();
+ std::vector<std::string>::const_iterator tpl = template_.begin()+1;
+
+ // Store each of the argument passed in as local templates:
+ while (arg != params.end())
+ {
+ std::vector<std::string> tinfo;
+ tinfo.push_back(*tpl);
+ tinfo.push_back(*arg);
+ template_symbol template_(tinfo, pos, &scope);
+
+ if (actions.templates.find_top_scope(*tpl))
+ {
+ detail::outerr(pos.file,pos.line)
+ << "Duplicate Symbol Found" << std::endl;
+ ++actions.error_count;
+ return std::make_pair(false, tpl);
+ }
+ else
+ {
+ actions.templates.add(*tpl, template_);
+ }
+ ++arg; ++tpl;
+ }
+ return std::make_pair(true, tpl);
+ }
+
+ bool parse_template(
+ std::string& body
+ , std::string& result
+ , boost::spirit::classic::file_position const& template_pos
+ , bool template_escape
+ , quickbook::actions& actions
+ )
+ {
+ // How do we know if we are to parse the template as a block or
+ // a phrase? We apply a simple heuristic: if the body starts with
+ // a newline, then we regard it as a block, otherwise, we parse
+ // it as a phrase.
+
+ std::string::const_iterator iter = body.begin();
+ while (iter != body.end() && ((*iter == ' ') || (*iter == '\t')))
+ ++iter; // skip spaces and tabs
+ bool is_block = (iter != body.end()) && ((*iter == '\r') || (*iter == '\n'));
+ bool r = false;
+
+ if (template_escape)
+ {
+ // escape the body of the template
+ // we just copy out the literal body
+ result = body;
+ r = true;
+ }
+ else if (!is_block)
+ {
+ simple_phrase_grammar phrase_p(actions);
+
+ // do a phrase level parse
+ iterator first(body.begin(), body.end(), actions.filename.native_file_string().c_str());
+ first.set_position(template_pos);
+ iterator last(body.end(), body.end());
+ r = boost::spirit::qi::parse(first, last, phrase_p) && first == last;
+ actions.phrase.swap(result);
+ }
+ else
+ {
+ block_grammar block_p(actions);
+
+ // do a block level parse
+ // ensure that we have enough trailing newlines to eliminate
+ // the need to check for end of file in the grammar.
+ body.push_back('\n');
+ body.push_back('\n');
+ while (iter != body.end() && ((*iter == '\r') || (*iter == '\n')))
+ ++iter; // skip initial newlines
+ iterator first(iter, body.end(), actions.filename.native_file_string().c_str());
+ first.set_position(template_pos);
+ iterator last(body.end(), body.end());
+ r = boost::spirit::qi::parse(first, last, block_p) && first == last;
+ actions.out.swap(result);
+ }
+ return r;
+ }
+ }
+
+ void process(quickbook::actions& actions, template_ const& x)
+ {
+ ++actions.template_depth;
+ if (actions.template_depth > actions.max_template_depth)
+ {
+ detail::outerr(x.position.file,x.position.line)
+ << "Infinite loop detected" << std::endl;
+ --actions.template_depth;
+ ++actions.error_count;
+ return;
+ }
+
+ // The template arguments should have the scope that the template was
+ // called from, not the template's own scope.
+ //
+ // Note that for quickbook 1.4- this value is just ignored when the
+ // arguments are expanded.
+ template_scope const& call_scope = actions.templates.top_scope();
+
+ std::string result;
+ actions.push(); // scope the actions' states
+ {
+ // Quickbook 1.4-: When expanding the tempalte continue to use the
+ // current scope (the dynamic scope).
+ // Quickbook 1.5+: Use the scope the template was defined in
+ // (the static scope).
+ if (qbk_version_n >= 105)
+ actions.templates.set_parent_scope(*boost::get<2>(x.symbol));
+
+ std::vector<std::string> template_ = boost::get<0>(x.symbol);
+ boost::spirit::classic::file_position template_pos = boost::get<1>(x.symbol);
+
+ std::vector<std::string> params = x.params;
+
+ ///////////////////////////////////
+ // Break the arguments
+ if (!break_arguments(params, template_, x.position))
+ {
+ actions.pop(); // restore the actions' states
+ --actions.template_depth;
+ ++actions.error_count;
+ return;
+ }
+
+ ///////////////////////////////////
+ // Prepare the arguments as local templates
+ bool get_arg_result;
+ std::vector<std::string>::const_iterator tpl;
+ boost::tie(get_arg_result, tpl) =
+ get_arguments(params, template_,
+ call_scope, x.position, actions);
+
+ if (!get_arg_result)
+ {
+ actions.pop(); // restore the actions' states
+ --actions.template_depth;
+ return;
+ }
+
+ ///////////////////////////////////
+ // parse the template body:
+ std::string body;
+ body.assign(tpl->begin(), tpl->end());
+ body.reserve(body.size()+2); // reserve 2 more
+
+ if (!parse_template(body, result, template_pos, x.escape, actions))
+ {
+ detail::outerr(x.position.file,x.position.line)
+ //<< "Expanding template:" << template_info[0] << std::endl
+ << std::endl
+ << "------------------begin------------------" << std::endl
+ << body
+ << "------------------end--------------------" << std::endl
+ << std::endl;
+ actions.pop(); // restore the actions' states
+ --actions.template_depth;
+ ++actions.error_count;
+ return;
+ }
+ }
+
+ actions.pop(); // restore the actions' states
+ actions.phrase << result; // print it!!!
+ --actions.template_depth;
+ }
+}
\ No newline at end of file
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk