anchor.diff:  %%      path_split("foo/bar") = {"foo", "bar"},
   %%      path_split("/foo/bar") = {"", "foo/bar"}.
   path_split(S) ->
  -    path_split(S, []).
  +  path_split(S, []).
   
   path_split("", Acc) ->
  -    {lists:reverse(Acc), ""};
  +  {lists:reverse(Acc), ""};
   path_split("/" ++ Rest, Acc) ->
  -    {lists:reverse(Acc), Rest};
  +  {lists:reverse(Acc), Rest};
   path_split([C | Rest], Acc) ->
  -    path_split(Rest, [C | Acc]).
  +  path_split(Rest, [C | Acc]).
   
   
   %% @spec urlunsplit({Scheme, Netloc, Path, Query, Fragment}) -> string()
   %% @doc Assemble a URL from the 5-tuple. Path must be absolute.
   urlunsplit({Scheme, Netloc, Path, Query, Fragment}) ->
  -    lists:flatten([case Scheme of "" -> "";  _ -> [Scheme, "://"] end,
  -                   Netloc,
  -                   urlunsplit_path({Path, Query, Fragment})]).
  +  lists:flatten([case Scheme of "" -> "";  _ -> [Scheme, "://"] end,
  +                 Netloc,
  +                 urlunsplit_path({Path, Query, Fragment})]).
   
   %% @spec urlunsplit_path({Path, Query, Fragment}) -> string()
   %% @doc Assemble a URL path from the 3-tuple.
   urlunsplit_path({Path, Query, Fragment}) ->
  -    lists:flatten([Path,
  -                   case Query of "" -> ""; _ -> [$? | Query] end,
  -                   case Fragment of "" -> ""; _ -> [$# | Fragment] end]).
  +  lists:flatten([Path,
  +                 case Query of "" -> ""; _ -> [$? | Query] end,
  +                 case Fragment of "" -> ""; _ -> [$# | Fragment] end]).
   
   %% @spec urlsplit_path(Url) -> {Path, Query, Fragment}
   %% @doc Return a 3-tuple, does not expand % escapes. Only supports HTTP style
   %%      paths.
   urlsplit_path(Path) ->
  -    urlsplit_path(Path, []).
  +  urlsplit_path(Path, []).
   
   urlsplit_path("", Acc) ->
  -    {lists:reverse(Acc), "", ""};
  +  {lists:reverse(Acc), "", ""};
   urlsplit_path("?" ++ Rest, Acc) ->
  -    {Query, Fragment} = urlsplit_query(Rest),
  -    {lists:reverse(Acc), Query, Fragment};
  +  {Query, Fragment} = urlsplit_query(Rest),
  +  {lists:reverse(Acc), Query, Fragment};
   urlsplit_path("#" ++ Rest, Acc) ->
  -    {lists:reverse(Acc), "", Rest};
  +  {lists:reverse(Acc), "", Rest};
   urlsplit_path([C | Rest], Acc) ->
  -    urlsplit_path(Rest, [C | Acc]).
  +  urlsplit_path(Rest, [C | Acc]).
   
   urlsplit_query(Query) ->
  -    urlsplit_query(Query, []).
  +  urlsplit_query(Query, []).
   
   urlsplit_query("", Acc) ->
  -    {lists:reverse(Acc), ""};
  +  {lists:reverse(Acc), ""};
   urlsplit_query("#" ++ Rest, Acc) ->
  -    {lists:reverse(Acc), Rest};
  +  {lists:reverse(Acc), Rest};
   urlsplit_query([C | Rest], Acc) ->
  -    urlsplit_query(Rest, [C | Acc]).
  +  urlsplit_query(Rest, [C | Acc]).
   
   %% @spec guess_mime(string()) -> string()
   %% @doc  Guess the mime type of a file by the extension of its filename.
   guess_mime(File) ->
  -    case filename:extension(File) of
  -        ".html" ->
  -            "text/html";
  -        ".xhtml" ->
  -            "application/xhtml+xml";
  -        ".xml" ->
  -            "application/xml";
  -        ".css" ->
  -            "text/css";
  -        ".js" ->
  -            "application/x-javascript";
  -        ".jpg" ->
  -            "image/jpeg";
  -        ".gif" ->
  -            "image/gif";
  -        ".png" ->
  -            "image/png";
  -        ".swf" ->
  -            "application/x-shockwave-flash";
  -        ".zip" ->
  -            "application/zip";
  -        ".bz2" ->
  -            "application/x-bzip2";
  -        ".gz" ->
  -            "application/x-gzip";
  -        ".tar" ->
  -            "application/x-tar";
  -        ".tgz" ->
  -            "application/x-gzip";
  -        ".txt" ->
  -            "text/plain";
  -        ".doc" ->
  -            "application/msword";
  -        ".pdf" ->
  -            "application/pdf";
  -        ".xls" ->
  -            "application/vnd.ms-excel";
  -        ".rtf" ->
  -            "application/rtf";
  -        ".mov" ->
  -            "video/quicktime";
  -        ".mp3" ->
  -            "audio/mpeg";
  -        ".z" ->
  -            "application/x-compress";
  -        ".wav" ->
  -            "audio/x-wav";
  -        ".ico" ->
  -            "image/x-icon";
  -        ".bmp" ->
  -            "image/bmp";
  -        ".m4a" ->
  -            "audio/mpeg";
  -        ".m3u" ->
  -            "audio/x-mpegurl";
  -        ".exe" ->
  -            "application/octet-stream";
  -        ".csv" ->
  -            "text/csv";
  -        _ ->
  -            "text/plain"
  -    end.
  +  case filename:extension(File) of
  +    ".html" ->
  +      "text/html";
  +    ".xhtml" ->
  +      "application/xhtml+xml";
  +    ".xml" ->
  +      "application/xml";
  +    ".css" ->
  +      "text/css";
  +    ".js" ->
  +      "application/x-javascript";
  +    ".jpg" ->
  +      "image/jpeg";
  +    ".gif" ->
  +      "image/gif";
  +    ".png" ->
  +      "image/png";
  +    ".swf" ->
  +      "application/x-shockwave-flash";
  +    ".zip" ->
  +      "application/zip";
  +    ".bz2" ->
  +      "application/x-bzip2";
  +    ".gz" ->
  +      "application/x-gzip";
  +    ".tar" ->
  +      "application/x-tar";
  +    ".tgz" ->
  +      "application/x-gzip";
  +    ".txt" ->
  +      "text/plain";
  +    ".doc" ->
  +      "application/msword";
  +    ".pdf" ->
  +      "application/pdf";
  +    ".xls" ->
  +      "application/vnd.ms-excel";
  +    ".rtf" ->
  +      "application/rtf";
  +    ".mov" ->
  +      "video/quicktime";
  +    ".mp3" ->
  +      "audio/mpeg";
  +    ".z" ->
  +      "application/x-compress";
  +    ".wav" ->
  +      "audio/x-wav";
  +    ".ico" ->
  +      "image/x-icon";
  +    ".bmp" ->
  +      "image/bmp";
  +    ".m4a" ->
  +      "audio/mpeg";
  +    ".m3u" ->
  +      "audio/x-mpegurl";
  +    ".exe" ->
  +      "application/octet-stream";
  +    ".csv" ->
  +      "text/csv";
  +    _ ->
  +      "text/plain"
  +  end.
   
   %% @spec parse_header(string()) -> {Type, [{K, V}]}
   %% @doc  Parse a Content-Type like header, return the main Content-Type
   %%       and a property list of options.
   parse_header(String) ->
  -    %% TODO: This is exactly as broken as Python's cgi module.
  -    %%       Should parse properly like mochiweb_cookies.
  -    [Type | Parts] = [string:strip(S) || S <- string:tokens(String, ";")],
  -    F = fun (S, Acc) ->
  -                case lists:splitwith(fun (C) -> C =/= $= end, S) of
  -                    {"", _} ->
  -                        %% Skip anything with no name
  -                        Acc;
  -                    {_, ""} ->
  -                        %% Skip anything with no value
  -                        Acc;
  -                    {Name, [$\= | Value]} ->
  -                        [{string:to_lower(string:strip(Name)),
  -                          unquote_header(string:strip(Value))} | Acc]
  -                end
  -        end,
  -    {string:to_lower(Type),
  -     lists:foldr(F, [], Parts)}.
  +%% TODO: This is exactly as broken as Python's cgi module.
  +%%       Should parse properly like mochiweb_cookies.
  +  [Type | Parts] = [string:strip(S) || S <- string:tokens(String, ";")],
  +  F = fun (S, Acc) ->
  +          case lists:splitwith(fun (C) -> C =/= $= end, S) of
  +            {"", _} ->
  +%% Skip anything with no name
  +              Acc;
  +            {_, ""} ->
  +%% Skip anything with no value
  +              Acc;
  +            {Name, [$\= | Value]} ->
  +              [{string:to_lower(string:strip(Name)),
  +                unquote_header(string:strip(Value))} | Acc]
  +          end
  +      end,
  +  {string:to_lower(Type),
  +   lists:foldr(F, [], Parts)}.
   
   unquote_header("\"" ++ Rest) ->
  -    unquote_header(Rest, []);
  +  unquote_header(Rest, []);
   unquote_header(S) ->
  -    S.
  +  S.
   
   unquote_header("", Acc) ->
  -    lists:reverse(Acc);
  +  lists:reverse(Acc);
   unquote_header("\"", Acc) ->
  -    lists:reverse(Acc);
  +  lists:reverse(Acc);
   unquote_header([$\\, C | Rest], Acc) ->
  -    unquote_header(Rest, [C | Acc]);
  +  unquote_header(Rest, [C | Acc]);
   unquote_header([C | Rest], Acc) ->
  -    unquote_header(Rest, [C | Acc]).
  +  unquote_header(Rest, [C | Acc]).
   
   %% @spec record_to_proplist(Record, Fields) -> proplist()
   %% @doc calls record_to_proplist/3 with a default TypeKey of '__record'
   record_to_proplist(Record, Fields) ->
  -    record_to_proplist(Record, Fields, '__record').
  +  record_to_proplist(Record, Fields, '__record').
   
   %% @spec record_to_proplist(Record, Fields, TypeKey) -> proplist()
   %% @doc Return a proplist of the given Record with each field in the
  
positive.code: %%      where record_type is the record type of Record
  record_to_proplist(Record, Fields, TypeKey)
    when is_tuple(Record),
         is_list(Fields),
         size(Record) - 1 =:= length(Fields) ->
      lists:zip([TypeKey | Fields], tuple_to_list(Record)).
  
  
  shell_quote([], Acc) ->
      lists:reverse([$\" | Acc]);
  shell_quote([C | Rest], Acc) when C =:= $\" orelse C =:= $\` orelse
                                    C =:= $\\ orelse C =:= $\$ ->
      shell_quote(Rest, [C, $\\ | Acc]);
  shell_quote([C | Rest], Acc) ->
      shell_quote(Rest, [C | Acc]).
  
  test() ->
      test_join(),
      test_quote_plus(),
      test_unquote(),
      test_urlencode(),
      test_parse_qs(),
      test_urlsplit_path(),
      test_urlunsplit_path(),
      test_urlsplit(),
      test_urlunsplit(),
      test_path_split(),
      test_guess_mime(),
      test_parse_header(),
      test_shell_quote(),
      test_cmd(),
      test_cmd_string(),
      test_partition(),
      test_safe_relative_path(),
      ok.
  
  test_shell_quote() ->
      "\"foo \\$bar\\\"\\`' baz\"" = shell_quote("foo $bar\"`' baz"),
      ok.
  
  test_cmd() ->
      "$bling$ `word`!\n" = cmd(["echo", "$bling$ `word`!"]),
      ok.
  
  test_cmd_string() ->
      "\"echo\" \"\\$bling\\$ \\`word\\`!\"" = cmd_string(["echo", "$bling$ `word`!"]),
      ok.
  
  test_parse_header() ->
      {"multipart/form-data", [{"boundary", "AaB03x"}]} =
          parse_header("multipart/form-data; boundary=AaB03x"),
      ok.
  
  test_guess_mime() ->
      "text/plain" = guess_mime(""),
      "text/plain" = guess_mime(".text"),
      "application/zip" = guess_mime(".zip"),
      "application/zip" = guess_mime("x.zip"),
      "text/html" = guess_mime("x.html"),
      "application/xhtml+xml" = guess_mime("x.xhtml"),
      ok.
  
  test_path_split() ->
      {"", "foo/bar"} = path_split("/foo/bar"),
      {"foo", "bar"} = path_split("foo/bar"),
      {"bar", ""} = path_split("bar"),
      ok.
  
  test_urlsplit() ->
      {"", "", "/foo", "", "bar?baz"} = urlsplit("/foo#bar?baz"),
      {"http", "host:port", "/foo", "", "bar?baz"} =
          urlsplit("http://host:port/foo#bar?baz"),
      ok.
  
  test_urlsplit_path() ->
      {"/foo/bar", "", ""} = urlsplit_path("/foo/bar"),
      {"/foo", "baz", ""} = urlsplit_path("/foo?baz"),
      {"/foo", "", "bar?baz"} = urlsplit_path("/foo#bar?baz"),
      {"/foo", "", "bar?baz#wibble"} = urlsplit_path("/foo#bar?baz#wibble"),
      {"/foo", "bar", "baz"} = urlsplit_path("/foo?bar#baz"),
      {"/foo", "bar?baz", "baz"} = urlsplit_path("/foo?bar?baz#baz"),
      ok.
  
  test_urlunsplit() ->
      "/foo#bar?baz" = urlunsplit({"", "", "/foo", "", "bar?baz"}),
      "http://host:port/foo#bar?baz" =
          urlunsplit({"http", "host:port", "/foo", "", "bar?baz"}),
      ok.
  
  test_urlunsplit_path() ->
      "/foo/bar" = urlunsplit_path({"/foo/bar", "", ""}),
      "/foo?baz" = urlunsplit_path({"/foo", "baz", ""}),
      "/foo#bar?baz" = urlunsplit_path({"/foo", "", "bar?baz"}),
      "/foo#bar?baz#wibble" = urlunsplit_path({"/foo", "", "bar?baz#wibble"}),
      "/foo?bar#baz" = urlunsplit_path({"/foo", "bar", "baz"}),
      "/foo?bar?baz#baz" = urlunsplit_path({"/foo", "bar?baz", "baz"}),
      ok.
  
  test_join() ->
      "foo,bar,baz" = join(["foo", "bar", "baz"], $,),
      "foo,bar,baz" = join(["foo", "bar", "baz"], ","),
      "foo bar" = join([["foo", " bar"]], ","),
      "foo bar,baz" = join([["foo", " bar"], "baz"], ","),
      "foo" = join(["foo"], ","),
      "foobarbaz" = join(["foo", "bar", "baz"], ""),
      ok.
  
  test_quote_plus() ->
      "foo" = quote_plus(foo),
      "1" = quote_plus(1),
      "1.1" = quote_plus(1.1),
      "foo" = quote_plus("foo"),
      "foo+bar" = quote_plus("foo bar"),
      "foo%0A" = quote_plus("foo\n"),
      "foo%0A" = quote_plus("foo\n"),
      "foo%3B%26%3D" = quote_plus("foo;&="),
      ok.
  
  test_unquote() ->
      "foo bar" = unquote("foo+bar"),
      "foo bar" = unquote("foo%20bar"),
      "foo\r\n" = unquote("foo%0D%0A"),
      ok.
  
  test_urlencode() ->
      "foo=bar&baz=wibble+%0D%0A&z=1" = urlencode([{foo, "bar"},
                                                   {"baz", "wibble \r\n"},
                                                   {z, 1}]),
      ok.
  
  test_parse_qs() ->
      [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}] =
          parse_qs("foo=bar&baz=wibble+%0D%0A&z=1"),
      ok.
  
  test_partition() ->
      {"foo", "", ""} = partition("foo", "/"),
      {"foo", "/", "bar"} = partition("foo/bar", "/"),
      {"foo", "/", ""} = partition("foo/", "/"),
      {"", "/", "bar"} = partition("/bar", "/"),
      {"f", "oo/ba", "r"} = partition("foo/bar", "oo/ba"),
      ok.
  
  test_safe_relative_path() ->
      "foo" = safe_relative_path("foo"),
      "foo/" = safe_relative_path("foo/"),
      "foo" = safe_relative_path("foo/bar/.."),
      "bar" = safe_relative_path("foo/../bar"),
      "bar/" = safe_relative_path("foo/../bar/"),
      "" = safe_relative_path("foo/.."),
      "" = safe_relative_path("foo/../"),
      undefined = safe_relative_path("/foo"),
      undefined = safe_relative_path("../foo"),
      undefined = safe_relative_path("foo/../.."),
      undefined = safe_relative_path("foo//"),
      ok.
  
negative.code: %%      partition("foo/bar", "/") = {"foo", "/", "bar"},
  %%      partition("foo", "/") = {"foo", "", ""}.
  partition(String, Sep) ->
    case partition(String, Sep, []) of
      undefined ->
        {String, "", ""};
      Result ->
        Result
    end.
  
  partition("", _Sep, _Acc) ->
    undefined;
  partition(S, Sep, Acc) ->
    case partition2(S, Sep) of
      undefined ->
        [C | Rest] = S,
        partition(Rest, Sep, [C | Acc]);
      Rest ->
        {lists:reverse(Acc), Sep, Rest}
    end.
  
  partition2(Rest, "") ->
    Rest;
  partition2([C | R1], [C | R2]) ->
    partition2(R1, R2);
  partition2(_S, _Sep) ->
    undefined.
  
  
  
  