anchor.diff:  
   -record( state, { port }  ).
   
  -start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  +%-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).
   
  -
  -c(Args) -> combine(Args).
  +%% @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(B) when is_binary (B) -> 
  -%   has_newline_binary(B);
  -% has_newline([ H |  T]) 
  -%   when is_list(H) ->
  -%     case has_newline(H) of
  -%       true -> true;
  -%       false -> has_newline(T)
  -%     end;
  -% has_newline([ H | _T]) 
  -%   when is_integer(H), H =:= $\n -> true;
  -% has_newline([ H | _T]) 
  -%   when is_binary(H) -> 
  -%     has_newline_binary(H);
  -% has_newline([_H |  T]) ->
  -%   has_newline(T).
  -% 
  -% has_newline_binary(<<>>) -> false;
  -% has_newline_binary(<<H:8,_T/binary>> = B)
  -%   when is_binary(B), H =:= $\n -> true;
  -% has_newline_binary(<<_H:8,T/binary>>) ->
  -%   has_newline(T).
   
   has_newline([]) -> false;
   has_newline(<<>>) -> false;
  
positive.code:     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, [], []).
  
  
negative.code: -export([stop/0]).
  -export([combine/1, c/1]).
  
  
  -behaviour(gen_server).
  -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
  