aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README22
-rw-r--r--interface.ccl2
-rw-r--r--param.ccl42
-rw-r--r--src/announce.cc97
-rw-r--r--src/make.code.defn2
-rw-r--r--src/make.code.deps34
-rw-r--r--src/make.configuration.deps2
-rw-r--r--src/portal.cc6
-rw-r--r--src/rdf.cc494
-rw-r--r--src/rdf.hh60
-rwxr-xr-xsrc/util/gethostname.pl38
11 files changed, 758 insertions, 41 deletions
diff --git a/README b/README
index d071c92..fa5c662 100644
--- a/README
+++ b/README
@@ -39,8 +39,6 @@ IOUtil should not depend on anything
use a configuration script to amend all thorns' make.code.deps files
to create the tarballs when the thorns are compiled
-use perl instead of C for makeblob and makemetablob
-
put arrangement name into thorn source name
use subdirectories for arrangements
use strings instead of chars for data
@@ -68,6 +66,19 @@ does not have to wait
make max_warn_level and output_info steerable
+register with Portal/Announce as information provider
+[Tom/Ian want to make Announce accept a table instead of XMLRPC]
+
+prevent connecting to the wrong simulation, if a simulation is
+outdated, and a new simulation uses the same hostname/port
+combination: the portal can store the job id, and if there is a new
+port id for the same hostname/port combination, deactivate the old
+link.
+
+Rebuild the flesh tarball when the ThornList changes
+
+factor out relaying into its own class
+
RSS: add RSS feed
@@ -84,3 +95,10 @@ a simple example is:
</item>
</channel>
</rss>
+
+
+
+Give each value a type. The type should describe the content, not the
+presentation. E.g.: "scalar double value, normal range [0..1]", or
+"keyword", or "keyword, possible values are ...", or "integer value,
+value range is [1..10]".
diff --git a/interface.ccl b/interface.ccl
index c963e8f..9a447c8 100644
--- a/interface.ccl
+++ b/interface.ccl
@@ -6,6 +6,8 @@ IMPLEMENTS: Formaline
# for HTTP_Port()
USES INCLUDE HEADER: http_Content.h
+USES INCLUDE HEADER: Announce.h
+
# Return a pointer to an unmodifiable C string
diff --git a/param.ccl b/param.ccl
index f561ba0..0d81ba4 100644
--- a/param.ccl
+++ b/param.ccl
@@ -16,6 +16,14 @@ REAL update_interval "Update interval for the meta information (in seconds)" STE
+# Parameters for creating files containing the build and job ids
+
+BOOLEAN create_id_files "Create files from the build and job ids"
+{
+} yes
+
+
+
# Parameters for announcing meta information to the portal
BOOLEAN announce_to_portal "Announce meta information to the portal" STEERABLE=always
@@ -37,15 +45,23 @@ STRING portal_username "User name on the portal" STEERABLE=always
"" :: ""
} ""
-BOOLEAN use_relay_host "Use a relay host for connecting to the portal" STEERABLE=always
+
+
+# Parameters for storing as RDF
+
+BOOLEAN send_as_rdf "Send meta as RDF to a server" STEERABLE=always
{
-} yes
+} no
-STRING relay_host "Relay host for connecting to the portal" STEERABLE=always
+STRING rdf_hostname "RDF server host name" STEERABLE=always
{
- ".+" :: "host name"
- "" :: "try to determine a relay host automatically"
-} ""
+ "" :: ""
+} "rdf.cct.lsu.edu"
+
+INT rdf_port "RDF server port" STEERABLE=always
+{
+ 1:65535 :: ""
+} 9296
@@ -62,6 +78,20 @@ STRING storage_filename "File name for meta information" STEERABLE=always
+# Parameters for relaying to remote servers
+
+BOOLEAN use_relay_host "Use a relay host for connecting to the portal" STEERABLE=always
+{
+} yes
+
+STRING relay_host "Relay host for connecting to the portal" STEERABLE=always
+{
+ ".+" :: "host name"
+ "" :: "try to determine a relay host automatically"
+} ""
+
+
+
# Parameters for storing the source tree in the executable
BOOLEAN output_source "Output a tarball with the cactus source tree" STEERABLE=recover
diff --git a/src/announce.cc b/src/announce.cc
index 3ebf384..fe3911e 100644
--- a/src/announce.cc
+++ b/src/announce.cc
@@ -47,6 +47,7 @@
#include "file.hh"
#include "multistorage.hh"
#include "portal.hh"
+#include "rdf.hh"
@@ -169,6 +170,29 @@ namespace Formaline
create_job_id (cctkGH);
+ if (create_id_files) {
+ // Create files from the build id and job id
+ // (This shows what jobs were run in the output directory)
+ {
+ ostringstream filenamebuf;
+ filenamebuf << out_dir << "/formaline-" << build_id;
+ string const filenamestring = filenamebuf.str();
+ ofstream fil;
+ fil.open (filenamestring.c_str(), ios::trunc);
+ fil << build_id << endl;
+ fil.close ();
+ }
+ {
+ ostringstream filenamebuf;
+ filenamebuf << out_dir << "/formaline-" << job_id;
+ string const filenamestring = filenamebuf.str();
+ ofstream fil;
+ fil.open (filenamestring.c_str(), ios::trunc);
+ fil << job_id << endl;
+ fil.close ();
+ }
+ }
+
// Announce
{
@@ -179,6 +203,11 @@ namespace Formaline
stores.add_storage (new portal (job_id, storage::initial));
}
+ if (send_as_rdf)
+ {
+ stores.add_storage (new rdf (job_id, storage::initial));
+ }
+
if (store_into_file)
{
stores.add_storage (new file (job_id, storage::initial));
@@ -309,19 +338,19 @@ namespace Formaline
{
// PBS logname
char const * const pbs_logname = getenv ("PBS_LOGNAME");
- stores.store ("PBS logname", pbs_logname);
+ stores.store ("PBS_LOGNAME", pbs_logname);
}
{
// PBS host
char const * const pbs_host = getenv ("PBS_HOST");
- stores.store ("PBS host", pbs_host);
+ stores.store ("PBS_HOST", pbs_host);
}
{
// PBS workdir
char const * const pbs_workdir = getenv ("PBS_WORKDIR");
- stores.store ("PBS workdir", pbs_workdir);
+ stores.store ("PBS_WORKDIR", pbs_workdir);
}
@@ -330,7 +359,7 @@ namespace Formaline
{
char const * const cactus_version = CCTK_FullVersion();
- stores.store ("Cactus version", cactus_version);
+ stores.store ("Cactus_version", cactus_version);
}
@@ -338,24 +367,24 @@ namespace Formaline
// Compiling
{
- stores.store ("build id", build_id);
+ stores.store ("build_id", build_id);
}
#if 0
{
char const * const compile_user = CCTK_CompileUser();
- stores.store ("compile user", compile_user);
+ stores.store ("compile_user", compile_user);
}
#endif
{
char const * const compile_date = CCTK_CompileDate();
- stores.store ("compile date", compile_date);
+ stores.store ("compile_date", compile_date);
}
{
char const * const compile_time = CCTK_CompileTime();
- stores.store ("compile time", compile_time);
+ stores.store ("compile_time", compile_time);
}
@@ -365,31 +394,31 @@ namespace Formaline
#if 0
{
char const * const run_user = CCTK_RunUser();
- stores.store ("run user", run_user);
+ stores.store ("run_user", run_user);
}
#else
{
char const * const run_user = getenv ("USER");
- stores.store ("run user", run_user);
+ stores.store ("run_user", run_user);
}
#endif
{
char run_date [1000];
Util_CurrentDate (sizeof run_date, run_date);
- stores.store ("run date", run_date);
+ stores.store ("run_date", run_date);
}
{
char run_time [1000];
Util_CurrentTime (sizeof run_time, run_time);
- stores.store ("run time", run_time);
+ stores.store ("run_time", run_time);
}
{
char run_host [1000];
Util_GetHostName (run_host, sizeof run_host);
- stores.store ("run host", run_host);
+ stores.store ("run_host", run_host);
}
{
@@ -399,7 +428,7 @@ namespace Formaline
assert (type == PARAMETER_STRING);
char const * const run_title
= * static_cast<char const * const *> (ptr);
- stores.store ("run title", run_title);
+ stores.store ("run_title", run_title);
}
@@ -424,7 +453,7 @@ namespace Formaline
{
char parameter_filename [10000];
CCTK_ParameterFilename (sizeof parameter_filename, parameter_filename);
- stores.store ("parameter filename", parameter_filename);
+ stores.store ("parameter_filename", parameter_filename);
}
// This is superfluous, and it does not look nice
@@ -440,7 +469,7 @@ namespace Formaline
fclose (file);
assert (count < sizeof parameter_file - 1);
parameter_file [count] = '\0';
- stores.store ("parameter file", parameter_file);
+ stores.store ("parameter_file", parameter_file);
}
#endif
@@ -448,11 +477,11 @@ namespace Formaline
char cwd [10000];
getcwd (cwd, sizeof cwd);
cwd [sizeof cwd - 1] = '\0'; // just to be sure
- stores.store ("current dir", cwd);
+ stores.store ("current_dir", cwd);
}
{
- stores.store ("out dir", out_dir);
+ stores.store ("out_dir", out_dir);
}
{
@@ -669,6 +698,11 @@ namespace Formaline
stores.add_storage (new portal (job_id, storage::update));
}
+ if (send_as_rdf)
+ {
+ stores.add_storage (new rdf (job_id, storage::initial));
+ }
+
if (store_into_file)
{
stores.add_storage (new file (job_id, storage::update));
@@ -683,13 +717,13 @@ namespace Formaline
{
char run_date [1000];
Util_CurrentDate (sizeof run_date, run_date);
- stores.store ("run date", run_date);
+ stores.store ("run_date", run_date);
}
{
char run_time [1000];
Util_CurrentTime (sizeof run_time, run_time);
- stores.store ("run time", run_time);
+ stores.store ("run_time", run_time);
}
@@ -858,6 +892,11 @@ namespace Formaline
stores.add_storage (new portal (job_id, storage::final));
}
+ if (send_as_rdf)
+ {
+ stores.add_storage (new rdf (job_id, storage::initial));
+ }
+
if (store_into_file)
{
stores.add_storage (new file (job_id, storage::final));
@@ -872,13 +911,13 @@ namespace Formaline
{
char run_date [1000];
Util_CurrentDate (sizeof run_date, run_date);
- stores.store ("run date", run_date);
+ stores.store ("run_date", run_date);
}
{
char run_time [1000];
Util_CurrentTime (sizeof run_time, run_time);
- stores.store ("run time", run_time);
+ stores.store ("run_time", run_time);
}
@@ -982,7 +1021,12 @@ namespace Formaline
{
stores.add_storage (new portal (job_id, storage::update));
}
-
+
+ if (send_as_rdf)
+ {
+ stores.add_storage (new rdf (job_id, storage::initial));
+ }
+
if (store_into_file)
{
stores.add_storage (new file (job_id, storage::update));
@@ -1053,7 +1097,12 @@ namespace Formaline
{
stores.add_storage (new portal (job_id, storage::update));
}
-
+
+ if (send_as_rdf)
+ {
+ stores.add_storage (new rdf (job_id, storage::initial));
+ }
+
if (store_into_file)
{
stores.add_storage (new file (job_id, storage::update));
diff --git a/src/make.code.defn b/src/make.code.defn
index ca1a706..9b4f9e1 100644
--- a/src/make.code.defn
+++ b/src/make.code.defn
@@ -2,7 +2,7 @@
# $Header$
# Source files in this directory
-SRCS = announce.cc file.cc multistorage.cc output_source.c portal.cc storage.cc
+SRCS = announce.cc file.cc multistorage.cc output_source.c portal.cc rdf.cc storage.cc
# Subdirectories containing source files
SUBDIRS =
diff --git a/src/make.code.deps b/src/make.code.deps
index a1bfdb0..04c3713 100644
--- a/src/make.code.deps
+++ b/src/make.code.deps
@@ -1,21 +1,47 @@
# make.code.deps file for thorn Formaline -*-Makefile-*-
# $Header$
-# Store the "makeblob" utilities in the scratch directory of this
-# configuration.
+# Store the utilities in the scratch directory of this configuration.
# This has to live here in the file make.code.deps instead of in the
# file make.configuration.deps because only here the location of the
-# source file makeblob.c is known.
+# source directory is known.
TARBALL_DIR = $(SCRATCH_BUILD)
-$(CCTK_TARGET): $(TARBALL_DIR)/makeblob.pl $(TARBALL_DIR)/makemetablob.pl
+$(CCTK_TARGET): $(TARBALL_DIR)/gethostname.pl $(TARBALL_DIR)/makeblob.pl $(TARBALL_DIR)/makemetablob.pl
+$(TARBALL_DIR)/gethostname.pl: $(SRCDIR)/util/gethostname.pl
+ cd $(TARBALL_DIR) && cp $^ $@
+
$(TARBALL_DIR)/makeblob.pl: $(SRCDIR)/util/makeblob.pl
cd $(TARBALL_DIR) && cp $^ $@
$(TARBALL_DIR)/makemetablob.pl: $(SRCDIR)/util/makemetablob.pl
cd $(TARBALL_DIR) && cp $^ $@
+
+
+
+
+
+
+# Unique ID for the build
+$(TARBALL_DIR)/build-id.o: $(TARBALL_DIR)/build-id.c
+ $(CC) $(CFLAGS) -c -o $@ $^
+
+# (force a new ID to be created every time)
+.PHONY: $(TARBALL_DIR)/build-id.c
+$(TARBALL_DIR)/build-id.c:
+ { \
+ echo '/* This is an auto-generated file -- do not edit */'; \
+ hostname=`hostname`; \
+ user="$$USER"; \
+ timestamp=`date +%Y%m%d-%H%M%S`; \
+ pid="$$$$"; \
+ id="build-$$hostname-$$user-$$timestamp-$$pid"; \
+ echo 'char const build_id[] = "'$$id'";'; \
+ } > $@
+
+.PRECIOUS: $(TARBALL_DIR)/build-id.c $(TARBALL_DIR)/build-id.o
diff --git a/src/make.configuration.deps b/src/make.configuration.deps
index ca933e4..ada35c9 100644
--- a/src/make.configuration.deps
+++ b/src/make.configuration.deps
@@ -45,7 +45,7 @@ $(TARBALL_DIR)/build-id.o: $(TARBALL_DIR)/build-id.c
$(TARBALL_DIR)/build-id.c:
{ \
echo '/* This is an auto-generated file -- do not edit */'; \
- hostname=`hostname`; \
+ hostname=`$(TARBALL_DIR)/gethostname.pl`; \
user="$$USER"; \
timestamp=`date +%Y%m%d-%H%M%S`; \
pid="$$$$"; \
diff --git a/src/portal.cc b/src/portal.cc
index b5f79a9..e2acea5 100644
--- a/src/portal.cc
+++ b/src/portal.cc
@@ -30,7 +30,7 @@ namespace Formaline
static bool
- is_clean_for_shell (char const * string);
+ is_clean_for_shell (char const * str);
@@ -398,9 +398,9 @@ namespace Formaline
static bool
- is_clean_for_shell (char const * const string)
+ is_clean_for_shell (char const * const str)
{
- for (char const * p = string; * p; ++ p)
+ for (char const * p = str; * p; ++ p)
{
if (! isalnum (* p))
{
diff --git a/src/rdf.cc b/src/rdf.cc
new file mode 100644
index 0000000..028f54b
--- /dev/null
+++ b/src/rdf.cc
@@ -0,0 +1,494 @@
+// $Header$
+
+#include <cassert>
+#include <cctype>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <list>
+#include <string>
+#include <sstream>
+
+#include <unistd.h>
+
+#include "cctk.h"
+#include "cctk_Parameters.h"
+#include "util_Network.h"
+
+#include "rdf.hh"
+
+
+
+namespace Formaline
+{
+
+ using namespace std;
+
+
+
+ static bool
+ is_clean_for_shell (char const * str);
+
+ static list<string>
+ parse (string const str);
+
+
+
+ rdf::
+ rdf (char const * const id,
+ enum state const st)
+ : storage (st)
+ {
+ msgbuf
+<< "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << endl
+<< "<!DOCTYPE owl [" << endl
+<< " <!ENTITY dc 'http://purl.org/dc/elements/1.1/'>" << endl
+<< " <!ENTITY doap 'http://usefulinc.com/ns/doap#'>" << endl
+<< " <!ENTITY foaf 'http://xmlns.com/foaf/0.1/'>" << endl
+<< " <!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>" << endl
+<< " <!ENTITY rdfs 'http://www.w3.org/2000/01/rdf-schema#'>" << endl
+<< "" << endl
+<< " <!ENTITY cctk 'http://www.cct.lsu.edu/~dstark/cctk/0.1/'>" << endl
+<< " <!ENTITY form 'http://www.aei.mpg.de/form#'>" << endl
+<< "]>" << endl
+<< "<rdf:RDF" << endl
+<< " xmlns:dc=\"&dc;\"" << endl
+<< " xmlns:doap=\"&doap;\"" << endl
+<< " xmlns:foaf=\"&foaf;\"" << endl
+<< " xmlns:rdf=\"&rdf;\"" << endl
+<< " xmlns:rdfs=\"&rdfs;\"" << endl
+<< " xmlns:cctk=\"&cctk;\"" << endl
+<< " xmlns:form=\"&form;\"" << endl
+<< ">" << endl
+<< endl
+<< "<form:Simulation>" << endl;
+ }
+
+
+
+ rdf::
+ ~ rdf ()
+ {
+ DECLARE_CCTK_PARAMETERS;
+
+ string const socket_script = "socket-client.pl";
+ string const socket_data = "socket-data";
+
+
+
+ // Write the data
+ msgbuf
+<< "</form:Simulation" << endl;
+ string const msgstr = msgbuf.str();
+
+ ostringstream databuf;
+ databuf << "POST HTTP/1.0 200\r\n"
+ << "Content-Type: text/xml\r\n"
+ << "Content-Length: " << msgstr.length() << "\r\n"
+ << "\r\n"
+ << msgstr
+ << "\r\n"
+ << "\r\n";
+ string const datastr = databuf.str();
+
+ ostringstream datafilenamebuf;
+ datafilenamebuf << out_dir << "/" << socket_data;
+ string const datafilenamestr = datafilenamebuf.str();
+ char const * const datafilename = datafilenamestr.c_str();
+
+ ofstream datafile;
+ datafile.open (datafilename, ios::out);
+ datafile << datastr;
+ datafile.close ();
+
+
+
+ // Write the script
+ ostringstream scriptbuf;
+ scriptbuf
+<< "#! /usr/bin/perl -w" << endl
+<< endl
+<< "use strict;" << endl
+<< "use Socket;" << endl
+<< endl
+<< "my $input = '" << datafilename << "';" << endl
+<< "my $host = '" << rdf_hostname << "';" << endl
+<< "my $port = '" << rdf_port << "';" << endl
+<< endl
+<< "open (my $FH, '<' . $input);" << endl
+<< endl
+<< "socket (my $SH, PF_INET, SOCK_STREAM, getprotobyname ('tcp'));" << endl
+<< "my $sin = sockaddr_in ($port, inet_aton ($host));" << endl
+<< "connect ($SH, $sin) || exit -1;" << endl
+<< endl
+<< "while (my $line = <$FH>)" << endl
+<< "{" << endl
+<< " print $SH $line;" << endl
+<< "}" << endl
+<< endl
+<< "close $SH;" << endl;
+ string const scriptstr = scriptbuf.str();
+
+ ostringstream scriptfilenamebuf;
+ scriptfilenamebuf << out_dir << "/" << socket_script;
+ string const scriptfilenamestr = scriptfilenamebuf.str();
+ char const * const scriptfilename = scriptfilenamestr.c_str();
+
+ ofstream scriptfile;
+ scriptfile.open (scriptfilename, ios::out);
+ scriptfile << scriptstr;
+ scriptfile.close ();
+
+
+
+ // Check that the file name is sane
+ if (! is_clean_for_shell (scriptfilename))
+ {
+ static bool did_complain = false;
+ if (! did_complain)
+ {
+ did_complain = true;
+ CCTK_WARN (1, "Strange character in file name -- not calling system()");
+ return;
+ }
+ }
+
+
+
+ // Make the script executable
+ ostringstream chmodbuf;
+ chmodbuf << "chmod a+x " << scriptfilenamestr
+ << " < /dev/null > /dev/null 2> /dev/null";
+ string const chmodstr = chmodbuf.str();
+ char const * const chmod = chmodstr.c_str();
+ system (chmod);
+
+
+
+ bool my_use_relay_host = use_relay_host;
+ char const * my_relay_host = 0;
+ if (my_use_relay_host)
+ {
+ my_relay_host = relay_host;
+ if (strcmp (my_relay_host, "") == 0)
+ {
+ // Determine a good relay host
+ char run_host [1000];
+ Util_GetHostName (run_host, sizeof run_host);
+ if (strncmp (run_host, "ic", 2) == 0 && strlen (run_host) == 6)
+ {
+ // Peyote or Lagavulin
+ int const node = atoi (run_host + 2);
+ if (node < 192)
+ {
+ // Peyote
+ my_relay_host = "peyote";
+ }
+ else
+ {
+ // Lagavulin
+ my_relay_host = "lagavulin";
+ }
+ }
+ else if (strncmp (run_host, "mike", 4) == 0 && strlen (run_host) == 7)
+ {
+ // Supermike
+ my_use_relay_host = false;
+ }
+ else
+ {
+ // Don't know a good relay host; try without
+ my_use_relay_host = false;
+ }
+
+ if (verbose)
+ {
+ if (my_use_relay_host)
+ {
+ CCTK_VInfo (CCTK_THORNSTRING,
+ "Using \"%s\" as relay host", my_relay_host);
+ }
+ else
+ {
+ CCTK_INFO ("Announcing without relay host");
+ }
+ }
+ }
+ }
+
+ if (my_use_relay_host)
+ {
+ // Check that the relay host name is sane
+ if (! is_clean_for_shell (my_relay_host))
+ {
+ static bool did_complain = false;
+ if (! did_complain)
+ {
+ did_complain = true;
+ CCTK_WARN (1, "Strange character in relay host name -- not calling system()");
+ return;
+ }
+ }
+ }
+
+
+
+ char cwd[10000];
+ if (my_use_relay_host)
+ {
+ // Get the current directory
+ char * const cwderr = getcwd (cwd, sizeof cwd);
+ if (cwderr == NULL) {
+ static bool did_complain = false;
+ if (! did_complain)
+ {
+ did_complain = true;
+ CCTK_WARN (1, "Cannot determine current working directory");
+ return;
+ }
+ }
+
+ // Check that the current directory name is sane
+ if (! is_clean_for_shell (cwd))
+ {
+ static bool did_complain = false;
+ if (! did_complain)
+ {
+ did_complain = true;
+ CCTK_WARN (1, "Strange character in current directory -- not calling system()");
+ return;
+ }
+ }
+ }
+ else
+ {
+ cwd[0] = '\0';
+ }
+
+
+
+ // Send the data
+ ostringstream cmdbuf;
+ if (my_use_relay_host)
+ {
+ cmdbuf << "ssh " << my_relay_host << " '"
+ << "cd " << cwd << " && ";
+ }
+ cmdbuf << scriptfilenamestr << " < /dev/null > /dev/null 2> /dev/null";
+ if (my_use_relay_host)
+ {
+ cmdbuf << "'";
+ }
+ string const cmdstr = cmdbuf.str();
+ char const * const cmd = cmdstr.c_str();
+
+ int const ierr = system (cmd);
+ if (ierr != 0)
+ {
+ static bool did_complain = false;
+ if (! did_complain)
+ {
+ did_complain = true;
+ CCTK_WARN (1, "Failed to send data to the rdf");
+ }
+ }
+
+ remove (datafilename);
+ remove (scriptfilename);
+ }
+
+
+
+ void rdf::
+ store (char const * const key,
+ bool const value)
+ {
+ assert (key);
+
+ ostringstream keybuf;
+ keybuf << key;
+ ostringstream valuebuf;
+ valuebuf << (value ? "true" : "false");
+
+ list<string> const keys = parse (keybuf.str());
+
+ msgbuf << " ";
+ for (list<string>::const_iterator lsi = keys.begin();
+ lsi != keys.end(); ++ lsi)
+ {
+ msgbuf << "<form:" << * lsi << ">";
+ }
+ msgbuf << clean (valuebuf.str());
+ for (list<string>::const_reverse_iterator lsi = keys.rbegin();
+ lsi != keys.rend(); ++ lsi)
+ {
+ msgbuf << "</form:" << * lsi << ">";
+ }
+ msgbuf << endl;
+ }
+
+
+
+ void rdf::
+ store (char const * const key,
+ int const value)
+ {
+ assert (key);
+
+ ostringstream keybuf;
+ keybuf << key;
+ ostringstream valuebuf;
+ valuebuf << value;
+
+ list<string> const keys = parse (keybuf.str());
+
+ msgbuf << " ";
+ for (list<string>::const_iterator lsi = keys.begin();
+ lsi != keys.end(); ++ lsi)
+ {
+ msgbuf << "<form:" << * lsi << ">";
+ }
+ msgbuf << clean (valuebuf.str());
+ for (list<string>::const_reverse_iterator lsi = keys.rbegin();
+ lsi != keys.rend(); ++ lsi)
+ {
+ msgbuf << "</form:" << * lsi << ">";
+ }
+ msgbuf << endl;
+ }
+
+
+
+ void rdf::
+ store (char const * const key,
+ double const value)
+ {
+ assert (key);
+
+ ostringstream keybuf;
+ keybuf << key;
+ ostringstream valuebuf;
+ valuebuf << setprecision(15) << value;
+
+ list<string> const keys = parse (keybuf.str());
+
+ msgbuf << " ";
+ for (list<string>::const_iterator lsi = keys.begin();
+ lsi != keys.end(); ++ lsi)
+ {
+ msgbuf << "<form:" << * lsi << ">";
+ }
+ msgbuf << clean (valuebuf.str());
+ for (list<string>::const_reverse_iterator lsi = keys.rbegin();
+ lsi != keys.rend(); ++ lsi)
+ {
+ msgbuf << "</form:" << * lsi << ">";
+ }
+ msgbuf << endl;
+ }
+
+
+
+ void rdf::
+ store (char const * const key,
+ char const * const value)
+ {
+ assert (key);
+
+ ostringstream keybuf;
+ keybuf << key;
+ ostringstream valuebuf;
+ valuebuf << value;
+
+ list<string> const keys = parse (keybuf.str());
+
+ msgbuf << " ";
+ for (list<string>::const_iterator lsi = keys.begin();
+ lsi != keys.end(); ++ lsi)
+ {
+ msgbuf << "<form:" << * lsi << ">";
+ }
+ msgbuf << clean (valuebuf.str());
+ for (list<string>::const_reverse_iterator lsi = keys.rbegin();
+ lsi != keys.rend(); ++ lsi)
+ {
+ msgbuf << "</form:" << * lsi << ">";
+ }
+ msgbuf << endl;
+ }
+
+
+
+ string rdf::
+ clean (string const & txt)
+ const
+ {
+ ostringstream buf;
+
+ for (string::const_iterator p = txt.begin(); p != txt.end(); ++ p)
+ {
+ switch (* p)
+ {
+ case '<': buf << "&lt;"; break;
+ case '&': buf << "&amp;"; break;
+ default: buf << * p;
+ }
+ }
+
+ return buf.str();
+ }
+
+
+
+ static bool
+ is_clean_for_shell (char const * const str)
+ {
+ for (char const * p = str; * p; ++ p)
+ {
+ if (! isalnum (* p))
+ {
+ // Allow only certain characters
+ switch (* p)
+ {
+ case '+':
+ case ',':
+ case '-':
+ case '.':
+ case '/':
+ case ':':
+ case '_':
+ case '~':
+ break;
+ default:
+ // We don't like this character
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+
+
+ static list<string>
+ parse (string const str)
+ {
+ list<string> strs;
+ size_t p = 0;
+ for (;;) {
+ size_t const s = str.find ("/", p);
+ if (s == string::npos) break;
+ strs.push_back (str.substr (p, s - p));
+ p = s + 1;
+ }
+ strs.push_back (str.substr (p));
+ return strs;
+ }
+
+
+
+} // namespace Formaline
diff --git a/src/rdf.hh b/src/rdf.hh
new file mode 100644
index 0000000..610842f
--- /dev/null
+++ b/src/rdf.hh
@@ -0,0 +1,60 @@
+// $Header$
+
+#ifndef FORMALINE_RDF_HH
+#define FORMALINE_RDF_HH
+
+#include <sstream>
+#include <string>
+
+#include "storage.hh"
+
+
+
+namespace Formaline
+{
+
+
+
+ class rdf : public storage
+ {
+
+ std::ostringstream msgbuf;
+
+ public:
+
+ rdf (char const * id,
+ enum state st);
+
+ virtual
+ ~ rdf ();
+
+ virtual void
+ store (char const * key,
+ bool value);
+
+ virtual void
+ store (char const * key,
+ int value);
+
+ virtual void
+ store (char const * key,
+ double value);
+
+ virtual void
+ store (char const * key,
+ char const * value);
+
+ private:
+
+ std::string
+ clean (std::string const & txt)
+ const;
+ };
+
+
+
+} // namespace Formaline
+
+
+
+#endif // ifndef FORMALINE_RDF_HH
diff --git a/src/util/gethostname.pl b/src/util/gethostname.pl
new file mode 100755
index 0000000..d403e05
--- /dev/null
+++ b/src/util/gethostname.pl
@@ -0,0 +1,38 @@
+#! /usr/bin/perl -w
+
+# Find the host name of this machine
+
+# 2006-05-02 Erik Schnetter <schnetter@cct.lsu.edu>
+
+# Search through the host name and all aliases.
+# Use the first name that contains dots, indicating that this name is
+# a long host name that includes a domain name.
+# If there is no name that includes a domain name, use whatever the
+# system calls "host name".
+
+use strict;
+
+# Get the system's idea of its host name
+my $hostname = `hostname`;
+chomp $hostname;
+
+# Find its host name and all aliases
+my ($name, $aliases, $addrtype, $length, @addrs) = gethostbyname ($hostname);
+
+# Split the aliases
+my @names = ($name, split (' ', $aliases));
+
+# Use the host name as fallback
+my $goodname = $name;
+
+# Search for a name that contains a dot
+foreach my $maybename (@names)
+{
+ if ($maybename =~ /[.]/)
+ {
+ $goodname = $maybename;
+ last;
+ }
+}
+
+print "$goodname\n";