anchor.diff:      true -> has_newline(T)
     end.
   
  -do(Command, Args) -> 
  -  case has_newline(Args) of
  -    true  -> { error, "No newlines" };
  -    false -> gen_server:call (?MODULE, { do, Command, Args } ) 
  -  end.
  -% rrd commands
  -create     (Args) when is_list(Args) -> do(create,     Args).
  -update     (Args) when is_list(Args) -> do(update,     Args).
  -updatev    (Args) when is_list(Args) -> do(updatev,    Args).
  -dump       (Args) when is_list(Args) -> do(dump,       Args).
  -restore    (Args) when is_list(Args) -> do(restore,    Args).
  -last       (Args) when is_list(Args) -> do(last,       Args).
  -lastupdate (Args) when is_list(Args) -> do(lastupdate, Args).
  -first      (Args) when is_list(Args) -> do(first,      Args).
  -info       (Args) when is_list(Args) -> do(info,       Args).
  -fetch      (Args) when is_list(Args) -> do(fetch,      Args).
  -tune       (Args) when is_list(Args) -> do(tune,       Args).
  -resize     (Args) when is_list(Args) -> do(resize,     Args).
  -xport      (Args) when is_list(Args) -> do(xport,      Args).
  -graph      (Args) when is_list(Args) -> 
  -  % TODO: regexp:match needs a string, will break w/binarys
  -  %   lists:flatten needs a deeplist, will break with iolists
  -  %   fix both of these.
  -  case regexp:match(lists:flatten([ [ " ", X ] || X <- Args ]), " -( |$)") of
  -    { match, _, _ } -> 
  -      % graph to stdout will break this Ports parsing of reponses..
  -      { error, "Graphing to stdout not supported." };
  -    nomatch -> 
  -      do(graph, Args)
  -  end.
  -
  -% rrd "remote" commands
  -cd         (Arg)  when is_list(Arg) -> do(cd,         Arg).
  -mkdir      (Arg)  when is_list(Arg) -> do(mkdir,      Arg).
  -ls         ()     -> do(ls,         []  ).
  -pwd        ()     -> do(pwd,        []  ).
  -quit() -> stop().
  -
  -iodata_to_deeplist([ H | B ]) 
  -  when is_binary(B) -> [ iodata_to_deeplist(H) | [B] ];
  -iodata_to_deeplist([H | T] ) ->
  -  [ iodata_to_deeplist(H) | iodata_to_deeplist(T) ];
  -iodata_to_deeplist(X) -> X.
  -
  -%iodata_has(_L, []) -> true;
  -%iodata_has([H|T], For) 
  -%  when is_list(H); is_binary(H) -> 
  -%    case iodata_has(H, For) of
  -%      true -> true;
  -%      false -> iodata_has(T, For)
  -%    end;
  -%iodata_has([H|T], [Hf,Tf]) 
  -%  when is_integer(H),  ->
  -    
  -
  -init(_Args) -> 
  -  % TODO choose mechanism to allow user to set rrdtool
  -  ExtProg = "rrdtool -",
  -  process_flag(trap_exit, true),
  -  Port = erlang:open_port({spawn, ExtProg}, [ {line, 10000}, eof, exit_status, stream ] ),
  -  {ok, #state{port = Port}}.
  -
  -%%
  -%% handle_call
  -%%
  -handle_call({do, Action, Args }, _From, #state{port = Port} = State) ->
  -    Line = [ erlang:atom_to_list(Action), " ", Args , "\n"],
  -    io:format("command to send: ~p\n", [ Line ] ),
  -    port_command(Port, Line),
  -    case collect_response(Port) of
  -        {response, Response} -> 
  -            {reply, { ok, Response }, State};
  -        { error, timeout } ->
  -            {stop, port_timeout, State};
  -        { error, Error } -> 
  -            {reply, { error, Error  }, State}
  -    end;
  -
  -handle_call(stop, _From, State) -> 
  -  {stop, normal, stopped, State}.
  -
  -%% handle_cast
  -handle_cast(_Msg, State) -> {noreply, State}.
  -
  -%% handle_info
  -handle_info(_Info, State) -> {noreply, State}.
  -
  -%% terminate
  -terminate(_Reason, _State) -> ok.
  -
  -%% code_change
  -code_change(OldVsn, State, _Extra) -> 
  -  io:format("Yo! code load time!!! ~p ~n",[OldVsn]), 
  -  {ok, State}.
  -
  -%%%%
  -
  -%
  +
   collect_response(Port) ->
       collect_response(Port, [], []).
   
  
positive.code: -export([stop/0]).
  -export([combine/1, c/1]).
  
  -export([join/2]).
  %-export([do/2]).
  -export([has_newline/1]).
  -export([iodata_to_deeplist/1]).
  
  -behaviour(gen_server).
  -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
  
negative.code: 
  -record( state, { port }  ).
  
  %-export([join/2]).
  %-export([do/2]).
  %-export([has_newline/1]).
  %-export([iodata_to_deeplist/1]).
  
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %% Public 
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  %% @spec start() -> any()
  %% @doc start the rrdtool gen_server
  %%    calls gen_server:start
  start()      -> gen_server:start     ({local, ?MODULE}, ?MODULE, [], []).
  %% @spec stop() -> any()
  %% @doc stop the rrdtool gen_server
  stop()       -> gen_server:call      (?MODULE, stop).
  
  %% @spec combine(List) -> List
  %%   List = [ term() ]
  %% @doc "joins" and quotes the given arg list. 
  %%   takes a list of arguments, and returns a deeplist with 
  %%   each argument surrounded by double quotes
  %%   then separated by spaces
  %%
  %%   combine(["these", "are", "my args"]). ->
  %%
  %%   [["\"","these","\""]," ",["\"","are","\""]," ",["\"","my args","\""]]
  %%
  %%   it is intended as a convinence function to the 
  %%   rrdtool commands which all take a single iodata() argument
  %%   which represents the string to be passed as the arguments 
  %%   to the corresponding rrdtool command.  
  %%
  %%   erlrrd:xport(erlrrd:c(["DEF:foo=/path with/space/foo.rrd:foo:AVERAGE", "XPORT:foo"])).
  combine(Args) -> 
      join([ [ "\"", X, "\"" ] || X <- Args ], " ").
  %% @spec c(List) -> List
  %%   List = [ term() ]
  % @equiv combine(Args)
  c(Args) -> combine(Args).
  
  
  % rrdtool commands
  
  %% @spec create(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc Set up a new Round Robin Database (RRD). Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html rrdcreate].
  create     (Args) when is_list(Args) -> do(create,     Args).
  
  %% @spec update(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc Store new data values into an RRD. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html rrdupdate].
  update     (Args) when is_list(Args) -> do(update,     Args).
  
  %% @spec updatev(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc Operationally equivalent to update except for output. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html rrdupdate].
  updatev    (Args) when is_list(Args) -> do(updatev,    Args).
  
  %% @spec dump(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc    Dump the contents of an RRD in plain ASCII. In connection with
  %%         restore you can use this to move an RRD from one computer
  %%         architecture to another.  Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrddump.en.html rrddump].
  dump       (Args) when is_list(Args) -> do(dump,       Args).
  
  %% @spec restore(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc Restore an RRD in XML format to a binary RRD. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdrestore.en.html rrdrestore]
  restore    (Args) when is_list(Args) -> do(restore,    Args).
  
  %% @spec last(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc    Return the date of the last data sample in an RRD. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html rrdlast]
  last       (Args) when is_list(Args) -> do(last,       Args).
  
  %% @spec lastupdate(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc    Return the most recent update to an RRD. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdlastupdate.en.html rrdlastupdate]
  lastupdate (Args) when is_list(Args) -> do(lastupdate, Args).
  
  %% @spec first(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc Return the date of the first data sample in an RRA within an
  %%       RRD. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html rrdfirst]
  first      (Args) when is_list(Args) -> do(first,      Args).
  
  %% @spec info(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc Get information about an RRD. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html rrdinfo].
  info       (Args) when is_list(Args) -> do(info,       Args).
  
  %% @spec fetch(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc   Get data for a certain time period from a RRD. The graph func-
  %%         tion uses fetch to retrieve its data from an RRD. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html rrdfetch].
  fetch      (Args) when is_list(Args) -> do(fetch,      Args).
  
  %% @spec tune(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc Alter setup of an RRD. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html rrdtune].
  tune       (Args) when is_list(Args) -> do(tune,       Args).
  
  %% @spec resize(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc    Change the size of individual RRAs. This is dangerous! Check
  %%         rrdresize.
  resize     (Args) when is_list(Args) -> do(resize,     Args).
  
  %% @spec xport(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc   Export data retrieved from one or several RRDs. Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdxport.en.html rrdxport]
  %%
  %%  erlrrd:xport("'DEF:foo=/path with/space/foo.rrd:foo:AVERAGE' XPORT:foo").
  %%
  %%  erlrrd:xport(erlrrd:c(["DEF:foo=/path with/space/foo.rrd:foo:AVERAGE", "XPORT:foo"])).
  xport      (Args) when is_list(Args) -> do(xport,      Args).
  
  %% @spec graph(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc   Create a graph from data stored in one or several RRDs. Apart
  %%         from generating graphs, data can also be extracted to stdout.
  %%         Check 
  %% [http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html rrdgraph].
  graph      (Args) when is_list(Args) -> 
    % TODO: regexp:match needs a string, will break w/binarys
    %   lists:flatten needs a deeplist, will break with iolists
    %   fix both of these.
    case regexp:match(lists:flatten([ [ " ", X ] || X <- Args ]), " -( |$)") of
      { match, _, _ } -> 
        % graph to stdout will break this Ports parsing of reponses..
        { error, "Graphing to stdout not supported." };
      nomatch -> 
        do(graph, Args)
    end.
  
  % rrd "remote" commands
  %% @spec cd(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc   ask the rrdtool unix process to change directories
  %% 
  %%    erlrrd:cd("/usr/share/rrd/data").
  %%
  %%    erlrrd:cd(erlrrd:combine(["/Users/foo/Library/Application Support/myapp/rrd"]).
  cd         (Arg)  when is_list(Arg) -> do(cd,         Arg).
  
  %% @spec mkdir(erlang:iodata()) -> { ok, Response }  |  
  %%   { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc   ask the rrdtool unix process to create a directory
  mkdir      (Arg)  when is_list(Arg) -> do(mkdir,      Arg).
  
  %% @spec ls() -> { ok, Response }  | { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc  return a directory listing from the rrdtool unix process'
  %%       current working directory
  ls         ()     -> do(ls,         []  ).
  
  %% @spec pwd() -> { ok, Response }  |  { error, Reason } 
  %%  Reason = iolist()
  %%  Response = iolist()
  %% @doc  return the rrdtool unix process'
  %%       current working directory.
  pwd        ()     -> do(pwd,        []  ).
  
  %% @equiv stop()
  quit() -> stop().
  
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %% Gen server interface poo
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  %% @hidden
  start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  
  %% @hidden
  init(_Args) -> 
    % TODO choose mechanism to allow user to set rrdtool
    ExtProg = "rrdtool -",
    process_flag(trap_exit, true),
    Port = erlang:open_port({spawn, ExtProg}, [ {line, 10000}, eof, exit_status, stream ] ),
    {ok, #state{port = Port}}.
  
  %%
  %% handle_call
  %% @hidden
  handle_call({do, Action, Args }, _From, #state{port = Port} = State) ->
      Line = [ erlang:atom_to_list(Action), " ", Args , "\n"],
      io:format("command to send: ~p\n", [ Line ] ),
      port_command(Port, Line),
      case collect_response(Port) of
          {response, Response} -> 
              {reply, { ok, Response }, State};
          { error, timeout } ->
              {stop, port_timeout, State};
          { error, Error } -> 
              {reply, { error, Error  }, State}
      end;
  
  handle_call(stop, _From, State) -> 
    {stop, normal, stopped, State}.
  
  %% handle_cast
  %% @hidden
  handle_cast(_Msg, State) -> {noreply, State}.
  
  %% handle_info
  %% @hidden
  handle_info(_Info, State) -> {noreply, State}.
  
  %% terminate
  %% @hidden
  terminate(_Reason, _State) -> ok.
  
  %% code_change
  %% @hidden
  code_change(_OldVsn, State, _Extra) -> 
    {ok, State}.
  
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % Private poo
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  do(Command, Args) -> 
    case has_newline(Args) of
      true  -> { error, "No newlines" };
      false -> gen_server:call (?MODULE, { do, Command, Args } ) 
    end.
  
  join([Head | [] ], _Sep) ->
    [Head];
  join([Head | Tail], Sep) ->
    [ Head, Sep | join(Tail, Sep) ].
  
  has_newline([]) -> false;
  has_newline(<<>>) -> false;
  