anchor.diff:  
   -define(PERCENT, 37).  % $\%
   -define(FULLSTOP, 46). % $\.
  +-define(LBRACE, 91). % $\[.
  +-define(RBRACE, 93). % $\].
   -define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse
                       (C >= $a andalso C =< $f) orelse
                       (C >= $A andalso C =< $F))).
  
positive.code: %%      path_split("foo/bar") = {"foo", "bar"},
  %%      path_split("/foo/bar") = {"", "foo/bar"}.
  path_split(S) ->
      path_split(S, []).
  
  path_split("", Acc) ->
      {lists:reverse(Acc), ""};
  path_split("/" ++ Rest, Acc) ->
      {lists:reverse(Acc), Rest};
  path_split([C | Rest], 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})]).
  
  %% @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]).
  
  %% @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("", Acc) ->
      {lists:reverse(Acc), "", ""};
  urlsplit_path("?" ++ Rest, Acc) ->
      {Query, Fragment} = urlsplit_query(Rest),
      {lists:reverse(Acc), Query, Fragment};
  urlsplit_path("#" ++ Rest, Acc) ->
      {lists:reverse(Acc), "", Rest};
  urlsplit_path([C | Rest], Acc) ->
      urlsplit_path(Rest, [C | Acc]).
  
  urlsplit_query(Query) ->
      urlsplit_query(Query, []).
  
  urlsplit_query("", Acc) ->
      {lists:reverse(Acc), ""};
  urlsplit_query("#" ++ Rest, Acc) ->
      {lists:reverse(Acc), Rest};
  urlsplit_query([C | Rest], 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.
  
  %% @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)}.
  
  unquote_header("\"" ++ Rest) ->
      unquote_header(Rest, []);
  unquote_header(S) ->
      S.
  
  unquote_header("", Acc) ->
      lists:reverse(Acc);
  unquote_header("\"", Acc) ->
      lists:reverse(Acc);
  unquote_header([$\\, C | Rest], Acc) ->
      unquote_header(Rest, [C | Acc]);
  unquote_header([C | Rest], 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').
  
  %% @spec record_to_proplist(Record, Fields, TypeKey) -> proplist()
  %% @doc Return a proplist of the given Record with each field in the
  
negative.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_complex_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"),
    [{"foo", [{"bar", "1"}, {"baz", "2"}]}] =
      parse_qs("foo[bar]=1&foo[baz]=2"),
    ok.
  
  test_complex_parse_qs() ->
    [{"foo", [{"bar", "1"}, {"baz", "2"}]}] =
      parse_qs("foo[bar]=1&foo[baz]=2"),
    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.
  