anchor.diff:  to_list(quit, _, _Cmd) ->
       "QUIT";
   
  +to_list(join, Args, _cmd) ->
  +    Chans = proplists:get_value(channels, Args, []),
  +    Cs = case lists:partition(fun is_tuple/1, Chans) of
  +             {[], NonKeyed} ->
  +                 irc_parser:join(",", NonKeyed);
  +             {Keyed, NonKeyed} ->
  +                 irc_parser:join(",", [C || {C,_} <- Keyed] ++ NonKeyed) ++ " " ++
  +                     irc_parser:join(",", [K || {_C, K} <- Keyed])
  +         end,
  +    lists:flatten(["JOIN ", Cs]);
  +
   to_list(user, Args, _Cmd) ->
       Name = proplists:get_value(user_name, Args),
       Mode = proplists:get_value(mode, Args, "+w"),
  
positive.code: parse_line_8_test() ->
      ?assertMatch(X when is_record(X, cmd),
                   parse_line("AC N shinsterw 1 1167197569 sian leibniz.catalyst.net.nz DKTvAH ACAE[ :shinster\r\n")).
  
negative.code:     case string:tokens(Info, " ") of
          [Target, Command] ->
              Cmd#cmd{name=error,
                      target=Target,
                      args=[{code, Code},
                            {command, Command},
                            {text, Text}]}
      end.
  
  
  %%--------------------------------------------------------------------
  parse_notice(Cmd, [Target, Text]) ->
      Cmd#cmd{args = [{text, Text}], target=Target};
  parse_notice(Cmd, [Target | Text]) ->
      Cmd#cmd{target = string:strip(Target, right, $\s),
              args = [{text, irc_parser:join($:, Text)}]}.
  
  %%--------------------------------------------------------------------
  parse_server(Cmd, Args) when is_list(Args) ->
      parse_server(Cmd, irc_parser:split($:, Args));
  parse_server(Cmd, {Args,Description}) ->
  