diff options
Diffstat (limited to 'lib')
47 files changed, 3103 insertions, 451 deletions
diff --git a/lib/asn1/src/asn1rt_driver_handler.erl b/lib/asn1/src/asn1rt_driver_handler.erl index c95b243ae0..cc2b501e16 100644 --- a/lib/asn1/src/asn1rt_driver_handler.erl +++ b/lib/asn1/src/asn1rt_driver_handler.erl @@ -71,7 +71,10 @@ load_driver(Reason) -> end. init(FromPid,FromRef) -> - register(asn1_driver_owner,self()), + case catch register(asn1_driver_owner,self()) of + true -> true; + _Other -> exit(normal) + end, Dir = filename:join([code:priv_dir(asn1),"lib"]), case catch erl_ddll:load_driver(Dir,asn1_erl_drv) of ok -> diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 7bfb9ffb49..5e9792f02c 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -43,6 +43,14 @@ init_per_suite(Config) -> init_per_suite(Config, 50). init_per_suite(Config, Level) -> + case os:type() of + {win32, _} -> + %% Extend timeout for windows as starting node + %% can take a long time there + test_server:timetrap( 120000 * test_server:timetrap_scale_factor()); + _ -> + ok + end, case delete_old_logs(os:type(), Config) of {'EXIT',DelLogsReason} -> test_server:format(0, "Failed to delete old log directories: ~p~n", @@ -51,6 +59,8 @@ init_per_suite(Config, Level) -> ok end, [_,Host] = string:tokens(atom_to_list(node()), "@"), + + test_server:format(0, "Trying to start ~s~n", ["ct@"++Host]), case slave:start(Host, ct, []) of {error,Reason} -> test_server:fail(Reason); @@ -351,13 +361,33 @@ locate({parallel,TEvs}, Node, Evs, Config) -> case Evs of [{TEH,#event{name=tc_start, node=Node, - data={M,{init_per_group,GroupName,Props}}}}, - {TEH,#event{name=tc_done, - node=Node, - data={M,{init_per_group,GroupName,Props},R}}} | Es] -> + data={M,{init_per_group, + GroupName,Props}}}}|Es] -> + %% Use dropwhile here as a tc_done from a + %% previous testcase might sneak in here + EvsG = lists:dropwhile( + fun({EH,#event{name=tc_done, + node=EvNode, + data={EvM,{init_per_group, + EvGroupName, + EvProps},EvR}}}) + when TEH == EH, EvNode == Node, EvM == M, + EvGroupName == GroupName, + EvProps == Props, + EvR == R -> + false; + ({EH,#event{name=stop_logging, + node=EvNode,data=_}}) + when EH == TEH, EvNode == Node -> + exit({group_init_done_not_found, + GroupName,Props}); + (_) -> + true + end, Es), + test_server:format("Found ~p!", [InitStart]), test_server:format("Found ~p!", [InitDone]), - {TEs,Es}; + {TEs,EvsG}; _ -> nomatch end; diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 761d4ffec0..45cdf8a659 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -407,16 +407,23 @@ check_liveness(R, [{bif,Op,{f,Fail},Ss,D}|Is], St0) -> Other -> Other end; -check_liveness(R, [{gc_bif,Op,{f,Fail},_,Ss,D}|Is], St0) -> - case check_liveness_fail(R, Op, Ss, Fail, St0) of - {killed,St} = Killed -> - case member(R, Ss) of - true -> {used,St}; - false when R =:= D -> Killed; - false -> check_liveness(R, Is, St) - end; - Other -> - Other +check_liveness(R, [{gc_bif,Op,{f,Fail},Live,Ss,D}|Is], St0) -> + case R of + {x,X} when X >= Live -> + {killed,St0}; + {x,_} -> + {used,St0}; + _ -> + case check_liveness_fail(R, Op, Ss, Fail, St0) of + {killed,St}=Killed -> + case member(R, Ss) of + true -> {used,St}; + false when R =:= D -> Killed; + false -> check_liveness(R, Is, St) + end; + Other -> + Other + end end; check_liveness(R, [{bs_add,{f,0},Ss,D}|Is], St) -> case member(R, Ss) of @@ -482,10 +489,13 @@ check_liveness(R, [{bs_context_to_binary,S}|Is], St) -> S -> {used,St}; _ -> check_liveness(R, Is, St) end; -check_liveness(R, [{loop_rec,{f,_},{x,0}}|Is], St) -> +check_liveness(R, [{loop_rec,{f,_},{x,0}}|_], St) -> case R of - {x,_} -> {killed,St}; - _ -> check_liveness(R, Is, St) + {x,_} -> + {killed,St}; + _ -> + %% y register. Rarely happens. Be very conversative. + {unknown,St} end; check_liveness(R, [{loop_rec_end,{f,Fail}}|_], St) -> check_liveness_at(R, Fail, St); diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl index 9c06740816..935e384d2d 100644 --- a/lib/compiler/test/compilation_SUITE.erl +++ b/lib/compiler/test/compilation_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -46,7 +46,7 @@ all(suite) -> trycatch_4, opt_crash, otp_5404,otp_5436,otp_5481,otp_5553,otp_5632, otp_5714,otp_5872,otp_6121,otp_6121a,otp_6121b, - otp_7202,otp_7345,on_load,string_table + otp_7202,otp_7345,on_load,string_table,otp_8949_a,otp_8949_a ]. -define(comp(N), @@ -606,5 +606,45 @@ string_table(Config) when is_list(Config) -> ?line {"StrT", <<"stringabletringtable">>} = StringTableChunk, ok. +otp_8949_a(Config) when is_list(Config) -> + value = otp_8949_a(), + ok. + +-record(cs, {exs,keys = [],flags = 1}). +-record(exs, {children = []}). + +otp_8949_a() -> + case id([#cs{}]) of + [#cs{}=Cs] -> + SomeVar = id(value), + if + Cs#cs.flags band 1 =/= 0 -> + id(SomeVar); + (((Cs#cs.exs)#exs.children /= []) + and + (Cs#cs.flags band (1 bsl 0 bor (1 bsl 22)) == 0)); + Cs#cs.flags band (1 bsl 22) =/= 0 -> + ok + end + end. + +otp_8949_b(Config) when is_list(Config) -> + self() ! something, + ?line value = otp_8949_b([], false), + ?line {'EXIT',_} = (catch otp_8949_b([], true)), + ok. + +%% Would cause an endless loop in beam_utils. +otp_8949_b(A, B) -> + Var = id(value), + if + A == [], B == false -> + ok + end, + receive + something -> + id(Var) + end. + id(I) -> I. diff --git a/lib/dialyzer/doc/manual.txt b/lib/dialyzer/doc/manual.txt index 470ddd6c73..d9cb52f722 100644 --- a/lib/dialyzer/doc/manual.txt +++ b/lib/dialyzer/doc/manual.txt @@ -123,9 +123,10 @@ The exit status of the command line version is: Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose] - [-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]* - [--output_plt file] [-Wwarn]* [--src] [--gui | --wx] - [files_or_dirs] [-r dirs] [--apps applications] [-o outfile] + [-pa dir]* [--plt plt] [--plts plts] [-Ddefine]* + [-I include_dir]* [--output_plt file] [-Wwarn]* + [--src] [--gui | --wx] [files_or_dirs] [-r dirs] + [--apps applications] [-o outfile] [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] [--no_native] @@ -167,6 +168,10 @@ Options: --plt plt Use the specified plt as the initial plt (if the plt was built during setup the files will be checked for consistency) + --plts plts + Merges the specified plts to create the initial plt -- requires + that the plts are disjoint (i.e., do not have any module + appearing in more than one plt) -Wwarn A family of options which selectively turn on/off warnings (for help on the names of warnings use dialyzer -Whelp) @@ -294,6 +299,7 @@ Option :: {files, [Filename :: string()]} | {defines, [{Macro :: atom(), Value :: term()}]} | {from, src_code | byte_code} %% Defaults to byte_code | {init_plt, FileName :: string()} %% If changed from default + | {plts, [FileName :: string()]} %% If changed from default | {include_dirs, [DirName :: string()]} | {output_file, FileName :: string()} | {output_plt, FileName :: string()} diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl index b4161ea194..471f9fccd2 100644 --- a/lib/dialyzer/src/dialyzer.erl +++ b/lib/dialyzer/src/dialyzer.erl @@ -106,27 +106,35 @@ cl_print_plt_info(Opts) -> end, doit(F). -print_plt_info(#options{init_plt = PLT, output_file = OutputFile}) -> +print_plt_info(#options{init_plts = PLTs, output_file = OutputFile}) -> + PLTInfo = get_plt_info(PLTs), + do_print_plt_info(PLTInfo, OutputFile). + +get_plt_info([PLT|PLTs]) -> String = case dialyzer_plt:included_files(PLT) of {ok, Files} -> - io_lib:format("The PLT ~s includes the following files:\n~p\n", + io_lib:format("The PLT ~s includes the following files:\n~p\n\n", [PLT, Files]); {error, read_error} -> - Msg = io_lib:format("Could not read the PLT file ~p\n", [PLT]), + Msg = io_lib:format("Could not read the PLT file ~p\n\n", [PLT]), throw({dialyzer_error, Msg}); {error, no_such_file} -> - Msg = io_lib:format("The PLT file ~p does not exist\n", [PLT]), + Msg = io_lib:format("The PLT file ~p does not exist\n\n", [PLT]), throw({dialyzer_error, Msg}) end, + String ++ get_plt_info(PLTs); +get_plt_info([]) -> "". + +do_print_plt_info(PLTInfo, OutputFile) -> case OutputFile =:= none of true -> - io:format("~s", [String]), + io:format("~s", [PLTInfo]), ?RET_NOTHING_SUSPICIOUS; false -> case file:open(OutputFile, [write]) of {ok, FileDesc} -> - io:format(FileDesc, "~s", [String]), + io:format(FileDesc, "~s", [PLTInfo]), ok = file:close(FileDesc), ?RET_NOTHING_SUSPICIOUS; {error, Reason} -> diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index 2da8ed2e5d..1d98574585 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -129,7 +129,7 @@ defines = [] :: [dial_define()], from = byte_code :: start_from(), get_warnings = maybe :: boolean() | 'maybe', - init_plt = none :: 'none' | file:filename(), + init_plts = [] :: [file:filename()], include_dirs = [] :: [file:filename()], output_plt = none :: 'none' | file:filename(), legal_warnings = ordsets:new() :: ordset(dial_warn_tag()), diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index 0250c47ad0..1987c1732c 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -81,11 +81,15 @@ build_plt(Opts) -> init_opts_for_build(Opts) -> case Opts#options.output_plt =:= none of true -> - case Opts#options.init_plt of - none -> Opts#options{init_plt = none, output_plt = get_default_plt()}; - Plt -> Opts#options{init_plt = none, output_plt = Plt} + case Opts#options.init_plts of + [] -> Opts#options{output_plt = get_default_output_plt()}; + [Plt] -> Opts#options{init_plts = [], output_plt = Plt}; + Plts -> + Msg = io_lib:format("Could not build multiple PLT files: ~s\n", + [format_plts(Plts)]), + error(Msg) end; - false -> Opts#options{init_plt = none} + false -> Opts#options{init_plts = []} end. %%-------------------------------------------------------------------- @@ -98,39 +102,58 @@ add_to_plt(Opts) -> init_opts_for_add(Opts) -> case Opts#options.output_plt =:= none of true -> - case Opts#options.init_plt of - none -> Opts#options{output_plt = get_default_plt(), - init_plt = get_default_plt()}; - Plt -> Opts#options{output_plt = Plt} + case Opts#options.init_plts of + [] -> Opts#options{output_plt = get_default_output_plt(), + init_plts = get_default_init_plt()}; + [Plt] -> Opts#options{output_plt = Plt}; + Plts -> + Msg = io_lib:format("Could not add to multiple PLT files: ~s\n", + [format_plts(Plts)]), + error(Msg) end; false -> - case Opts#options.init_plt =:= none of - true -> Opts#options{init_plt = get_default_plt()}; + case Opts#options.init_plts =:= [] of + true -> Opts#options{init_plts = get_default_init_plt()}; false -> Opts end end. %%-------------------------------------------------------------------- -check_plt(Opts) -> +check_plt(#options{init_plts = []} = Opts) -> Opts1 = init_opts_for_check(Opts), - report_check(Opts), - plt_common(Opts1, [], []). + report_check(Opts1), + plt_common(Opts1, [], []); +check_plt(#options{init_plts = Plts} = Opts) -> + check_plt_aux(Plts, Opts). + +check_plt_aux([_] = Plt, Opts) -> + Opts1 = Opts#options{init_plts = Plt}, + Opts2 = init_opts_for_check(Opts1), + report_check(Opts2), + plt_common(Opts2, [], []); +check_plt_aux([Plt|Plts], Opts) -> + Opts1 = Opts#options{init_plts = [Plt]}, + Opts2 = init_opts_for_check(Opts1), + report_check(Opts2), + plt_common(Opts2, [], []), + check_plt_aux(Plts, Opts). init_opts_for_check(Opts) -> - Plt = - case Opts#options.init_plt of - none -> get_default_plt(); - Plt0 -> Plt0 + InitPlt = + case Opts#options.init_plts of + []-> get_default_init_plt(); + Plt -> Plt end, + [OutputPlt] = InitPlt, Opts#options{files = [], files_rec = [], analysis_type = plt_check, defines = [], from = byte_code, - init_plt = Plt, + init_plts = InitPlt, include_dirs = [], - output_plt = Plt, + output_plt = OutputPlt, use_contracts = true }. @@ -144,21 +167,25 @@ remove_from_plt(Opts) -> init_opts_for_remove(Opts) -> case Opts#options.output_plt =:= none of true -> - case Opts#options.init_plt of - none -> Opts#options{output_plt = get_default_plt(), - init_plt = get_default_plt()}; - Plt -> Opts#options{output_plt = Plt} + case Opts#options.init_plts of + [] -> Opts#options{output_plt = get_default_output_plt(), + init_plts = get_default_init_plt()}; + [Plt] -> Opts#options{output_plt = Plt}; + Plts -> + Msg = io_lib:format("Could not remove from multiple PLT files: ~s\n", + [format_plts(Plts)]), + error(Msg) end; false -> - case Opts#options.init_plt =:= none of - true -> Opts#options{init_plt = get_default_plt()}; + case Opts#options.init_plts =:= [] of + true -> Opts#options{init_plts = get_default_init_plt()}; false -> Opts end end. %%-------------------------------------------------------------------- -plt_common(Opts, RemoveFiles, AddFiles) -> +plt_common(#options{init_plts = [InitPlt]} = Opts, RemoveFiles, AddFiles) -> case check_plt(Opts, RemoveFiles, AddFiles) of ok -> case Opts#options.report_mode of @@ -174,7 +201,7 @@ plt_common(Opts, RemoveFiles, AddFiles) -> report_failed_plt_check(Opts, DiffMd5), {AnalFiles, RemovedMods, ModDeps1} = expand_dependent_modules(Md5, DiffMd5, ModDeps), - Plt = clean_plt(Opts#options.init_plt, RemovedMods), + Plt = clean_plt(InitPlt, RemovedMods), case AnalFiles =:= [] of true -> %% Only removed stuff. Just write the PLT. @@ -186,19 +213,19 @@ plt_common(Opts, RemoveFiles, AddFiles) -> end; {error, no_such_file} -> Msg = io_lib:format("Could not find the PLT: ~s\n~s", - [Opts#options.init_plt, default_plt_error_msg()]), + [InitPlt, default_plt_error_msg()]), error(Msg); {error, not_valid} -> Msg = io_lib:format("The file: ~s is not a valid PLT file\n~s", - [Opts#options.init_plt, default_plt_error_msg()]), + [InitPlt, default_plt_error_msg()]), error(Msg); {error, read_error} -> Msg = io_lib:format("Could not read the PLT: ~s\n~s", - [Opts#options.init_plt, default_plt_error_msg()]), + [InitPlt, default_plt_error_msg()]), error(Msg); {error, {no_file_to_remove, F}} -> Msg = io_lib:format("Could not remove the file ~s from the PLT: ~s\n", - [F, Opts#options.init_plt]), + [F, InitPlt]), error(Msg) end. @@ -218,8 +245,7 @@ default_plt_error_msg() -> %%-------------------------------------------------------------------- -check_plt(Opts, RemoveFiles, AddFiles) -> - Plt = Opts#options.init_plt, +check_plt(#options{init_plts = [Plt]} = Opts, RemoveFiles, AddFiles) -> case dialyzer_plt:check_plt(Plt, RemoveFiles, AddFiles) of {old_version, _MD5} = OldVersion -> report_old_version(Opts), @@ -234,14 +260,14 @@ check_plt(Opts, RemoveFiles, AddFiles) -> %%-------------------------------------------------------------------- -report_check(#options{report_mode = ReportMode, init_plt = InitPlt}) -> +report_check(#options{report_mode = ReportMode, init_plts = [InitPlt]}) -> case ReportMode of quiet -> ok; _ -> io:format(" Checking whether the PLT ~s is up-to-date...", [InitPlt]) end. -report_old_version(#options{report_mode = ReportMode, init_plt = InitPlt}) -> +report_old_version(#options{report_mode = ReportMode, init_plts = [InitPlt]}) -> case ReportMode of quiet -> ok; _ -> @@ -264,7 +290,7 @@ report_failed_plt_check(#options{analysis_type = AnalType, report_analysis_start(#options{analysis_type = Type, report_mode = ReportMode, - init_plt = InitPlt, + init_plts = InitPlts, output_plt = OutputPlt}) -> case ReportMode of quiet -> ok; @@ -272,6 +298,7 @@ report_analysis_start(#options{analysis_type = Type, io:format(" "), case Type of plt_add -> + [InitPlt] = InitPlts, case InitPlt =:= OutputPlt of true -> io:format("Adding information to ~s...", [OutputPlt]); false -> io:format("Adding information from ~s to ~s...", @@ -282,6 +309,7 @@ report_analysis_start(#options{analysis_type = Type, plt_check -> io:format("Rebuilding the information in ~s...", [OutputPlt]); plt_remove -> + [InitPlt] = InitPlts, case InitPlt =:= OutputPlt of true -> io:format("Removing information from ~s...", [OutputPlt]); false -> io:format("Removing information from ~s to ~s...", @@ -320,16 +348,28 @@ report_md5_diff(List) -> %%-------------------------------------------------------------------- -get_default_plt() -> +get_default_init_plt() -> + [dialyzer_plt:get_default_plt()]. + +get_default_output_plt() -> dialyzer_plt:get_default_plt(). %%-------------------------------------------------------------------- +format_plts([Plt]) -> Plt; +format_plts([Plt|Plts]) -> + Plt ++ ", " ++ format_plts(Plts). + +%%-------------------------------------------------------------------- + do_analysis(Options) -> Files = get_files_from_opts(Options), - case Options#options.init_plt of - none -> do_analysis(Files, Options, dialyzer_plt:new(), none); - File -> do_analysis(Files, Options, dialyzer_plt:from_file(File), none) + case Options#options.init_plts of + [] -> do_analysis(Files, Options, dialyzer_plt:new(), none); + PltFiles -> + Plts = [dialyzer_plt:from_file(F) || F <- PltFiles], + Plt = dialyzer_plt:merge_plts_or_report_conflicts(PltFiles, Plts), + do_analysis(Files, Options, Plt, none) end. do_analysis(Files, Options, Plt, PltInfo) -> diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index 0160b84abc..2f9e577544 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -138,11 +138,17 @@ cl(["-pa", Path|T]) -> true -> cl(T); {error, _} -> error("Bad directory for -pa: "++Path) end; -cl(["--plt", PLT|T]) -> - put(dialyzer_init_plt, PLT), - cl(T); cl(["--plt"]) -> error("No plt specified for --plt"); +cl(["--plt", PLT|T]) -> + put(dialyzer_init_plts, [PLT]), + cl(T); +cl(["--plts"]) -> + error("No plts specified for --plts"); +cl(["--plts"|T]) -> + {PLTs, NewT} = get_plts(T, []), + put(dialyzer_init_plts, PLTs), + cl(NewT); cl(["-q"|T]) -> put(dialyzer_options_report_mode, quiet), cl(T); @@ -284,7 +290,7 @@ common_options() -> [{defines, get(dialyzer_options_defines)}, {from, get(dialyzer_options_from)}, {include_dirs, get(dialyzer_include)}, - {init_plt, get(dialyzer_init_plt)}, + {plts, get(dialyzer_init_plts)}, {output_plt, get(dialyzer_output_plt)}, {report_mode, get(dialyzer_options_report_mode)}, {use_spec, get(dialyzer_options_use_contracts)}, @@ -309,6 +315,13 @@ get_lib_dir([], Acc) -> %%----------------------------------------------------------------------- +get_plts(["--"|T], Acc) -> {lists:reverse(Acc), T}; +get_plts(["-"++_Opt = H|T], Acc) -> {lists:reverse(Acc), [H|T]}; +get_plts([H|T], Acc) -> get_plts(T, [H|Acc]); +get_plts([], Acc) -> {lists:reverse(Acc), []}. + +%%----------------------------------------------------------------------- + help_warnings() -> S = warning_options_msg(), io:put_chars(S), @@ -316,9 +329,10 @@ help_warnings() -> help_message() -> S = "Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose] - [-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]* - [--output_plt file] [-Wwarn]* [--src] [--gui | --wx] - [files_or_dirs] [-r dirs] [--apps applications] [-o outfile] + [-pa dir]* [--plt plt] [--plts plts] [-Ddefine]* + [-I include_dir]* [--output_plt file] [-Wwarn]* + [--src] [--gui | --wx] [files_or_dirs] [-r dirs] + [--apps applications] [-o outfile] [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] [--no_native] @@ -362,6 +376,10 @@ Options: --plt plt Use the specified plt as the initial plt (if the plt was built during setup the files will be checked for consistency) + --plts plts + Merges the specified plts to create the initial plt -- requires + that the plts are disjoint (i.e., do not have any module + appearing in more than one plt) -Wwarn A family of options which selectively turn on/off warnings (for help on the names of warnings use dialyzer -Whelp) @@ -378,12 +396,12 @@ Options: --build_plt The analysis starts from an empty plt and creates a new one from the files specified with -c and -r. Only works for beam files. - Use --plt or --output_plt to override the default plt location. + Use --plt(s) or --output_plt to override the default plt location. --add_to_plt The plt is extended to also include the files specified with -c and -r. - Use --plt to specify wich plt to start from, and --output_plt to - specify where to put the plt. Note that the analysis might include - files from the plt if they depend on the new files. + Use --plt(s) to specify wich plt to start from, and --output_plt to + specify where to put the plt. Note that the analysis might include + files from the plt if they depend on the new files. This option only works with beam files. --remove_from_plt The information from the files specified with -c and -r is removed @@ -396,8 +414,8 @@ Options: Skip the plt check when running Dialyzer. Useful when working with installed plts that never change. --plt_info - Makes Dialyzer print information about the plt and then quit. The plt - can be specified with --plt. + Makes Dialyzer print information about the plt and then quit. The plt + can be specified with --plt(s). --get_warnings Makes Dialyzer emit warnings even when manipulating the plt. Only emits warnings for files that are actually analyzed. diff --git a/lib/dialyzer/src/dialyzer_gui.erl b/lib/dialyzer/src/dialyzer_gui.erl index f353638cdf..4436330f7f 100644 --- a/lib/dialyzer/src/dialyzer_gui.erl +++ b/lib/dialyzer/src/dialyzer_gui.erl @@ -88,8 +88,8 @@ -spec start(#options{}) -> ?RET_NOTHING_SUSPICIOUS. -start(DialyzerOptions = #options{from = From, init_plt = InitPltFile, - legal_warnings = LegalWarnings}) -> +start(#options{from = From, init_plts = InitPltFiles, + legal_warnings = LegalWarnings} = DialyzerOptions) -> process_flag(trap_exit, true), GS = gs:start(), @@ -336,9 +336,13 @@ start(DialyzerOptions = #options{from = From, init_plt = InitPltFile, gs:config(Packer, WH), {ok, CWD} = file:get_cwd(), - InitPlt = try dialyzer_plt:from_file(InitPltFile) - catch throw:{dialyzer_error, _} -> dialyzer_plt:new() - end, + InitPlt = + case InitPltFiles of + [] -> dialyzer_plt:new(); + _ -> + Plts = [dialyzer_plt:from_file(F) || F <- InitPltFiles], + dialyzer_plt:merge_plts_or_report_conflicts(InitPltFiles, Plts) + end, State = #gui_state{add_all = AddAll, add_file = AddFile, diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl index 2e309d7ec1..e711c15ea7 100644 --- a/lib/dialyzer/src/dialyzer_gui_wx.erl +++ b/lib/dialyzer/src/dialyzer_gui_wx.erl @@ -88,7 +88,7 @@ start(DialyzerOptions) -> State = wx:batch(fun() -> create_window(Wx, DialyzerOptions) end), gui_loop(State). -create_window(Wx, DialyzerOptions) -> +create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) -> {ok, Host} = inet:gethostname(), %%---------- initializing frame --------- @@ -258,11 +258,15 @@ create_window(Wx, DialyzerOptions) -> plt = PltMenu, options =OptionsMenu, help = HelpMenu}, - - InitPlt = try dialyzer_plt:from_file(DialyzerOptions#options.init_plt) - catch throw:{dialyzer_error, _} -> dialyzer_plt:new() - end, + InitPlt = + case InitPltFiles of + [] -> dialyzer_plt:new(); + _ -> + Plts = [dialyzer_plt:from_file(F) || F <- InitPltFiles], + dialyzer_plt:merge_plts_or_report_conflicts(InitPltFiles, Plts) + end, + #gui_state{add = AddButton, add_dir = AddDirButton, add_rec = AddRecButton, diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl index 010625b7bd..2c0afa6e2b 100644 --- a/lib/dialyzer/src/dialyzer_options.erl +++ b/lib/dialyzer/src/dialyzer_options.erl @@ -53,14 +53,21 @@ build(Opts) -> InitPlt = dialyzer_plt:get_default_plt(), DefaultOpts = #options{}, DefaultOpts1 = DefaultOpts#options{legal_warnings = DefaultWarns1, - init_plt = InitPlt}, - try - NewOpts = build_options(Opts, DefaultOpts1), + init_plts = [InitPlt]}, + try + Opts1 = preprocess_opts(Opts), + NewOpts = build_options(Opts1, DefaultOpts1), postprocess_opts(NewOpts) catch throw:{dialyzer_options_error, Msg} -> {error, Msg} end. +preprocess_opts([]) -> []; +preprocess_opts([{init_plt, File}|Opts]) -> + [{plts, [File]}|preprocess_opts(Opts)]; +preprocess_opts([Opt|Opts]) -> + [Opt|preprocess_opts(Opts)]. + postprocess_opts(Opts = #options{}) -> Opts1 = check_output_plt(Opts), adapt_get_warnings(Opts1). @@ -144,9 +151,9 @@ build_options([{OptionName, Value} = Term|Rest], Options) -> build_options(Rest, Options#options{from = Value}); get_warnings -> build_options(Rest, Options#options{get_warnings = Value}); - init_plt -> - assert_filenames([Term], [Value]), - build_options(Rest, Options#options{init_plt = Value}); + plts -> + assert_filenames(Term, Value), + build_options(Rest, Options#options{init_plts = Value}); include_dirs -> assert_filenames(Term, Value), OldVal = Options#options.include_dirs, diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 08d0b318b5..a7ba270c41 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -51,6 +51,7 @@ lookup_contract/2, lookup_module/2, merge_plts/1, + merge_plts_or_report_conflicts/2, new/0, plt_and_info_from_file/1, get_specs/1, @@ -292,6 +293,38 @@ merge_plts(List) -> exported_types = sets_merge(ExpTypesList), contracts = table_merge(ContractsList)}. +-spec merge_disj_plts([plt()]) -> plt(). + +merge_disj_plts(List) -> + InfoList = [Info || #plt{info = Info} <- List], + TypesList = [Types || #plt{types = Types} <- List], + ExpTypesList = [ExpTypes || #plt{exported_types = ExpTypes} <- List], + ContractsList = [Contracts || #plt{contracts = Contracts} <- List], + #plt{info = table_disj_merge(InfoList), + types = table_disj_merge(TypesList), + exported_types = sets_disj_merge(ExpTypesList), + contracts = table_disj_merge(ContractsList)}. + +-spec merge_plts_or_report_conflicts([file:filename()], [plt()]) -> plt(). + +merge_plts_or_report_conflicts(PltFiles, Plts) -> + try + merge_disj_plts(Plts) + catch throw:{dialyzer_error, not_disjoint_plts} -> + IncFiles = lists:append([begin {ok, Fs} = included_files(F), Fs end + || F <- PltFiles]), + ConfFiles = find_duplicates(IncFiles), + Msg = io_lib:format("Could not merge PLTs since they are not disjoint\n" + "The following files are included in more than one " + "PLTs:\n~p\n", [ConfFiles]), + error(Msg) + end. + +find_duplicates(List) -> + ModList = [filename:basename(E) || E <- List], + SortedList = lists:usort(ModList), + lists:usort(ModList -- SortedList). + -spec to_file(file:filename(), plt(), mod_deps(), {[file_md5()], mod_deps()}) -> 'ok'. to_file(FileName, @@ -556,6 +589,25 @@ table_merge([Plt|Plts], Acc) -> NewAcc = dict:merge(fun(_Key, Val, Val) -> Val end, Plt, Acc), table_merge(Plts, NewAcc). +table_disj_merge([H|T]) -> + table_disj_merge(T, H). + +table_disj_merge([], Acc) -> + Acc; +table_disj_merge([Plt|Plts], Acc) -> + case table_is_disjoint(Plt, Acc) of + true -> + NewAcc = dict:merge(fun(_Key, _Val1, _Val2) -> gazonk end, + Plt, Acc), + table_disj_merge(Plts, NewAcc); + false -> throw({dialyzer_error, not_disjoint_plts}) + end. + +table_is_disjoint(T1, T2) -> + K1 = dict:fetch_keys(T1), + K2 = dict:fetch_keys(T2), + lists:all(fun(E) -> not lists:member(E, K2) end, K1). + sets_merge([H|T]) -> sets_merge(T, H). @@ -565,6 +617,19 @@ sets_merge([Plt|Plts], Acc) -> NewAcc = sets:union(Plt, Acc), sets_merge(Plts, NewAcc). +sets_disj_merge([H|T]) -> + sets_disj_merge(T, H). + +sets_disj_merge([], Acc) -> + Acc; +sets_disj_merge([Plt|Plts], Acc) -> + case sets:is_disjoint(Plt, Acc) of + true -> + NewAcc = sets:union(Plt, Acc), + sets_disj_merge(Plts, NewAcc); + false -> throw({dialyzer_error, not_disjoint_plts}) + end. + %%--------------------------------------------------------------------------- %% Debug utilities. diff --git a/lib/docbuilder/src/docb_main.erl b/lib/docbuilder/src/docb_main.erl index 87a1401a02..4f5f035a65 100644 --- a/lib/docbuilder/src/docb_main.erl +++ b/lib/docbuilder/src/docb_main.erl @@ -34,14 +34,23 @@ %% Parses the source file File and transforms the result to html, %% latex and/or man page format. process(File, Opts) -> - - File1 = File ++ ".tmpconv", - os:cmd("sed -e 's/xi:include[ \t]*href/include file/g' -e 's/xmlns:xi=\"http:\\/\\/www.w3.org\\/2001\\/XInclude\"//g' < " ++ - File ++ ".xml > " ++ File1 ++ ".xml"), %LATH + + SrcType = docb_util:lookup_option(src_type, Opts), + + File1 = + case SrcType of + ".xml" -> + FileTmp = File ++ ".tmpconv", + os:cmd("sed -e 's/xi:include[ \t]*href/include file/g' -e 's/xmlns:xi=\"http:\\/\\/www.w3.org\\/2001\\/XInclude\"//g' < " ++ + File ++ ".xml > " ++ FileTmp ++ ".xml"), + FileTmp; + ".sgml" -> + File + end, case parse1(File1, Opts) of errors -> - file:delete(File1 ++ ".xml"), + delete_tmp_file(SrcType, File1), errors; {ok, Tree} -> From = element(1, Tree), @@ -62,15 +71,21 @@ process(File, Opts) -> Result = [transform(From, To, Opts, File, Tree)||To <- Tos], case lists:member(transformation_error,Result) of true -> - file:delete(File1 ++ ".xml"), + delete_tmp_file(SrcType, File1), errors; _ -> - file:delete(File1 ++ ".xml"), + delete_tmp_file(SrcType, File1), ok end end. + +delete_tmp_file(".xml", File) -> + file:delete(File ++ ".xml"); +delete_tmp_file(_, _) -> + ok. + %%---------------------------------------------------------------------- %% parse(File, Opts) -> {ok, Tree} | errors diff --git a/lib/erl_interface/src/legacy/erl_marshal.c b/lib/erl_interface/src/legacy/erl_marshal.c index 5084c65230..a6c2f64dd0 100644 --- a/lib/erl_interface/src/legacy/erl_marshal.c +++ b/lib/erl_interface/src/legacy/erl_marshal.c @@ -511,29 +511,28 @@ static int erl_term_len_helper(ETERM *ep, int dist) case ERL_INTEGER: i = ep->uval.ival.i; - if ((i > ERL_MAX) || (i < ERL_MIN)) len = 7; - else if ((i < 256) && (i >= 0)) len = 2; + if ((i < 256) && (i >= 0)) len = 2; else len = 5; break; case ERL_U_INTEGER: u = ep->uval.uival.u; - if (u > ERL_MAX) len = 7; + if ((int)u < 0) len = 7; else if (u < 256) len = 2; else len = 5; break; case ERL_LONGLONG: l = ep->uval.llval.i; - if ((l > ((long long) ERL_MAX)) || - (l < ((long long) ERL_MIN))) len = 11; + if ((l > ((long long) INT_MAX)) || + (l < ((long long) INT_MIN))) len = 11; else if ((l < 256) && (l >= 0)) len = 2; else len = 5; break; case ERL_U_LONGLONG: ul = ep->uval.ullval.u; - if (ul > ((unsigned long long) ERL_MAX)) len = 11; + if (ul > ((unsigned long long) INT_MAX)) len = 11; else if (ul < 256) len = 2; else len = 5; break; @@ -546,12 +545,7 @@ static int erl_term_len_helper(ETERM *ep, int dist) case ERL_REF: i = strlen((char *)ERL_REF_NODE(ep)); - if (dist >= 4 && ERL_REF_LEN(ep) > 1) { - len = 1 + 2 + (i+3) + 1 + ERL_REF_LEN(ep) * 4; - } else { - /* 1 + N + 4 + 1 where N = 3 + strlen */ - len = 9 + i; - } + len = 1 + 2 + (i+3) + 1 + ERL_REF_LEN(ep) * 4; break; case ERL_PORT: diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl index 09a37409f2..524a04a3b4 100644 --- a/lib/erl_interface/test/ei_decode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_SUITE.erl @@ -232,7 +232,7 @@ send_integers(P) -> ?line send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*) ?line send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*) - case erlang:system_info(wordsize) of + case erlang:system_info({wordsize,external}) of 4 -> ?line send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32 ?line send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32 diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c index f273efd532..80d7f69520 100644 --- a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c +++ b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * Copyright Ericsson AB 1997-2010. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -98,14 +98,30 @@ static void encode_decode(ETERM* original, const char* text) { static unsigned char encoded[16*1024]; ETERM* new_terms; - int bytes = erl_encode(original, encoded); + ETERM* head; + int bytes; + int len; + + /* If a list, check the elements one by one first */ + head = erl_hd(original); + if (head != NULL) { + encode_decode(head, "CAR"); + encode_decode(erl_tl(original), "CDR"); + } + bytes = erl_encode(original, encoded); if (bytes == 0) { fail("failed to encode terms"); } else if (bytes > sizeof(encoded)) { fail("encoded terms buffer overflow"); } + else if (bytes != (len=erl_term_len(original))) { + fprintf(stderr, "bytes(%d) != len(%d) for term ", bytes, len); + erl_print_term(stderr, original); + fprintf(stderr, " [%s]\r\n", text); + fail("erl_encode and erl_term_len do not agree"); + } else if ((new_terms = erl_decode(encoded)) == NULL) { fail("failed to decode terms"); } diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 1cba89c621..ed5bf03804 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -192,7 +192,7 @@ type(binary, referenced_byte_size, 1, Xs) -> type(code, add_path, 1, Xs) -> strict(arg_types(code, add_path, 1), Xs, fun (_) -> - t_sup(t_boolean(), + t_sup(t_atom('true'), t_tuple([t_atom('error'), t_atom('bad_directory')])) end); type(code, add_patha, 1, Xs) -> @@ -1992,28 +1992,18 @@ type(ets, update_counter, 3, Xs) -> type(ets, update_element, 3, Xs) -> strict(arg_types(ets, update_element, 3), Xs, fun (_) -> t_boolean() end); %%-- file --------------------------------------------------------------------- -type(file, close, 1, Xs) -> - strict(arg_types(file, close, 1), Xs, fun (_) -> t_file_return() end); -type(file, delete, 1, Xs) -> - strict(arg_types(file, delete, 1), Xs, fun (_) -> t_file_return() end); -type(file, get_cwd, 0, _) -> - t_sup(t_tuple([t_atom('ok'), t_string()]), - t_tuple([t_atom('error'), t_file_posix_error()])); -type(file, make_dir, 1, Xs) -> - strict(arg_types(file, make_dir, 1), Xs, fun (_) -> t_file_return() end); -type(file, read_file, 1, Xs) -> - strict(arg_types(file, read_file, 1), Xs, - fun (_) -> - t_sup([t_tuple([t_atom('ok'), t_binary()]), - t_tuple([t_atom('error'), t_file_posix_error()])]) - end); -type(file, set_cwd, 1, Xs) -> - strict(arg_types(file, set_cwd, 1), Xs, - fun (_) -> t_sup(t_atom('ok'), - t_tuple([t_atom('error'), t_file_posix_error()])) - end); -type(file, write_file, 2, Xs) -> - strict(arg_types(file, write_file, 2), Xs, fun (_) -> t_file_return() end); +type(file, native_name_encoding, 0, _) -> + t_file_encoding(); +%%-- prim_file ---------------------------------------------------------------- +type(prim_file, internal_name2native, 1, Xs) -> + strict(arg_types(prim_file, internal_name2native, 1), Xs, + fun (_) -> t_binary() end); +type(prim_file, internal_native2name, 1, Xs) -> + strict(arg_types(prim_file, internal_native2name, 1), Xs, + fun (_) -> t_prim_file_name() end); +type(prim_file, internal_normalize_utf8, 1, Xs) -> + strict(arg_types(prim_file, internal_normalize_utf8, 1), Xs, + fun (_) -> t_binary() end); %%-- gen_tcp ------------------------------------------------------------------ %% NOTE: All type information for this module added to avoid loss of precision type(gen_tcp, accept, 1, Xs) -> @@ -3357,7 +3347,7 @@ arg_types(code, all_loaded, 0) -> arg_types(code, compiler_dir, 0) -> []; arg_types(code, del_path, 1) -> - [t_sup(t_string(), t_atom())]; % OBS: doc differs from add_path/1 - why? + [t_sup(t_string(), t_atom())]; % OBS: differs from add_path/1 arg_types(code, delete, 1) -> [t_atom()]; arg_types(code, ensure_loaded, 1) -> @@ -3405,7 +3395,7 @@ arg_types(code, replace_path, 2) -> arg_types(code, root_dir, 0) -> []; arg_types(code, set_path, 1) -> - [t_string()]; + [t_list(t_string())]; arg_types(code, soft_purge, 1) -> arg_types(code, delete, 1); arg_types(code, stick_mod, 1) -> @@ -4200,22 +4190,15 @@ arg_types(ets, update_element, 3) -> PosValue = t_tuple([t_integer(), t_any()]), [t_tab(), t_any(), t_sup(PosValue, t_list(PosValue))]; %%------- file ---------------------------------------------------------------- -arg_types(file, close, 1) -> - [t_file_io_device()]; -arg_types(file, delete, 1) -> - [t_file_name()]; -arg_types(file, get_cwd, 0) -> +arg_types(file, native_name_encoding, 0) -> []; -arg_types(file, make_dir, 1) -> - [t_file_name()]; -arg_types(file, read_file, 1) -> - [t_file_name()]; -arg_types(file, set_cwd, 1) -> - [t_file_name()]; -arg_types(file, write, 2) -> - [t_file_io_device(), t_iodata()]; -arg_types(file, write_file, 2) -> - [t_file_name(), t_sup(t_binary(), t_list())]; +%%-- prim_file ---------------------------------------------------------------- +arg_types(prim_file, internal_name2native, 1) -> + [t_prim_file_name()]; +arg_types(prim_file, internal_native2name, 1) -> + [t_binary()]; +arg_types(prim_file, internal_normalize_utf8, 1) -> + [t_binary()]; %%------- gen_tcp ------------------------------------------------------------- arg_types(gen_tcp, accept, 1) -> [t_socket()]; @@ -4647,7 +4630,7 @@ t_httppacket() -> t_HttpHeader(), t_atom('http_eoh'), t_HttpError()]). t_endian() -> - t_sup([t_atom('big'), t_atom('little')]). + t_sup(t_atom('big'), t_atom('little')). %% ===================================================================== %% Types for the binary module @@ -4991,68 +4974,12 @@ t_ets_info_items() -> t_atom('type')]). %% ===================================================================== -%% These are used for the built-in functions of 'file' +%% These are used for the built-in functions of 'prim_file' %% ===================================================================== -t_file_io_device() -> - t_sup(t_pid(), t_tuple([t_atom('file_descriptor'), t_atom(), t_any()])). - -t_file_name() -> - t_sup([t_atom(), - t_string(), - %% DeepList = [char() | atom() | DeepList] -- approximation below - t_list(t_sup([t_atom(), t_string(), t_list()]))]). - -t_file_open_option() -> - t_sup([t_atom('read'), - t_atom('write'), - t_atom('append'), - t_atom('raw'), - t_atom('binary'), - t_atom('delayed_write'), - t_atom('read_ahead'), - t_atom('compressed'), - t_tuple([t_atom('delayed_write'), - t_pos_integer(), t_non_neg_integer()]), - t_tuple([t_atom('read_ahead'), t_pos_integer()])]). - -%% This lists all Posix errors that can occur in file:*/* functions -t_file_posix_error() -> - t_sup([t_atom('eacces'), - t_atom('eagain'), - t_atom('ebadf'), - t_atom('ebusy'), - t_atom('edquot'), - t_atom('eexist'), - t_atom('efault'), - t_atom('efbig'), - t_atom('eintr'), - t_atom('einval'), - t_atom('eio'), - t_atom('eisdir'), - t_atom('eloop'), - t_atom('emfile'), - t_atom('emlink'), - t_atom('enametoolong'), - t_atom('enfile'), - t_atom('enodev'), - t_atom('enoent'), - t_atom('enomem'), - t_atom('enospc'), - t_atom('enotblk'), - t_atom('enotdir'), - t_atom('enotsup'), - t_atom('enxio'), - t_atom('eperm'), - t_atom('epipe'), - t_atom('erofs'), - t_atom('espipe'), - t_atom('esrch'), - t_atom('estale'), - t_atom('exdev')]). - -t_file_return() -> - t_sup(t_atom('ok'), t_tuple([t_atom('error'), t_file_posix_error()])). +t_prim_file_name() -> + t_sup([t_string(), + t_binary()]). %% ===================================================================== %% These are used for the built-in functions of 'gen_tcp' @@ -5254,7 +5181,12 @@ t_ML() -> % a binary or a possibly deep list of integers or binaries t_sup(t_list(t_sup([t_integer(), t_binary(), t_list()])), t_binary()). t_encoding() -> - t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']). + t_sup([t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']), + t_tuple([t_atom('utf16'), t_endian()]), + t_tuple([t_atom('utf32'), t_endian()])]). + +t_file_encoding() -> + t_atoms(['latin1', 'utf8']). t_encoding_a2b() -> % for the 2nd arg of atom_to_binary/2 and binary_to_atom/2 t_atoms(['latin1', 'unicode', 'utf8']). diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 64cdd3a8ea..d3441d3623 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -36,6 +36,61 @@ other Erlang processes to continue executing in parallel with the file operations. See the command line flag <c>+A</c> in <seealso marker="erts:erl">erl(1)</seealso>.</p> + + <p>The Erlang VM supports file names in Unicode to a limited + extent. Depending on how the VM is started (with the parameter + <c>+fnu</c> or <c>+fnl</c>), file names given can contain + characters > 255 and the VM system will convert file names + back and forth to the native file name encoding.</p> + + <p>The default behavior for Unicode character translation depends + on to what extent the underlying OS/filesystem enforces consistent + naming. On OSes where all file names are ensured to be in one or + another encoding, Unicode is the default (currently this holds for + Windows and MacOSX). On OSes with completely transparent file + naming (i.e. all Unixes except MacOSX), ISO-latin-1 file naming is + the default. The reason for the ISO-latin-1 default is that + file names are not guaranteed to be possible to interpret according to + the Unicode encoding expected (i.e. UTF-8), and file names that + cannot be decoded will only be accessible by using "raw + file names", in other word file names given as binaries.</p> + + <p>As file names are traditionally not binaries in Erlang, + applications that need to handle raw file names need to be + converted, why the Unicode mode for file names is not default on + systems having completely transparent file naming.</p> + + <note>As of R14B01, the most basic file handling modules + (<c>file</c>, <c>prim_file</c>, <c>filelib</c> and + <c>filename</c>) accept raw file names, but the rest of OTP is not + guaranteed to handle them, why Unicode file naming on systems + where it is not default is still considered experimental.</note> + + <p>Raw file names is a new feature in OTP R14B01, which allows the + user to supply completely uninterpreted file names to the + underlying OS/filesystem. They are supplied as binaries, where it + is up to the user to supply a correct encoding for the + environment. The function <c>file:native_name_encoding()</c> can + be used to check what encoding the VM is working in. If the + function returns <c>latin1</c> file names are not in any way + converted to Unicode, if it is <c>utf8</c>, raw file names should + be encoded as UTF-8 if they are to follow the convention of the VM + (and usually the convention of the OS as well). Using raw + file names is useful if you have a filesystem with inconsistent + file naming, where some files are named in UTF-8 encoding while + others are not. A file:list_dir on such mixed file name systems + when the VM is in Unicode file name mode might return file names as + raw binaries as they cannot be interpreted as Unicode + file names. Raw file names can also be used to give UTF-8 encoded + file names even though the VM is not started in Unicode file name + translation mode.</p> + + <p>Note that on Windows, <c>file:native_name_encoding()</c> + returns <c>utf8</c> per default, which is the format for raw + file names even on Windows, although the underlying OS specific + code works in a limited version of little endian UTF16. As far as + the Erlang programmer is concerned, Windows native Unicode format + is UTF-8...</p> </description> <section> @@ -47,8 +102,14 @@ iodata() = iolist() | binary() io_device() as returned by file:open/2, a process handling IO protocols -name() = string() | atom() | DeepList +name() = string() | atom() | DeepList | RawFilename DeepList = [char() | atom() | DeepList] + RawFilename = binary() + If VM is in unicode filename mode, string() and char() are allowed to be > 255. + RawFilename is a filename not subject to Unicode translation, meaning that it + can contain characters not conforming to the Unicode encoding expected from the + filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode + filename mode). posix() an atom which is named from the POSIX error codes used in @@ -598,6 +659,15 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> + <name>native_name_encoding() -> latin1 | utf8</name> + <fsummary>Retunr the VMs configure filename encoding.</fsummary> + <desc> + <p>This function returns the configured default file name encoding to use for raw file names. Generally an application supplying file names raw (as binaries), should obey the character encoding returned by this function.</p> + <p>By default, the VM uses ISO-latin-1 file name encoding on filesystems and/or OSes that use completely transparent file naming. This includes all Unix versions except for MacOSX, where the vfs layer enforces UTF-8 file naming. By giving the experimental option <c>+fnu</c> when starting Erlang, UTF-8 translation of file names can be turned on even for those systems. If Unicode file name translation is in effect, the system behaves as usual as long as file names conform to the encoding, but will return file names that are not properly encoded in UTF-8 as raw file names (i.e. binaries).</p> + <p>On Windows, this function also returns <c>utf8</c> by default. The OS uses a pure Unicode naming scheme and file names are always possible to interpret as valid Unicode. The fact that the underlying Windows OS actually encodes file names using little endian UTF-16 can be ignored by the Erlang programmer. Windows and MacOSX are the only operating systems where the VM operates in Unicode file name mode by default.</p> + </desc> + </func> + <func> <name>open(Filename, Modes) -> {ok, IoDevice} | {error, Reason}</name> <fsummary>Open a file</fsummary> <type> diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index ec256d5806..feb5131aad 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -213,19 +213,20 @@ unstick_mod(Mod) when is_atom(Mod) -> call({unstick_mod,Mod}). -spec is_sticky(Module :: atom()) -> boolean(). is_sticky(Mod) when is_atom(Mod) -> call({is_sticky,Mod}). --spec set_path(Directories :: [file:filename()]) -> 'true' | {'error', term()}. +-spec set_path(Directories :: [file:filename()]) -> + 'true' | {'error', 'bad_directory' | 'bad_path'}. set_path(PathList) when is_list(PathList) -> call({set_path,PathList}). -spec get_path() -> [file:filename()]. get_path() -> call(get_path). --spec add_path(Directory :: file:filename()) -> 'true' | {'error', term()}. +-spec add_path(Directory :: file:filename()) -> 'true' | {'error', 'bad_directory'}. add_path(Dir) when is_list(Dir) -> call({add_path,last,Dir}). --spec add_pathz(Directory :: file:filename()) -> 'true' | {'error', term()}. +-spec add_pathz(Directory :: file:filename()) -> 'true' | {'error', 'bad_directory'}. add_pathz(Dir) when is_list(Dir) -> call({add_path,last,Dir}). --spec add_patha(Directory :: file:filename()) -> 'true' | {'error', term()}. +-spec add_patha(Directory :: file:filename()) -> 'true' | {'error', 'bad_directory'}. add_patha(Dir) when is_list(Dir) -> call({add_path,first,Dir}). -spec add_paths(Directories :: [file:filename()]) -> 'ok'. @@ -237,7 +238,6 @@ add_pathsz(Dirs) when is_list(Dirs) -> call({add_paths,last,Dirs}). -spec add_pathsa(Directories :: [file:filename()]) -> 'ok'. add_pathsa(Dirs) when is_list(Dirs) -> call({add_paths,first,Dirs}). -%% XXX Contract's input argument differs from add_path/1 -- why? -spec del_path(Name :: file:filename() | atom()) -> boolean() | {'error', 'bad_name'}. del_path(Name) when is_list(Name) ; is_atom(Name) -> call({del_path,Name}). @@ -286,6 +286,8 @@ do_start(Flags) -> ets:module_info(module), os:module_info(module), + binary:module_info(module), + unicode:module_info(module), filename:module_info(module), lists:module_info(module), diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 97d914b043..bc95359986 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -75,7 +75,7 @@ -define(RAM_FILE, ram_file). % Module %% data types --type filename() :: string(). +-type filename() :: string() | binary(). -type file_info() :: #file_info{}. -type fd() :: #file_descriptor{}. -type io_device() :: pid() | fd(). @@ -87,7 +87,7 @@ | 'delayed_write' | {'read_ahead', pos_integer()} | 'read_ahead' | 'compressed' | {'encoding', unicode:encoding()}. --type name() :: string() | atom() | [name()]. +-type name() :: string() | atom() | [name()] | binary(). -type posix() :: 'eacces' | 'eagain' | 'ebadf' | 'ebusy' | 'edquot' | 'eexist' | 'efault' | 'efbig' | 'eintr' | 'einval' | 'eio' | 'eisdir' | 'eloop' | 'emfile' | 'emlink' @@ -183,7 +183,7 @@ make_dir(Name) -> del_dir(Name) -> check_and_call(del_dir, [file_name(Name)]). --spec read_file_info(Name :: name()) -> {'ok', #file_info{}} | {'error', posix()}. +-spec read_file_info(Name :: name()) -> {'ok', file_info()} | {'error', posix()}. read_file_info(Name) -> check_and_call(read_file_info, [file_name(Name)]). @@ -193,7 +193,7 @@ read_file_info(Name) -> altname(Name) -> check_and_call(altname, [file_name(Name)]). --spec read_link_info(Name :: name()) -> {'ok', #file_info{}} | {'error', posix()}. +-spec read_link_info(Name :: name()) -> {'ok', file_info()} | {'error', posix()}. read_link_info(Name) -> check_and_call(read_link_info, [file_name(Name)]). @@ -203,7 +203,7 @@ read_link_info(Name) -> read_link(Name) -> check_and_call(read_link, [file_name(Name)]). --spec write_file_info(Name :: name(), Info :: #file_info{}) -> +-spec write_file_info(Name :: name(), Info :: file_info()) -> 'ok' | {'error', posix()}. write_file_info(Name, Info = #file_info{}) -> @@ -214,7 +214,8 @@ write_file_info(Name, Info = #file_info{}) -> list_dir(Name) -> check_and_call(list_dir, [file_name(Name)]). --spec read_file(Name :: name()) -> {'ok', binary()} | {'error', posix()}. +-spec read_file(Name :: name()) -> + {'ok', binary()} | {'error', posix() | 'terminated' | 'system_limit'}. read_file(Name) -> check_and_call(read_file, [file_name(Name)]). @@ -229,15 +230,15 @@ make_link(Old, New) -> make_symlink(Old, New) -> check_and_call(make_symlink, [file_name(Old), file_name(New)]). --spec write_file(Name :: name(), Bin :: binary()) -> 'ok' | {'error', posix()}. +-spec write_file(Name :: name(), Bin :: iodata()) -> + 'ok' | {'error', posix() | 'terminated' | 'system_limit'}. write_file(Name, Bin) -> check_and_call(write_file, [file_name(Name), make_binary(Bin)]). %% This whole operation should be moved to the file_server and prim_file %% when it is time to change file server protocol again. -%% Meanwhile, it is implemented here, slihtly less efficient. -%% +%% Meanwhile, it is implemented here, slightly less efficient. -spec write_file(Name :: name(), Bin :: binary(), Modes :: [mode()]) -> 'ok' | {'error', posix()}. @@ -348,7 +349,7 @@ open(Item, Mode) -> %%% The File argument must be either a Pid or a handle %%% returned from ?PRIM_FILE:open. --spec close(File :: io_device()) -> 'ok' | {'error', posix()}. +-spec close(File :: io_device()) -> 'ok' | {'error', posix() | 'terminated'}. close(File) when is_pid(File) -> R = file_request(File, close), @@ -367,7 +368,7 @@ close(_) -> {error, badarg}. -spec advise(File :: io_device(), Offset :: integer(), - Length :: integer(), Advise :: posix_file_advise()) -> + Length :: integer(), Advise :: posix_file_advise()) -> 'ok' | {'error', posix()}. advise(File, Offset, Length, Advise) when is_pid(File) -> @@ -449,7 +450,7 @@ pread(_, _, _) -> {error, badarg}. -spec write(File :: io_device() | atom(), Byte :: iodata()) -> - 'ok' | {'error', posix()}. + 'ok' | {'error', posix() | 'terminated'}. write(File, Bytes) when (is_pid(File) orelse is_atom(File)) -> case make_binary(Bytes) of @@ -1033,22 +1034,26 @@ path_open_first([], _Name, _Mode, LastError) -> %% Generates a flat file name from a deep list of atoms and %% characters (integers). +file_name(N) when is_binary(N) -> + N; file_name(N) -> try - file_name_1(N) + file_name_1(N,file:native_name_encoding()) catch Reason -> {error, Reason} end. -file_name_1([C|T]) when is_integer(C), C > 0, C =< 255 -> - [C|file_name_1(T)]; -file_name_1([H|T]) -> - file_name_1(H) ++ file_name_1(T); -file_name_1([]) -> +file_name_1([C|T],latin1) when is_integer(C), C < 256-> + [C|file_name_1(T,latin1)]; +file_name_1([C|T],utf8) when is_integer(C) -> + [C|file_name_1(T,utf8)]; +file_name_1([H|T],E) -> + file_name_1(H,E) ++ file_name_1(T,E); +file_name_1([],_) -> []; -file_name_1(N) when is_atom(N) -> +file_name_1(N,_) when is_atom(N) -> atom_to_list(N); -file_name_1(_) -> +file_name_1(_,_) -> throw(badarg). make_binary(Bin) when is_binary(Bin) -> diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index 39dc32bb79..14da9c1a55 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -44,11 +44,11 @@ format_error(ErrorId) -> erl_posix_msg:message(ErrorId). start(Owner, FileName, ModeList) - when is_pid(Owner), is_list(FileName), is_list(ModeList) -> + when is_pid(Owner), (is_list(FileName) orelse is_binary(FileName)), is_list(ModeList) -> do_start(spawn, Owner, FileName, ModeList). start_link(Owner, FileName, ModeList) - when is_pid(Owner), is_list(FileName), is_list(ModeList) -> + when is_pid(Owner), (is_list(FileName) orelse is_binary(FileName)), is_list(ModeList) -> do_start(spawn_link, Owner, FileName, ModeList). %%%----------------------------------------------------------------- diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 293c368e2a..f84b343de8 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -51,6 +51,7 @@ MODULES= \ error_logger_SUITE \ error_logger_warn_SUITE \ file_SUITE \ + file_name_SUITE \ prim_file_SUITE \ ram_file_SUITE \ gen_tcp_api_SUITE \ diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 6f846ebc56..e52f8a0e37 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -645,7 +645,7 @@ analyse([], [This={M,F,A}|Path], Visited, ErrCnt0) -> %% These modules should be loaded by code.erl before %% the code_server is started. OK = [erlang, os, prim_file, erl_prim_loader, init, ets, - code_server, lists, lists_sort, filename, packages, + code_server, lists, lists_sort, unicode, binary, filename, packages, gb_sets, gb_trees, hipe_unified_loader, hipe_bifs, prim_zip, zlib], ErrCnt1 = @@ -674,6 +674,22 @@ analyse2(MFA={_,_,_}, Path, Visited0) -> %%%% We need to check these manually... % fun's are ok as long as they are defined locally. check_funs({'$M_EXPR','$F_EXPR',_}, + [{unicode,characters_to_binary_int,3}, + {unicode,characters_to_binary,3}, + {filename,filename_string_to_binary,1}|_]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',_}, + [{unicode,ml_map,3}, + {unicode,characters_to_binary_int,3}, + {unicode,characters_to_binary,3}, + {filename,filename_string_to_binary,1}|_]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',_}, + [{unicode,do_o_binary2,2}, + {unicode,do_o_binary,2}, + {unicode,o_trans,1}, + {unicode,characters_to_binary_int,3}, + {unicode,characters_to_binary,3}, + {filename,filename_string_to_binary,1}|_]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',_}, [{code_server,load_native_code,4}, {code_server,load_native_code_1,2}, {code_server,load_native_code,2}, @@ -1252,7 +1268,8 @@ on_load_embedded_1(Config) -> ?line LibRoot = code:lib_dir(), ?line LinkName = filename:join(LibRoot, "on_load_app-1.0"), ?line OnLoadApp = filename:join(DataDir, "on_load_app-1.0"), - ?line file:delete(LinkName), + ?line del_link(LinkName), + io:format("LinkName :~p, OnLoadApp: ~p~n",[LinkName,OnLoadApp]), case file:make_symlink(OnLoadApp, LinkName) of {error,enotsup} -> throw({skip,"Support for symlinks required"}); @@ -1281,7 +1298,15 @@ on_load_embedded_1(Config) -> %% Clean up. ?line stop_node(Node), - ?line ok = file:delete(LinkName). + ?line ok = del_link(LinkName). + +del_link(LinkName) -> + case file:delete(LinkName) of + {error,eperm} -> + file:del_dir(LinkName); + Other -> + Other + end. create_boot(Config, Options) -> ?line {ok, OldDir} = file:get_cwd(), diff --git a/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl b/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl index b7fdd4d9ae..646921026d 100644 --- a/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl +++ b/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl @@ -8,7 +8,7 @@ run_me() -> LibDir = code:lib_dir(on_load_app), PrivDir = code:priv_dir(on_load_app), LibDir = filename:dirname(PrivDir), - ModPath = code:which(?MODULE), + ModPath = filename:join(filename:split(code:which(?MODULE))), LibDir = filename:dirname(filename:dirname(ModPath)), %% Start a process to remember that the on_load was called. diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 17c47f871d..47592ddb14 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -3268,7 +3268,7 @@ large_file(Config) when is_list(Config) -> {{unix,sunos},{A,B,C}} when A == 5, B == 5, C >= 1; A == 5, B >= 6; A >= 6 -> do_large_file(Config); - {{unix,Unix},_} when Unix =:= linux; Unix =:= darwin -> + {{unix,Unix},_} when Unix =/= sunos -> N = unix_free(Config), io:format("Free: ~w KByte~n", [N]), if N < 5 * (1 bsl 20) -> @@ -3278,7 +3278,7 @@ large_file(Config) when is_list(Config) -> do_large_file(Config) end; _ -> - {skipped,"Only supported on Win32, Linux, or SunOS >= 5.5.1"} + {skipped,"Only supported on Win32, Unix or SunOS >= 5.5.1"} end. unix_free(Config) -> @@ -3290,7 +3290,7 @@ unix_free(Config) -> N. do_large_file(Config) -> - ?line Watchdog = ?t:timetrap(?t:minutes(4)), + ?line Watchdog = ?t:timetrap(?t:minutes(5)), %% ?line Name = filename:join(?config(priv_dir, Config), ?MODULE_STRING ++ "_large_file"), @@ -3329,6 +3329,17 @@ do_large_file(Config) -> ?line {ok,P} = ?FILE_MODULE:position(F, {eof,-L}), ?line {ok,Rs} = ?FILE_MODULE:read(F, L+1), ?line ok = ?FILE_MODULE:close(F), + %% Reopen the file with 'append'; used to fail on Windows causing + %% writes to go to the beginning of the file for files > 4GB. + ?line PL = P + L, + ?line PLL = PL + L, + ?line {ok,F1} = ?FILE_MODULE:open(Name, [raw,read,write,append]), + ?line ok = ?FILE_MODULE:write(F1, R), + ?line {ok,PLL} = ?FILE_MODULE:position(F1, {cur,0}), + ?line {ok,Rs} = ?FILE_MODULE:pread(F1, P, L), + ?line {ok,PL} = ?FILE_MODULE:position(F1, {eof,-L}), + ?line {ok,R} = ?FILE_MODULE:read(F1, L+1), + ?line ok = ?FILE_MODULE:close(F1), %% ?line Mref = erlang:monitor(process, Deleter), ?line Deleter ! {Tester,done}, diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl new file mode 100644 index 0000000000..fea4df8539 --- /dev/null +++ b/lib/kernel/test/file_name_SUITE.erl @@ -0,0 +1,1729 @@ +-module(file_name_SUITE). +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-include("test_server.hrl"). +-include_lib("kernel/include/file.hrl"). + +%% +%% File operations that take filenames as parameters (* not prim_file operation) (** a drive): +%% altname +%% copy (*) +%% del_dir +%% delete +%% get_cwd (**) +%% list_dir +%% make_dir +%% make_link +%% make_symlink +%% open +%% read_file +%% read_file_info +%% read_link +%% read_link_info +%% rename +%% set_cwd +%% write_file +%% write_file_info +%% +%% File operations that opens/uses separate driver port (not connected to file) +%% altname +%% del_dir +%% delete +%% get_cwd +%% list_dir +%% make_dir +%% make_link +%% make_symlink +%% read_file_info +%% read_link +%% read_link_info +%% rename +%% set_cwd +%% write_file_info +%% +%% Operations that use ?FD_DRV in prim_file +%% open +%% read_file +%% write_file +%% +%% +%% Operations that return a filename/path +%% altname +%% get_cwd +%% list_dir +%% read_link + +-export([all/1,init_per_testcase/2, fin_per_testcase/2]). +-export([normal/1,icky/1,very_icky/1,normalize/1]). + + +init_per_testcase(_Func, Config) -> + Dog = test_server:timetrap(test_server:seconds(60)), + [{watchdog,Dog}|Config]. + +fin_per_testcase(_Func, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog). + + +all(suite) -> + [normal,icky,very_icky,normalize]. + +normalize(suite) -> + []; +normalize(doc) -> + ["Check that filename normalization works"]; +normalize(Config) when is_list(Config) -> + random:seed({1290,431421,830412}), + try + ?line UniMode = file:native_name_encoding() =/= latin1, + if + not UniMode -> + throw(need_unicode_mode); + true -> + ok + end, + ?line Pairs = [rand_comp_decomp(200) || _ <- lists:seq(1,1000)], + case os:type() of + {unix,darwin} -> + ?line [ true = (A =:= prim_file:internal_native2name(B)) || + {A,B} <- Pairs ]; + _ -> + ok + end, + ?line [ true = (A =:= prim_file:internal_normalize_utf8(B)) || + {A,B} <- Pairs ] + + catch + throw:need_unicode_mode -> + io:format("Sorry, can only run in unicode mode.~n"), + {skipped,"VM needs to be started in Unicode filename mode"} + end. + +normal(suite) -> + []; +normal(doc) -> + "Check file operations on normal file names regardless of unicode mode"; +normal(Config) when is_list(Config) -> + {ok,Dir} = file:get_cwd(), + try + Priv = ?config(priv_dir, Config), + file:set_cwd(Priv), + put(file_module,prim_file), + ok = check_normal(prim_file), + put(file_module,file), + ok = check_normal(file) + after + file:set_cwd(Dir) + end. + + +icky(suite) -> + []; +icky(doc) -> + "Check file operations on normal file names regardless of unicode mode"; +icky(Config) when is_list(Config) -> + case hopeless_darwin() of + true -> + {skipped,"This version of darwin does not support icky names at all."}; + false -> + {ok,Dir} = file:get_cwd(), + try + Priv = ?config(priv_dir, Config), + file:set_cwd(Priv), + put(file_module,prim_file), + ok = check_icky(prim_file), + put(file_module,file), + ok = check_icky(file) + after + file:set_cwd(Dir) + end + end. +very_icky(suite) -> + []; +very_icky(doc) -> + "Check file operations on normal file names regardless of unicode mode"; +very_icky(Config) when is_list(Config) -> + case hopeless_darwin() of + true -> + {skipped,"This version of darwin does not support icky names at all."}; + false -> + {ok,Dir} = file:get_cwd(), + try + Priv = ?config(priv_dir, Config), + file:set_cwd(Priv), + put(file_module,prim_file), + case check_very_icky(prim_file) of + need_unicode_mode -> + {skipped,"VM needs to be started in Unicode filename mode"}; + ok -> + put(file_module,file), + ok = check_very_icky(file) + end + after + file:set_cwd(Dir) + end + end. + + +check_normal(Mod) -> + {ok,Dir} = Mod:get_cwd(), + try + ?line make_normal_dir(Mod), + ?line {ok, L0} = Mod:list_dir("."), + ?line L1 = lists:sort(L0), + %erlang:display(L1), + ?line L1 = lists:sort(list(normal_dir())), + ?line {ok,D2} = Mod:get_cwd(), + ?line true = is_list(D2), + ?line case Mod:altname("fil1") of + {error,enotsup} -> + ok; + {ok,LLL} when is_list(LLL) -> + ok + end, + ?line [ true = is_list(El) || El <- L1], + ?line Syms = [ {S,Targ,list_to_binary(get_data(Targ,normal_dir()))} + || {T,S,Targ} <- normal_dir(), T =:= symlink ], + ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], + ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ], + ?line chk_cre_dir(Mod,[{directory,"temp_dir",normal_dir()}]), + ?line {ok,BeginAt} = Mod:get_cwd(), + ?line true = is_list(BeginAt), + ?line {error,enoent} = Mod:set_cwd("tmp_dir"), + ?line ok = Mod:set_cwd("temp_dir"), + ?line {ok, NowAt} = Mod:get_cwd(), + ?line true = BeginAt =/= NowAt, + ?line ok = Mod:set_cwd(".."), + ?line {ok,BeginAt} = Mod:get_cwd(), + ?line rm_r(Mod,"temp_dir"), + ?line true = is_list(Dir), + ?line [ true = is_list(FN) || FN <- L0 ], + case has_links() of + true -> + ?line ok = Mod:make_link("fil1","nisse"), + ?line {ok, <<"fil1">>} = Mod:read_file("nisse"), + ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisse"), + ?line ok = Mod:delete("nisse"), + ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), + ?line {error,enoent} = Mod:read_file("nisse"), + ?line {error,enoent} = Mod:read_link_info("nisse"); + false -> + ok + end, + ?line [ begin + ?line {ok, FD} = Mod:open(Name,[read]), + ?line {ok, Content} = Mod:read(FD,1024), + ?line ok = file:close(FD) + end || {regular,Name,Content} <- normal_dir() ], + ?line [ begin + ?line {ok, FD} = Mod:open(Name,[read,binary]), + ?line BC = list_to_binary(Content), + ?line {ok, BC} = Mod:read(FD,1024), + ?line ok = file:close(FD) + end || {regular,Name,Content} <- normal_dir() ], + ?line Mod:rename("fil1","tmp_fil1"), + ?line {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"), + ?line {error,enoent} = Mod:read_file("fil1"), + ?line Mod:rename("tmp_fil1","fil1"), + ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), + ?line {error,enoent} = Mod:read_file("tmp_fil1"), + ?line {ok,FI} = Mod:read_file_info("fil1"), + ?line NewMode = FI#file_info.mode band (bnot 8#333), + ?line NewMode2 = NewMode bor 8#222, + ?line true = NewMode2 =/= NewMode, + ?line ok = Mod:write_file_info("fil1",FI#file_info{mode = NewMode}), + ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info("fil1"), + ?line ok = Mod:write_file_info("fil1",FI#file_info{mode = NewMode2}), + ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("fil1"), + ok + after + case Mod:read_file_info("fil1") of + {ok,FII} -> + NewModeI = FII#file_info.mode bor 8#777, + Mod:write_file_info("fil1",FII#file_info{mode = NewModeI}); + _ -> + ok + end, + Mod:set_cwd(Dir), + io:format("Wd now: ~s~n",[Dir]) + end. + +check_icky(Mod) -> + {ok,Dir} = Mod:get_cwd(), + try + ?line true=(length("���") =:= 3), + ?line UniMode = file:native_name_encoding() =/= latin1, + ?line make_icky_dir(Mod), + ?line {ok, L0} = Mod:list_dir("."), + ?line L1 = lists:sort(L0), + io:format("~p ~p~n",[L1,list(icky_dir())]), + ?line L1 = lists:sort(convlist(list(icky_dir()))), + ?line {ok,D2} = Mod:get_cwd(), + ?line true = is_list(D2), +%% Altname only on windows, and there are no non native filenames there +%% ?line case Mod:altname("fil1") of +%% {error,enotsup} -> +%% ok; +%% {ok,LLL} when is_list(LLL) -> +%% ok +%% end, + ?line [ true = ((is_list(El) or (UniMode and is_binary(El)))) || El <- L1], + ?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,icky_dir()))} + || {T,S,Targ} <- icky_dir(), T =:= symlink ], + ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], + ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ], + ?line chk_cre_dir(Mod,[{directory,"���_dir",icky_dir()}]), + ?line {ok,BeginAt} = Mod:get_cwd(), + ?line true = is_list(BeginAt), + ?line {error,enoent} = Mod:set_cwd("��_dir"), + ?line ok = Mod:set_cwd("���_dir"), + ?line {ok, NowAt} = Mod:get_cwd(), + ?line true = is_list(NowAt), + ?line true = BeginAt =/= NowAt, + ?line ok = Mod:set_cwd(".."), + ?line {ok,BeginAt} = Mod:get_cwd(), + ?line rm_r2(Mod,"���_dir"), + {OS,TYPE} = os:type(), + % Check that treat_icky really converts to the same as the OS + case UniMode of + true -> + ?line chk_cre_dir(Mod,[{directory,"���_dir",[]}]), + ?line ok = Mod:set_cwd("���_dir"), + ?line ok = Mod:write_file(<<"���">>,<<"hello">>), + ?line Treated = treat_icky(<<"���">>), + ?line {ok,[Treated]} = Mod:list_dir("."), + ?line ok = Mod:delete(<<"���">>), + ?line {ok,[]} = Mod:list_dir("."), + ?line ok = Mod:set_cwd(".."), + ?line rm_r2(Mod,"���_dir"); + false -> + ok + end, + + ?line chk_cre_dir(Mod,[{directory,treat_icky(<<"���_dir">>),icky_dir()}]), + if + UniMode and (OS =/= win32) -> + ?line {error,enoent} = Mod:set_cwd("���_dir"); + true -> + ok + end, + ?line ok = Mod:set_cwd(treat_icky(<<"���_dir">>)), + ?line {ok, NowAt2} = Mod:get_cwd(), + io:format("~p~n",[NowAt2]), + % Cannot create raw unicode-breaking filenames on windows or macos + ?line true = ((((not UniMode) or (OS =:= win32) or (TYPE=:=darwin)) and is_list(NowAt2)) orelse ((UniMode) and is_binary(NowAt2))), + ?line true = BeginAt =/= NowAt2, + ?line ok = Mod:set_cwd(".."), + ?line {ok,BeginAt} = Mod:get_cwd(), + ?line rm_r2(Mod,conv(treat_icky(<<"���_dir">>))), + case has_links() of + true -> + ?line ok = Mod:make_link("fil1","nisse�"), + ?line {ok, <<"fil1">>} = Mod:read_file("nisse�"), + ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisse�"), + ?line ok = Mod:delete("nisse�"), + ?line ok = Mod:make_link("fil1",treat_icky(<<"nisse�">>)), + ?line {ok, <<"fil1">>} = Mod:read_file(treat_icky(<<"nisse�">>)), + ?line {ok, #file_info{type = regular}} = Mod:read_link_info(treat_icky(<<"nisse�">>)), + ?line ok = Mod:delete(treat_icky(<<"nisse�">>)), + ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), + ?line {error,enoent} = Mod:read_file("nisse�"), + ?line {error,enoent} = Mod:read_link_info("nisse�"), + ?line {error,enoent} = Mod:read_file(treat_icky(<<"nisse�">>)), + ?line {error,enoent} = Mod:read_link_info(treat_icky(<<"nisse�">>)); + false -> + ok + end, + ?line [ begin + ?line {ok, FD} = Mod:open(Name,[read]), + ?line {ok, Content} = Mod:read(FD,1024), + ?line ok = file:close(FD) + end || {regular,Name,Content} <- icky_dir() ], + ?line [ begin + ?line {ok, FD} = Mod:open(Name,[read,binary]), + ?line BC = list_to_binary([Content]), + ?line {ok, BC} = Mod:read(FD,1024), + ?line ok = file:close(FD) + end || {regular,Name,Content} <- icky_dir() ], + ?line Mod:rename("���2","���_fil1"), + ?line {ok, <<"���2">>} = Mod:read_file("���_fil1"), + ?line {error,enoent} = Mod:read_file("���2"), + ?line Mod:rename("���_fil1","���2"), + ?line {ok, <<"���2">>} = Mod:read_file("���2"), + ?line {error,enoent} = Mod:read_file("���_fil1"), + + ?line Mod:rename("���2",treat_icky(<<"���_fil1">>)), + ?line {ok, <<"���2">>} = Mod:read_file(treat_icky(<<"���_fil1">>)), + if + UniMode and (OS =/= win32) -> + {error,enoent} = Mod:read_file("���_fil1"); + true -> + ok + end, + ?line {error,enoent} = Mod:read_file("���2"), + ?line Mod:rename(treat_icky(<<"���_fil1">>),"���2"), + ?line {ok, <<"���2">>} = Mod:read_file("���2"), + ?line {error,enoent} = Mod:read_file("���_fil1"), + ?line {error,enoent} = Mod:read_file(treat_icky(<<"���_fil1">>)), + + ?line {ok,FI} = Mod:read_file_info("���2"), + ?line NewMode = FI#file_info.mode band (bnot 8#333), + ?line NewMode2 = NewMode bor 8#222, + ?line true = NewMode2 =/= NewMode, + ?line ok = Mod:write_file_info("���2",FI#file_info{mode = NewMode}), + ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info("���2"), + ?line ok = Mod:write_file_info("���2",FI#file_info{mode = NewMode2}), + ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("���2"), + + ?line {ok,FII} = Mod:read_file_info(treat_icky(<<"���5">>)), + ?line true = NewMode2 =/= NewMode, + ?line ok = Mod:write_file_info(treat_icky(<<"���5">>),FII#file_info{mode = NewMode}), + ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info(treat_icky(<<"���5">>)), + ?line ok = Mod:write_file_info(<<"���5">>,FII#file_info{mode = NewMode2}), + ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info(treat_icky(<<"���5">>)), + ok + after + Mod:set_cwd(Dir), + io:format("Wd now: ~s~n",[Dir]) + end. + +check_very_icky(Mod) -> + {ok,Dir} = Mod:get_cwd(), + try + ?line true=(length("���") =:= 3), + ?line UniMode = file:native_name_encoding() =/= latin1, + if + not UniMode -> + throw(need_unicode_mode); + true -> + ok + end, + ?line make_very_icky_dir(Mod), + ?line {ok, L0} = Mod:list_dir("."), + ?line L1 = lists:sort(L0), + ?line L1 = lists:sort(convlist(list(very_icky_dir()))), + ?line {ok,D2} = Mod:get_cwd(), + ?line true = is_list(D2), + ?line [ true = ((is_list(El) or is_binary(El))) || El <- L1], + ?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,very_icky_dir()))} + || {T,S,Targ} <- very_icky_dir(), T =:= symlink ], + ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], + ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ], + ?line chk_cre_dir(Mod,[{directory,[1088,1079,1091]++"_dir",very_icky_dir()}]), + ?line {ok,BeginAt} = Mod:get_cwd(), + ?line true = is_list(BeginAt), + ?line {error,enoent} = Mod:set_cwd("��_dir"), + ?line ok = Mod:set_cwd([1088,1079,1091]++"_dir"), + ?line {ok, NowAt} = Mod:get_cwd(), + ?line true = is_list(NowAt), + ?line true = BeginAt =/= NowAt, + ?line ok = Mod:set_cwd(".."), + ?line {ok,BeginAt} = Mod:get_cwd(), + ?line rm_r2(Mod,[1088,1079,1091]++"_dir"), + + case has_links() of + true -> + ?line ok = Mod:make_link("fil1","nisse"++[1088,1079,1091]), + ?line {ok, <<"fil1">>} = + Mod:read_file("nisse"++[1088,1079,1091]), + ?line {ok, #file_info{type = regular}} = + Mod:read_link_info("nisse"++[1088,1079,1091]), + ?line ok = Mod:delete("nisse"++[1088,1079,1091]), + ?line ok = Mod:make_link("fil1",<<"nisse�">>), + ?line {ok, <<"fil1">>} = Mod:read_file(<<"nisse�">>), + ?line {ok, #file_info{type = regular}} = + Mod:read_link_info(<<"nisse�">>), + ?line ok = Mod:delete(<<"nisse�">>), + ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), + ?line {error,enoent} = Mod:read_file("nisse"++[1088,1079,1091]), + ?line {error,enoent} = Mod:read_link_info("nisse"++[1088,1079,1091]), + ?line {error,enoent} = Mod:read_file(<<"nisse�">>), + ?line {error,enoent} = Mod:read_link_info(<<"nisse�">>); + false -> + ok + end, + ?line [ begin + ?line {ok, FD} = Mod:open(Name,[read]), + ?line {ok, Content} = Mod:read(FD,1024), + ?line ok = file:close(FD) + end || {regular,Name,Content} <- very_icky_dir() ], + ?line [ begin + ?line {ok, FD} = Mod:open(Name,[read,binary]), + ?line BC = list_to_binary([Content]), + ?line {ok, BC} = Mod:read(FD,1024), + ?line ok = file:close(FD) + end || {regular,Name,Content} <- very_icky_dir() ], + ?line Mod:rename([956,965,963,954,959,49], + [956,965,963,954,959]++"_fil1"), + ?line {ok, <<"���2">>} = Mod:read_file([956,965,963,954,959]++"_fil1"), + ?line {error,enoent} = Mod:read_file([956,965,963,954,959,49]), + ?line Mod:rename([956,965,963,954,959]++"_fil1",[956,965,963,954,959,49]), + ?line {ok, <<"���2">>} = Mod:read_file([956,965,963,954,959,49]), + ?line {error,enoent} = Mod:read_file([956,965,963,954,959]++"_fil1"), + + ?line {ok,FI} = Mod:read_file_info([956,965,963,954,959,49]), + ?line NewMode = FI#file_info.mode band (bnot 8#333), + ?line NewMode2 = NewMode bor 8#222, + ?line true = NewMode2 =/= NewMode, + ?line ok = Mod:write_file_info([956,965,963,954,959,49], + FI#file_info{mode = NewMode}), + ?line {ok,#file_info{mode = NewMode}} = + Mod:read_file_info([956,965,963,954,959,49]), + ?line ok = Mod:write_file_info([956,965,963,954,959,49], + FI#file_info{mode = NewMode2}), + ?line {ok,#file_info{mode = NewMode2}} = + Mod:read_file_info([956,965,963,954,959,49]), + ?line NumOK0 = case has_links() of + true -> 5; + false -> 3 + end, + ?line NumNOK0 = case has_links() of + true -> 4; + false -> 3 + end, + ?line {NumOK,NumNOK} = case is_binary(treat_icky(<<"foo">>)) of + false -> + {NumOK0+NumNOK0,0}; + true -> + {NumOK0,NumNOK0} + end, + ?line {NumOK,NumNOK} = filelib:fold_files(".",".*",true,fun(_F,{N,M}) when is_list(_F) -> io:format("~ts~n",[_F]),{N+1,M}; (_F,{N,M}) -> io:format("~p~n",[_F]),{N,M+1} end,{0,0}), + ?line ok = filelib:fold_files(".",[1076,1089,1072,124,46,42],true,fun(_F,_) -> ok end,false), + ?line SF3 = unicode:characters_to_binary("���subfil3",file:native_name_encoding()), + ?line Sorted = lists:sort([SF3,<<"���subfil2">>]), + ?line Sorted = lists:sort(filelib:wildcard("*",<<"���subdir2">>)), + ok + catch + throw:need_unicode_mode -> + io:format("Sorry, can only run in unicode mode.~n"), + need_unicode_mode + after + Mod:set_cwd(Dir), + io:format("Wd now: ~s~n",[Dir]) + end. + +%% +%% Utilities +%% + + +rm_rf(Mod,Dir) -> + case Mod:read_link_info(Dir) of + {ok, #file_info{type = directory}} -> + {ok, Content} = Mod:list_dir(Dir), + [ rm_rf(Mod,filename:join(Dir,C)) || C <- Content ], + Mod:del_dir(Dir), + ok; + {ok, #file_info{}} -> + Mod:delete(Dir); + _ -> + ok + end. + +rm_r(Mod,Dir) -> + %erlang:display({rm_r,Dir}), + case Mod:read_link_info(Dir) of + {ok, #file_info{type = directory}} -> + {ok,#file_info{type = directory}} = Mod:read_file_info(Dir), + {ok, Content} = Mod:list_dir(Dir), + [ true = is_list(Part) || Part <- Content ], + [ true = is_list(filename:join(Dir,Part)) || Part <- Content ], + [ rm_r(Mod,filename:join(Dir,C)) || C <- Content ], + ok = Mod:del_dir(Dir), + ok; + {ok, #file_info{type = regular}} -> + {ok,#file_info{type = regular}} = Mod:read_file_info(Dir), + ok = Mod:delete(Dir); + {ok, #file_info{type = symlink}} -> + ok = Mod:delete(Dir) + end. +%% For icky test, allow binaries sometimes +rm_r2(Mod,Dir) -> + %erlang:display({rm_r2,Dir}), + case Mod:read_link_info(Dir) of + {ok, #file_info{type = directory}} -> + {ok,#file_info{type = directory}} = Mod:read_file_info(Dir), + {ok, Content} = Mod:list_dir(Dir), + UniMode = file:native_name_encoding() =/= latin1, + [ true = (is_list(Part) orelse UniMode) || Part <- Content ], + [ true = (is_list(filename:join(Dir,Part)) orelse UniMode) || Part <- Content ], + [ rm_r2(Mod,filename:join(Dir,C)) || C <- Content ], + ok = Mod:del_dir(Dir), + ok; + {ok, #file_info{type = regular}} -> + {ok,#file_info{type = regular}} = Mod:read_file_info(Dir), + ok = Mod:delete(Dir); + {ok, #file_info{type = symlink}} -> + ok = Mod:delete(Dir) + end. +chk_cre_dir(_,[]) -> + ok; +chk_cre_dir(Mod,[{regular,Name,Content}|T]) -> + %io:format("~p~n",[Name]), + ok = Mod:write_file(Name,Content), + chk_cre_dir(Mod,T); +chk_cre_dir(Mod,[{link,Name,Target}|T]) -> + ok = Mod:make_link(Target,Name), + chk_cre_dir(Mod,T); +chk_cre_dir(Mod,[{symlink,Name,Target}|T]) -> + ok = Mod:make_symlink(Target,Name), + chk_cre_dir(Mod,T); +chk_cre_dir(Mod,[{directory,Name,Content}|T]) -> + ok = Mod:make_dir(Name), + %io:format("Content = ~p~n",[Content]), + Content2 = [{Ty,filename:join(Name,N),case Ty of link -> filename:join(Name,C); _ -> C end} || {Ty,N,C} <- Content ], + %io:format("Content2 = ~p~n",[Content2]), + chk_cre_dir(Mod,Content2), + chk_cre_dir(Mod,T). + +has_links() -> + case os:type() of + {win32,_} -> + case os:version() of + {N,NN,_} when (N > 5) andalso (NN >= 1) -> + true; + _ -> + false + end; + _ -> + true + end. + +make_normal_dir(Mod) -> + rm_rf(Mod,"normal_dir"), + Mod:make_dir("normal_dir"), + Mod:set_cwd("normal_dir"), + Mod:write_file("fil1","fil1"), + Mod:write_file("fil2","fil2"), + case has_links() of + true -> + Mod:make_link("fil2","fil3"), + Mod:make_symlink("fil2","fil4"); + _ -> + ok + end, + Mod:make_dir("subdir"), + Mod:write_file(filename:join("subdir","subfil1"),"subfil1"), + ok. + +normal_dir() -> + [{regular,"fil1","fil1"}, + {regular,"fil2","fil2"}] ++ + case has_links() of + true -> + [{regular,"fil3","fil2"}, + {symlink,"fil4","fil2"}]; + false -> + [] + end ++ + [{directory,"subdir", + [{regular,"subfil1","subfil1"}]}]. + +make_icky_dir(Mod) -> + rm_rf(Mod,"icky_dir"), + Icky=icky_dir(), + chk_cre_dir(Mod,[{directory,"icky_dir",linkify([],Icky)}]), + Mod:set_cwd("icky_dir"), + ok. + +linkify(_Passed,[]) -> + []; +linkify(Passed,[{regular,Name,Content}|T]) -> + Regulars = [ {N,C} || {regular,N,C} <- Passed, N =/= Name ], + case lists:keysearch(Content,2,Regulars) of + {value, {Linkto, Content}} -> + [{link,Name,Linkto} | linkify(Passed,T)]; + _ -> + [{regular,Name,Content} | linkify([{regular,Name,Content}|Passed],T)] + end; +linkify(Passed,[{directory, Name, Content}|T]) -> + [{directory,Name, linkify(Content,Content)}|linkify(Passed,T)]; +linkify(Passed,[H|T]) -> + [H|linkify([H|Passed],T)]. + +hopeless_darwin() -> + case {os:type(),os:version()} of + {{unix,darwin},{Major,_,_}} when Major < 9 -> + true; + _ -> + false + end. + +icky_dir() -> + [{regular,"fil1","fil1"}, + {regular,"���2","���2"}] ++ + case has_links() of + true -> + [{regular,"���3","���2"}, + {symlink,"���4","���2"}]; + false -> + [] + end ++ + [{regular,treat_icky(<<"���5">>),"���5"}] ++ + case has_links() of + true -> + [{symlink,treat_icky(<<"���6">>),treat_icky(<<"���5">>)}]; + false -> + [] + end ++ + [{directory,treat_icky(<<"���subdir2">>), + [{regular,treat_icky(<<"���subfil2">>),"���subfil12"}, + {regular,"���subfil3","���subfil13"}]}, + {directory,"���subdir", + [{regular,"���subfil1","���subfil1"}]}]. + +make_very_icky_dir(Mod) -> + rm_rf(Mod,"very_icky_dir"), + Icky=very_icky_dir(), + chk_cre_dir(Mod,[{directory,"very_icky_dir",linkify([],Icky)}]), + Mod:set_cwd("very_icky_dir"), + ok. + +very_icky_dir() -> + [{regular,"fil1","fil1"}, + {regular,[956,965,963,954,959,49],"���2"}] ++ + case has_links() of + true -> + [{regular,[956,965,963,954,959,50],"���2"}, + {symlink,[956,965,963,954,959,51],[956,965,963,954,959,49]}]; + false -> + [] + end ++ + [{regular,treat_icky(<<"���5">>),"���5"}] ++ + case has_links() of + true -> + [{symlink,treat_icky(<<"���6">>),treat_icky(<<"���5">>)}]; + false -> + [] + end ++ + [{directory,treat_icky(<<"���subdir2">>), + [{regular,treat_icky(<<"���subfil2">>),"���subfil12"}, + {regular,"���subfil3","���subfil13"}]}, + {directory,[956,965,963,954,959]++"subdir1", + [{regular,[956,965,963,954,959]++"subfil1","���subfil1"}]}]. + +%% Some OS'es simply do not allow non UTF8 filenames +treat_icky(Bin) -> + case os:type() of + {unix,darwin} -> + binary_to_list(procentify(Bin)); + {win32,_} -> + binary_to_list(Bin); + _ -> + Bin + end. + +% Handle windows having absolute soft link targets. +fixlink({ok,Link}) -> + case os:type() of + {win32,_} -> + {ok,filename:basename(Link)}; + _ -> + {ok,Link} + end; +fixlink(X) -> + X. + +procentify(<<>>) -> + <<>>; +procentify(<<X:8,Rst/binary>>) when X > 127 -> + T=procentify(Rst), + Y = list_to_binary([$% + | io_lib:format("~2.16B",[X])]), + <<Y/binary,T/binary>>; +procentify(<<X:8,Rst/binary>>) -> + T=procentify(Rst), + <<X:8,T/binary>>. + + +list([]) -> + []; +list([{_,Name,_} | T]) -> + [Name | list(T)]. + + +get_data(FN,List) -> + case lists:keysearch(FN,2,List) of + {value,{regular,FN,C}} -> + C; + {value,{symlink,FN,NewFN}} -> + get_data(NewFN,List); + _-> + [] + end. + + +convlist(L) -> + convlist(file:native_name_encoding(),L). +convlist(latin1,[Bin|T]) when is_binary(Bin) -> + %erlang:display('Convert...'), + [binary_to_list(Bin)| convlist(latin1,T)]; +convlist(Any,[H|T]) -> + [H|convlist(Any,T)]; +convlist(_,[]) -> + []. + +conv(L) -> + NoUniMode = file:native_name_encoding() =:= latin1, + if + NoUniMode, is_binary(L) -> + binary_to_list(L); + true -> + L + end. + + +rand_comp_decomp(Max) -> + N = random:uniform(Max), + L = [ rand_decomp() || _ <- lists:seq(1,N) ], + LC = [ A || {A,_} <- L], + LD = lists:flatten([B || {_,B} <- L]), + LB = unicode:characters_to_binary(LD,unicode,utf8), + {LC,LB}. + +rand_decomp() -> + BT = bigtup(), + SZ = tuple_size(BT), + element(random:uniform(SZ),BT). +bigtup() -> + {{192,[65,768]}, + {200,[69,768]}, + {204,[73,768]}, + {210,[79,768]}, + {217,[85,768]}, + {7808,[87,768]}, + {7922,[89,768]}, + {224,[97,768]}, + {232,[101,768]}, + {236,[105,768]}, + {242,[111,768]}, + {249,[117,768]}, + {7809,[119,768]}, + {7923,[121,768]}, + {8173,[168,768]}, + {7846,[65,770,768]}, + {7872,[69,770,768]}, + {7890,[79,770,768]}, + {7847,[97,770,768]}, + {7873,[101,770,768]}, + {7891,[111,770,768]}, + {7700,[69,772,768]}, + {7760,[79,772,768]}, + {7701,[101,772,768]}, + {7761,[111,772,768]}, + {7856,[65,774,768]}, + {7857,[97,774,768]}, + {475,[85,776,768]}, + {476,[117,776,768]}, + {8146,[953,776,768]}, + {8162,[965,776,768]}, + {8074,[913,837,787,768]}, + {8090,[919,837,787,768]}, + {8106,[937,837,787,768]}, + {8066,[945,837,787,768]}, + {8082,[951,837,787,768]}, + {8098,[969,837,787,768]}, + {7946,[913,787,768]}, + {7962,[917,787,768]}, + {7978,[919,787,768]}, + {7994,[921,787,768]}, + {8010,[927,787,768]}, + {8042,[937,787,768]}, + {7938,[945,787,768]}, + {7954,[949,787,768]}, + {7970,[951,787,768]}, + {7986,[953,787,768]}, + {8002,[959,787,768]}, + {8018,[965,787,768]}, + {8034,[969,787,768]}, + {8075,[913,837,788,768]}, + {8091,[919,837,788,768]}, + {8107,[937,837,788,768]}, + {8067,[945,837,788,768]}, + {8083,[951,837,788,768]}, + {8099,[969,837,788,768]}, + {7947,[913,788,768]}, + {7963,[917,788,768]}, + {7979,[919,788,768]}, + {7995,[921,788,768]}, + {8011,[927,788,768]}, + {8027,[933,788,768]}, + {8043,[937,788,768]}, + {7939,[945,788,768]}, + {7955,[949,788,768]}, + {7971,[951,788,768]}, + {7987,[953,788,768]}, + {8003,[959,788,768]}, + {8019,[965,788,768]}, + {8035,[969,788,768]}, + {7900,[79,795,768]}, + {7914,[85,795,768]}, + {7901,[111,795,768]}, + {7915,[117,795,768]}, + {8114,[945,837,768]}, + {8130,[951,837,768]}, + {8178,[969,837,768]}, + {8122,[913,768]}, + {8136,[917,768]}, + {8138,[919,768]}, + {8154,[921,768]}, + {8184,[927,768]}, + {8170,[933,768]}, + {8186,[937,768]}, + {8048,[945,768]}, + {8050,[949,768]}, + {8052,[951,768]}, + {8054,[953,768]}, + {8056,[959,768]}, + {8058,[965,768]}, + {8060,[969,768]}, + {8141,[8127,768]}, + {8157,[8190,768]}, + {193,[65,769]}, + {262,[67,769]}, + {201,[69,769]}, + {500,[71,769]}, + {205,[73,769]}, + {7728,[75,769]}, + {313,[76,769]}, + {7742,[77,769]}, + {323,[78,769]}, + {211,[79,769]}, + {7764,[80,769]}, + {340,[82,769]}, + {346,[83,769]}, + {218,[85,769]}, + {7810,[87,769]}, + {221,[89,769]}, + {377,[90,769]}, + {225,[97,769]}, + {263,[99,769]}, + {233,[101,769]}, + {501,[103,769]}, + {237,[105,769]}, + {7729,[107,769]}, + {314,[108,769]}, + {7743,[109,769]}, + {324,[110,769]}, + {243,[111,769]}, + {7765,[112,769]}, + {341,[114,769]}, + {347,[115,769]}, + {250,[117,769]}, + {7811,[119,769]}, + {253,[121,769]}, + {378,[122,769]}, + {8174,[168,769]}, + {508,[198,769]}, + {510,[216,769]}, + {509,[230,769]}, + {511,[248,769]}, + {7844,[65,770,769]}, + {7870,[69,770,769]}, + {7888,[79,770,769]}, + {7845,[97,770,769]}, + {7871,[101,770,769]}, + {7889,[111,770,769]}, + {7756,[79,771,769]}, + {7800,[85,771,769]}, + {7757,[111,771,769]}, + {7801,[117,771,769]}, + {7702,[69,772,769]}, + {7762,[79,772,769]}, + {7703,[101,772,769]}, + {7763,[111,772,769]}, + {7854,[65,774,769]}, + {7855,[97,774,769]}, + {7726,[73,776,769]}, + {471,[85,776,769]}, + {7727,[105,776,769]}, + {472,[117,776,769]}, + {8147,[953,776,769]}, + {8163,[965,776,769]}, + {506,[65,778,769]}, + {507,[97,778,769]}, + {8076,[913,837,787,769]}, + {8092,[919,837,787,769]}, + {8108,[937,837,787,769]}, + {8068,[945,837,787,769]}, + {8084,[951,837,787,769]}, + {8100,[969,837,787,769]}, + {7948,[913,787,769]}, + {7964,[917,787,769]}, + {7980,[919,787,769]}, + {7996,[921,787,769]}, + {8012,[927,787,769]}, + {8044,[937,787,769]}, + {7940,[945,787,769]}, + {7956,[949,787,769]}, + {7972,[951,787,769]}, + {7988,[953,787,769]}, + {8004,[959,787,769]}, + {8020,[965,787,769]}, + {8036,[969,787,769]}, + {8077,[913,837,788,769]}, + {8093,[919,837,788,769]}, + {8109,[937,837,788,769]}, + {8069,[945,837,788,769]}, + {8085,[951,837,788,769]}, + {8101,[969,837,788,769]}, + {7949,[913,788,769]}, + {7965,[917,788,769]}, + {7981,[919,788,769]}, + {7997,[921,788,769]}, + {8013,[927,788,769]}, + {8029,[933,788,769]}, + {8045,[937,788,769]}, + {7941,[945,788,769]}, + {7957,[949,788,769]}, + {7973,[951,788,769]}, + {7989,[953,788,769]}, + {8005,[959,788,769]}, + {8021,[965,788,769]}, + {8037,[969,788,769]}, + {7898,[79,795,769]}, + {7912,[85,795,769]}, + {7899,[111,795,769]}, + {7913,[117,795,769]}, + {7688,[67,807,769]}, + {7689,[99,807,769]}, + {8116,[945,837,769]}, + {8132,[951,837,769]}, + {8180,[959,837,769]}, + {8123,[913,769]}, + {8137,[917,769]}, + {8139,[919,769]}, + {8155,[921,769]}, + {8185,[927,769]}, + {8171,[933,769]}, + {8187,[937,769]}, + {8049,[945,769]}, + {8051,[949,769]}, + {8053,[951,769]}, + {8055,[953,769]}, + {8057,[959,769]}, + {8059,[965,769]}, + {8061,[969,769]}, + {1027,[1043,769]}, + {1036,[1050,769]}, + {1107,[1075,769]}, + {1116,[1082,769]}, + {8142,[8127,769]}, + {8158,[8190,769]}, + {194,[65,770]}, + {264,[67,770]}, + {202,[69,770]}, + {284,[71,770]}, + {292,[72,770]}, + {206,[73,770]}, + {308,[74,770]}, + {212,[79,770]}, + {348,[83,770]}, + {219,[85,770]}, + {372,[87,770]}, + {374,[89,770]}, + {7824,[90,770]}, + {226,[97,770]}, + {265,[99,770]}, + {234,[101,770]}, + {285,[103,770]}, + {293,[104,770]}, + {238,[105,770]}, + {309,[106,770]}, + {244,[111,770]}, + {349,[115,770]}, + {251,[117,770]}, + {373,[119,770]}, + {375,[121,770]}, + {7825,[122,770]}, + {7852,[65,803,770]}, + {7878,[69,803,770]}, + {7896,[79,803,770]}, + {7853,[97,803,770]}, + {7879,[101,803,770]}, + {7897,[111,803,770]}, + {195,[65,771]}, + {7868,[69,771]}, + {296,[73,771]}, + {209,[78,771]}, + {213,[79,771]}, + {360,[85,771]}, + {7804,[86,771]}, + {7928,[89,771]}, + {227,[97,771]}, + {7869,[101,771]}, + {297,[105,771]}, + {241,[110,771]}, + {245,[111,771]}, + {361,[117,771]}, + {7805,[118,771]}, + {7929,[121,771]}, + {7850,[65,770,771]}, + {7876,[69,770,771]}, + {7894,[79,770,771]}, + {7851,[97,770,771]}, + {7877,[101,770,771]}, + {7895,[111,770,771]}, + {7860,[65,774,771]}, + {7861,[97,774,771]}, + {7904,[79,795,771]}, + {7918,[85,795,771]}, + {7905,[111,795,771]}, + {7919,[117,795,771]}, + {256,[65,772]}, + {274,[69,772]}, + {7712,[71,772]}, + {298,[73,772]}, + {332,[79,772]}, + {362,[85,772]}, + {257,[97,772]}, + {275,[101,772]}, + {7713,[103,772]}, + {299,[105,772]}, + {333,[111,772]}, + {363,[117,772]}, + {482,[198,772]}, + {483,[230,772]}, + {480,[65,775,772]}, + {481,[97,775,772]}, + {478,[65,776,772]}, + {469,[85,776,772]}, + {479,[97,776,772]}, + {470,[117,776,772]}, + {7736,[76,803,772]}, + {7772,[82,803,772]}, + {7737,[108,803,772]}, + {7773,[114,803,772]}, + {492,[79,808,772]}, + {493,[111,808,772]}, + {8121,[913,772]}, + {8153,[921,772]}, + {8169,[933,772]}, + {8113,[945,772]}, + {8145,[953,772]}, + {8161,[965,772]}, + {1250,[1048,772]}, + {1262,[1059,772]}, + {1251,[1080,772]}, + {1263,[1091,772]}, + {258,[65,774]}, + {276,[69,774]}, + {286,[71,774]}, + {300,[73,774]}, + {334,[79,774]}, + {364,[85,774]}, + {259,[97,774]}, + {277,[101,774]}, + {287,[103,774]}, + {301,[105,774]}, + {335,[111,774]}, + {365,[117,774]}, + {7862,[65,803,774]}, + {7863,[97,803,774]}, + {7708,[69,807,774]}, + {7709,[101,807,774]}, + {8120,[913,774]}, + {8152,[921,774]}, + {8168,[933,774]}, + {8112,[945,774]}, + {8144,[953,774]}, + {8160,[965,774]}, + {1232,[1040,774]}, + {1238,[1045,774]}, + {1217,[1046,774]}, + {1049,[1048,774]}, + {1038,[1059,774]}, + {1233,[1072,774]}, + {1239,[1077,774]}, + {1218,[1078,774]}, + {1081,[1080,774]}, + {1118,[1091,774]}, + {7682,[66,775]}, + {266,[67,775]}, + {7690,[68,775]}, + {278,[69,775]}, + {7710,[70,775]}, + {288,[71,775]}, + {7714,[72,775]}, + {304,[73,775]}, + {7744,[77,775]}, + {7748,[78,775]}, + {7766,[80,775]}, + {7768,[82,775]}, + {7776,[83,775]}, + {7786,[84,775]}, + {7814,[87,775]}, + {7818,[88,775]}, + {7822,[89,775]}, + {379,[90,775]}, + {7683,[98,775]}, + {267,[99,775]}, + {7691,[100,775]}, + {279,[101,775]}, + {7711,[102,775]}, + {289,[103,775]}, + {7715,[104,775]}, + {7745,[109,775]}, + {7749,[110,775]}, + {7767,[112,775]}, + {7769,[114,775]}, + {7777,[115,775]}, + {7787,[116,775]}, + {7815,[119,775]}, + {7819,[120,775]}, + {7823,[121,775]}, + {380,[122,775]}, + {7835,[383,775]}, + {7780,[83,769,775]}, + {7781,[115,769,775]}, + {784,[774,775]}, + {7782,[83,780,775]}, + {7783,[115,780,775]}, + {7784,[83,803,775]}, + {7785,[115,803,775]}, + {196,[65,776]}, + {203,[69,776]}, + {7718,[72,776]}, + {207,[73,776]}, + {214,[79,776]}, + {220,[85,776]}, + {7812,[87,776]}, + {7820,[88,776]}, + {376,[89,776]}, + {228,[97,776]}, + {235,[101,776]}, + {7719,[104,776]}, + {239,[105,776]}, + {246,[111,776]}, + {7831,[116,776]}, + {252,[117,776]}, + {7813,[119,776]}, + {7821,[120,776]}, + {255,[121,776]}, + {1242,[399,776]}, + {1258,[415,776]}, + {1243,[601,776]}, + {1259,[629,776]}, + {7758,[79,771,776]}, + {7759,[111,771,776]}, + {7802,[85,772,776]}, + {7803,[117,772,776]}, + {938,[921,776]}, + {939,[933,776]}, + {970,[953,776]}, + {971,[965,776]}, + {980,[978,776]}, + {1031,[1030,776]}, + {1234,[1040,776]}, + {1025,[1045,776]}, + {1244,[1046,776]}, + {1246,[1047,776]}, + {1252,[1048,776]}, + {1254,[1054,776]}, + {1264,[1059,776]}, + {1268,[1063,776]}, + {1272,[1067,776]}, + {1235,[1072,776]}, + {1105,[1077,776]}, + {1245,[1078,776]}, + {1247,[1079,776]}, + {1253,[1080,776]}, + {1255,[1086,776]}, + {1265,[1091,776]}, + {1269,[1095,776]}, + {1273,[1099,776]}, + {1111,[1110,776]}, + {7842,[65,777]}, + {7866,[69,777]}, + {7880,[73,777]}, + {7886,[79,777]}, + {7910,[85,777]}, + {7926,[89,777]}, + {7843,[97,777]}, + {7867,[101,777]}, + {7881,[105,777]}, + {7887,[111,777]}, + {7911,[117,777]}, + {7927,[121,777]}, + {7848,[65,770,777]}, + {7874,[69,770,777]}, + {7892,[79,770,777]}, + {7849,[97,770,777]}, + {7875,[101,770,777]}, + {7893,[111,770,777]}, + {7858,[65,774,777]}, + {7859,[97,774,777]}, + {7902,[79,795,777]}, + {7916,[85,795,777]}, + {7903,[111,795,777]}, + {7917,[117,795,777]}, + {197,[65,778]}, + {366,[85,778]}, + {229,[97,778]}, + {367,[117,778]}, + {7832,[119,778]}, + {7833,[121,778]}, + {336,[79,779]}, + {368,[85,779]}, + {337,[111,779]}, + {369,[117,779]}, + {1266,[1059,779]}, + {1267,[1091,779]}, + {461,[65,780]}, + {268,[67,780]}, + {270,[68,780]}, + {282,[69,780]}, + {486,[71,780]}, + {463,[73,780]}, + {488,[75,780]}, + {317,[76,780]}, + {327,[78,780]}, + {465,[79,780]}, + {344,[82,780]}, + {352,[83,780]}, + {356,[84,780]}, + {467,[85,780]}, + {381,[90,780]}, + {462,[97,780]}, + {269,[99,780]}, + {271,[100,780]}, + {283,[101,780]}, + {487,[103,780]}, + {464,[105,780]}, + {496,[106,780]}, + {489,[107,780]}, + {318,[108,780]}, + {328,[110,780]}, + {466,[111,780]}, + {345,[114,780]}, + {353,[115,780]}, + {357,[116,780]}, + {468,[117,780]}, + {382,[122,780]}, + {494,[439,780]}, + {495,[658,780]}, + {473,[85,776,780]}, + {474,[117,776,780]}, + {901,[168,781]}, + {912,[953,776,781]}, + {944,[965,776,781]}, + {902,[913,781]}, + {904,[917,781]}, + {905,[919,781]}, + {906,[921,781]}, + {908,[927,781]}, + {910,[933,781]}, + {911,[937,781]}, + {940,[945,781]}, + {941,[949,781]}, + {942,[951,781]}, + {943,[953,781]}, + {972,[959,781]}, + {973,[965,781]}, + {974,[969,781]}, + {979,[978,781]}, + {512,[65,783]}, + {516,[69,783]}, + {520,[73,783]}, + {524,[79,783]}, + {528,[82,783]}, + {532,[85,783]}, + {513,[97,783]}, + {517,[101,783]}, + {521,[105,783]}, + {525,[111,783]}, + {529,[114,783]}, + {533,[117,783]}, + {1142,[1140,783]}, + {1143,[1141,783]}, + {514,[65,785]}, + {518,[69,785]}, + {522,[73,785]}, + {526,[79,785]}, + {530,[82,785]}, + {534,[85,785]}, + {515,[97,785]}, + {519,[101,785]}, + {523,[105,785]}, + {527,[111,785]}, + {531,[114,785]}, + {535,[117,785]}, + {8072,[913,837,787]}, + {8088,[919,837,787]}, + {8104,[937,837,787]}, + {8064,[945,837,787]}, + {8080,[951,837,787]}, + {8096,[969,837,787]}, + {7944,[913,787]}, + {7960,[917,787]}, + {7976,[919,787]}, + {7992,[921,787]}, + {8008,[927,787]}, + {8040,[937,787]}, + {7936,[945,787]}, + {7952,[949,787]}, + {7968,[951,787]}, + {7984,[953,787]}, + {8000,[959,787]}, + {8164,[961,787]}, + {8016,[965,787]}, + {8032,[969,787]}, + {8073,[913,837,788]}, + {8089,[919,837,788]}, + {8105,[937,837,788]}, + {8065,[945,837,788]}, + {8081,[951,837,788]}, + {8097,[969,837,788]}, + {7945,[913,788]}, + {7961,[917,788]}, + {7977,[919,788]}, + {7993,[921,788]}, + {8009,[927,788]}, + {8172,[929,788]}, + {8025,[933,788]}, + {8041,[937,788]}, + {7937,[945,788]}, + {7953,[949,788]}, + {7969,[951,788]}, + {7985,[953,788]}, + {8001,[959,788]}, + {8165,[961,788]}, + {8017,[965,788]}, + {8033,[969,788]}, + {416,[79,795]}, + {431,[85,795]}, + {417,[111,795]}, + {432,[117,795]}, + {7840,[65,803]}, + {7684,[66,803]}, + {7692,[68,803]}, + {7864,[69,803]}, + {7716,[72,803]}, + {7882,[73,803]}, + {7730,[75,803]}, + {7734,[76,803]}, + {7746,[77,803]}, + {7750,[78,803]}, + {7884,[79,803]}, + {7770,[82,803]}, + {7778,[83,803]}, + {7788,[84,803]}, + {7908,[85,803]}, + {7806,[86,803]}, + {7816,[87,803]}, + {7924,[89,803]}, + {7826,[90,803]}, + {7841,[97,803]}, + {7685,[98,803]}, + {7693,[100,803]}, + {7865,[101,803]}, + {7717,[104,803]}, + {7883,[105,803]}, + {7731,[107,803]}, + {7735,[108,803]}, + {7747,[109,803]}, + {7751,[110,803]}, + {7885,[111,803]}, + {7771,[114,803]}, + {7779,[115,803]}, + {7789,[116,803]}, + {7909,[117,803]}, + {7807,[118,803]}, + {7817,[119,803]}, + {7925,[121,803]}, + {7827,[122,803]}, + {7906,[79,795,803]}, + {7920,[85,795,803]}, + {7907,[111,795,803]}, + {7921,[117,795,803]}, + {7794,[85,804]}, + {7795,[117,804]}, + {7680,[65,805]}, + {7681,[97,805]}, + {199,[67,807]}, + {7696,[68,807]}, + {290,[71,807]}, + {7720,[72,807]}, + {310,[75,807]}, + {315,[76,807]}, + {325,[78,807]}, + {342,[82,807]}, + {350,[83,807]}, + {354,[84,807]}, + {231,[99,807]}, + {7697,[100,807]}, + {291,[103,807]}, + {7721,[104,807]}, + {311,[107,807]}, + {316,[108,807]}, + {326,[110,807]}, + {343,[114,807]}, + {351,[115,807]}, + {355,[116,807]}, + {260,[65,808]}, + {280,[69,808]}, + {302,[73,808]}, + {490,[79,808]}, + {370,[85,808]}, + {261,[97,808]}, + {281,[101,808]}, + {303,[105,808]}, + {491,[111,808]}, + {371,[117,808]}, + {7698,[68,813]}, + {7704,[69,813]}, + {7740,[76,813]}, + {7754,[78,813]}, + {7792,[84,813]}, + {7798,[85,813]}, + {7699,[100,813]}, + {7705,[101,813]}, + {7741,[108,813]}, + {7755,[110,813]}, + {7793,[116,813]}, + {7799,[117,813]}, + {7722,[72,814]}, + {7723,[104,814]}, + {7706,[69,816]}, + {7724,[73,816]}, + {7796,[85,816]}, + {7707,[101,816]}, + {7725,[105,816]}, + {7797,[117,816]}, + {7686,[66,817]}, + {7694,[68,817]}, + {7732,[75,817]}, + {7738,[76,817]}, + {7752,[78,817]}, + {7774,[82,817]}, + {7790,[84,817]}, + {7828,[90,817]}, + {7687,[98,817]}, + {7695,[100,817]}, + {7830,[104,817]}, + {7733,[107,817]}, + {7739,[108,817]}, + {7753,[110,817]}, + {7775,[114,817]}, + {7791,[116,817]}, + {7829,[122,817]}, + {8129,[168,834]}, + {8151,[953,776,834]}, + {8167,[965,776,834]}, + {8078,[913,837,787,834]}, + {8094,[919,837,787,834]}, + {8110,[937,837,787,834]}, + {8070,[945,837,787,834]}, + {8086,[951,837,787,834]}, + {8102,[969,837,787,834]}, + {7950,[913,787,834]}, + {7982,[919,787,834]}, + {7998,[921,787,834]}, + {8046,[937,787,834]}, + {7942,[945,787,834]}, + {7974,[951,787,834]}, + {7990,[953,787,834]}, + {8022,[965,787,834]}, + {8038,[969,787,834]}, + {8079,[913,837,788,834]}, + {8095,[919,837,788,834]}, + {8111,[937,837,788,834]}, + {8071,[945,837,788,834]}, + {8087,[951,837,788,834]}, + {8103,[969,837,788,834]}, + {7951,[913,788,834]}, + {7983,[919,788,834]}, + {7999,[921,788,834]}, + {8031,[933,788,834]}, + {8047,[937,788,834]}, + {7943,[945,788,834]}, + {7975,[951,788,834]}, + {7991,[953,788,834]}, + {8023,[965,788,834]}, + {8039,[969,788,834]}, + {8119,[945,837,834]}, + {8135,[951,837,834]}, + {8183,[969,837,834]}, + {8118,[945,834]}, + {8134,[951,834]}, + {8150,[953,834]}, + {8166,[965,834]}, + {8182,[969,834]}, + {8143,[8127,834]}, + {8159,[8190,834]}, + {8124,[913,837]}, + {8140,[919,837]}, + {8188,[937,837]}, + {8115,[945,837]}, + {8131,[951,837]}, + {8179,[969,837]}, + {64302,[1488,1463]}, + {64287,[1522,1463]}, + {64303,[1488,1464]}, + {64331,[1493,1465]}, + {64304,[1488,1468]}, + {64305,[1489,1468]}, + {64306,[1490,1468]}, + {64307,[1491,1468]}, + {64308,[1492,1468]}, + {64309,[1493,1468]}, + {64310,[1494,1468]}, + {64312,[1496,1468]}, + {64313,[1497,1468]}, + {64314,[1498,1468]}, + {64315,[1499,1468]}, + {64316,[1500,1468]}, + {64318,[1502,1468]}, + {64320,[1504,1468]}, + {64321,[1505,1468]}, + {64323,[1507,1468]}, + {64324,[1508,1468]}, + {64326,[1510,1468]}, + {64327,[1511,1468]}, + {64328,[1512,1468]}, + {64329,[1513,1468]}, + {64330,[1514,1468]}, + {64332,[1489,1471]}, + {64333,[1499,1471]}, + {64334,[1508,1471]}, + {64300,[1513,1468,1473]}, + {64298,[1513,1473]}, + {64301,[1513,1468,1474]}, + {64299,[1513,1474]}, + {2392,[2325,2364]}, + {2393,[2326,2364]}, + {2394,[2327,2364]}, + {2395,[2332,2364]}, + {2396,[2337,2364]}, + {2397,[2338,2364]}, + {2345,[2344,2364]}, + {2398,[2347,2364]}, + {2399,[2351,2364]}, + {2353,[2352,2364]}, + {2356,[2355,2364]}, + {2524,[2465,2492]}, + {2525,[2466,2492]}, + {2480,[2476,2492]}, + {2527,[2479,2492]}, + {2507,[2503,2494]}, + {2508,[2503,2519]}, + {2649,[2582,2620]}, + {2650,[2583,2620]}, + {2651,[2588,2620]}, + {2652,[2593,2620]}, + {2654,[2603,2620]}, + {2908,[2849,2876]}, + {2909,[2850,2876]}, + {2911,[2863,2876]}, + {2891,[2887,2878]}, + {2888,[2887,2902]}, + {2892,[2887,2903]}, + {3018,[3014,3006]}, + {3019,[3015,3006]}, + {2964,[2962,3031]}, + {3020,[3014,3031]}, + {3144,[3142,3158]}, + {3274,[3270,3266]}, + {3264,[3263,3285]}, + {3275,[3270,3266,3285]}, + {3271,[3270,3285]}, + {3272,[3270,3286]}, + {3402,[3398,3390]}, + {3403,[3399,3390]}, + {3404,[3398,3415]}, + {3635,[3661,3634]}, + {3763,[3789,3762]}, + {3955,[3954,3953]}, + {3957,[3956,3953]}, + {3959,[4018,3968,3953]}, + {3961,[4019,3968,3953]}, + {3958,[4018,3968]}, + {3960,[4019,3968]}, + {3945,[3904,4021]}, + {4025,[3984,4021]}, + {3907,[3906,4023]}, + {3917,[3916,4023]}, + {3922,[3921,4023]}, + {3927,[3926,4023]}, + {3932,[3931,4023]}, + {3987,[3986,4023]}, + {3997,[3996,4023]}, + {4002,[4001,4023]}, + {4007,[4006,4023]}, + {4012,[4011,4023]}, + {12436,[12358,12441]}, + {12364,[12363,12441]}, + {12366,[12365,12441]}, + {12368,[12367,12441]}, + {12370,[12369,12441]}, + {12372,[12371,12441]}, + {12374,[12373,12441]}, + {12376,[12375,12441]}, + {12378,[12377,12441]}, + {12380,[12379,12441]}, + {12382,[12381,12441]}, + {12384,[12383,12441]}, + {12386,[12385,12441]}, + {12389,[12388,12441]}, + {12391,[12390,12441]}, + {12393,[12392,12441]}, + {12400,[12399,12441]}, + {12403,[12402,12441]}, + {12406,[12405,12441]}, + {12409,[12408,12441]}, + {12412,[12411,12441]}, + {12446,[12445,12441]}, + {12532,[12454,12441]}, + {12460,[12459,12441]}, + {12462,[12461,12441]}, + {12464,[12463,12441]}, + {12466,[12465,12441]}, + {12468,[12467,12441]}, + {12470,[12469,12441]}, + {12472,[12471,12441]}, + {12474,[12473,12441]}, + {12476,[12475,12441]}, + {12478,[12477,12441]}, + {12480,[12479,12441]}, + {12482,[12481,12441]}, + {12485,[12484,12441]}, + {12487,[12486,12441]}, + {12489,[12488,12441]}, + {12496,[12495,12441]}, + {12499,[12498,12441]}, + {12502,[12501,12441]}, + {12505,[12504,12441]}, + {12508,[12507,12441]}, + {12535,[12527,12441]}, + {12536,[12528,12441]}, + {12537,[12529,12441]}, + {12538,[12530,12441]}, + {12542,[12541,12441]}, + {12401,[12399,12442]}, + {12404,[12402,12442]}, + {12407,[12405,12442]}, + {12410,[12408,12442]}, + {12413,[12411,12442]}, + {12497,[12495,12442]}, + {12500,[12498,12442]}, + {12503,[12501,12442]}, + {12506,[12504,12442]}, + {12509,[12507,12442]}}. diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index ace9501d18..eacf3c7584 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -204,8 +204,9 @@ evil(Config) when is_list(Config) -> evil_loop(Parent, ?EVIL_LOOPS,N) end) end, lists:seq(1, ?EVIL_PROCS)), - Devil = spawn(fun () -> devil(hd(Ps), hd(lists:reverse(Ps))) end), + Devil = spawn_link(fun () -> devil(hd(Ps), hd(lists:reverse(Ps))) end), lists:foreach(fun (P) -> receive {P, done} -> ok end end, Ps), + unlink(Devil), exit(Devil, kill), test_server:timetrap_cancel(Dog), ok. diff --git a/lib/mnesia/src/mnesia.appup.src b/lib/mnesia/src/mnesia.appup.src index 47c9bf9979..22ef5178a7 100644 --- a/lib/mnesia/src/mnesia.appup.src +++ b/lib/mnesia/src/mnesia.appup.src @@ -1,7 +1,13 @@ %% -*- erlang -*- {"%VSN%", [ + {"4.4.15",[ + {update, mnesia_dumper, soft, soft_purge, soft_purge, []} + ]} ], [ + {"4.4.15",[ + {update, mnesia_dumper, soft, soft_purge, soft_purge, []} + ]} ] }. diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl index f669d009c6..644133cf5d 100644 --- a/lib/mnesia/src/mnesia_dumper.erl +++ b/lib/mnesia/src/mnesia_dumper.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -643,7 +643,7 @@ insert_op(Tid, _, {op, create_table, TabDef}, InPlace, InitBy) -> true -> ignore; false -> mnesia_log:open_log(temp, - mnesia_log:dcl_log_header(), + mnesia_log:dcd_log_header(), Dcd, false, false, diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk index bce0f7b739..5b52bc6075 100644 --- a/lib/mnesia/vsn.mk +++ b/lib/mnesia/vsn.mk @@ -1 +1 @@ -MNESIA_VSN = 4.4.15 +MNESIA_VSN = 4.4.16 diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src index e95e542ff5..f1a370d925 100644 --- a/lib/odbc/src/odbc.appup.src +++ b/lib/odbc/src/odbc.appup.src @@ -1 +1,8 @@ -{"%VSN%", [],[]} +%% -*- erlang -*- +{"%VSN%", + [ + {"2.10.8", [{restart_application, ssl}]} + ], + [ + {"2.10.8", [{restart_application, ssl}]} + ]}. diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk index fac3f06d4b..aacf3924db 100644 --- a/lib/odbc/vsn.mk +++ b/lib/odbc/vsn.mk @@ -1 +1 @@ -ODBC_VSN = 2.10.8 +ODBC_VSN = 2.10.9 diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src index 0f9f62d2f6..6b6b76d0a5 100644 --- a/lib/public_key/src/public_key.appup.src +++ b/lib/public_key/src/public_key.appup.src @@ -1,6 +1,12 @@ %% -*- erlang -*- {"%VSN%", [ + {"0.9", + [ + {update, public_key, soft, soft_purge, soft_purge, []}, + {update, pubkey_cert, soft, soft_purge, soft_purge, []} + ] + }, {"0.8", [ {update, 'OTP-PUB-KEY', soft, soft_purge, soft_purge, []}, @@ -12,6 +18,12 @@ } ], [ + {"0.9", + [ + {update, public_key, soft, soft_purge, soft_purge, []}, + {update, pubkey_cert, soft, soft_purge, soft_purge, []} + ] + }, {"0.8", [ {update, 'OTP-PUB-KEY', soft, soft_purge, soft_purge, []}, diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk index 2810942171..334b9d792e 100644 --- a/lib/public_key/vsn.mk +++ b/lib/public_key/vsn.mk @@ -1 +1 @@ -PUBLIC_KEY_VSN = 0.9 +PUBLIC_KEY_VSN = 0.10 diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index f4e6b59b6d..51c5289bd2 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,9 +1,11 @@ %% -*- erlang -*- {"%VSN%", [ + {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ], [ + {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 30a0a3b3f7..ee692adb3b 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1,2 +1,2 @@ -SSL_VSN = 4.1 +SSL_VSN = 4.1.1 diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index 4ff3b22f32..969aff4fcb 100644 --- a/lib/stdlib/doc/src/filelib.xml +++ b/lib/stdlib/doc/src/filelib.xml @@ -36,14 +36,23 @@ <description> <p>This module contains utilities on a higher level than the <c>file</c> module.</p> + <p>The module supports Unicode file names, so that it will match against regular expressions given in Unicode and that it will find and process raw file names (i.e. files named in a way that does not confirm to the expected encoding).</p> + <p>If the VM operates in Unicode file naming mode on a machine with transparent file naming, the <c>fun()</c> provided to <c>fold_files/5</c> needs to be prepared to handle binary file names.</p> + <p>For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p> </description> <section> <title>DATA TYPES</title> <code type="none"> -filename() = string() | atom() | DeepList -dirname() = filename() -DeepList = [char() | atom() | DeepList]</code> +filename() = = string() | atom() | DeepList | RawFilename + DeepList = [char() | atom() | DeepList] + RawFilename = binary() + If VM is in unicode filename mode, string() and char() are allowed to be > 255. + RawFilename is a filename not subject to Unicode translation, meaning that it + can contain characters not conforming to the Unicode encoding expected from the + filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode + filename mode). +dirname() = filename()</code> </section> <funcs> @@ -90,6 +99,18 @@ DeepList = [char() | atom() | DeepList]</code> If <c>Recursive</c> is true all sub-directories to <c>Dir</c> are processed. The regular expression matching is done on just the filename without the directory part.</p> + + <p>If Unicode file name translation is in effect and the file + system is completely transparent, file names that cannot be + interpreted as Unicode may be encountered, in which case the + <c>fun()</c> must be prepared to handle raw file names + (i.e. binaries). If the regular expression contains + codepoints beyond 255, it will not match file names that does + not conform to the expected character encoding (i.e. are not + encoded in valid UTF-8).</p> + + <p>For more information about raw file names, see the + <seealso marker="kernel:file">file</seealso> module.</p> </desc> </func> <func> diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml index fe6c6f898e..cdee6e4a81 100644 --- a/lib/stdlib/doc/src/filename.xml +++ b/lib/stdlib/doc/src/filename.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -43,13 +43,22 @@ only, even if the arguments contain back slashes. Use <c>join/1</c> to normalize a file name by removing redundant directory separators.</p> + <p>The module supports raw file names in the way that if a binary is present, or the file name cannot be interpreted according to the return value of + <seealso marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>, a raw file name will also be returned. For example filename:join/1 provided with a path component being a binary (and also not being possible to interpret under the current native file name encoding) will result in a raw file name being returned (the join operation will have been performed of course). For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p> </description> <section> <title>DATA TYPES</title> <code type="none"> -name() = string() | atom() | DeepList -DeepList = [char() | atom() | DeepList]</code> +name() = string() | atom() | DeepList | RawFilename + DeepList = [char() | atom() | DeepList] + RawFilename = binary() + If VM is in unicode filename mode, string() and char() are allowed to be > 255. + RawFilename is a filename not subject to Unicode translation, meaning that it + can contain characters not conforming to the Unicode encoding expected from the + filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode + filename mode). + </code> </section> <funcs> <func> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index f1b0659ea2..c02ea3cbcb 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1999</year> - <year>2009</year> + <year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -168,6 +168,48 @@ Eshell V5.7 (abort with ^G) <image file="ushell2.gif"><icaption>Unicode characters in allowed and disallowed context</icaption></image> </section> <section> +<title>Unicode file names</title> +<p>Most modern operating systems support Unicode file names in some way or another. There are several different ways to do this and Erlang by default treats the different approaches differently:</p> +<taglist> +<tag>Mandatory Unicode file naming</tag> +<item> +<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem has names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows have selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in "Unicode file name translation mode", meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p> +<p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems may return Unicode lists with codepoints beyond 255, depending on the content of the actual filesystem.</p> +<p>As the feature is fairly new, you may still stumble upon non core applications that cannot handle being provided with file names containing characters with codepoints larger than 255, but the core Erlang system should have no problems with Unicode file names.</p> +</item> +<tag>Transparent file naming</tag> +<item> +<p>Most Unix operating systems have adopted a simpler approach, namely that Unicode file naming is not enforced, but by convention. Those systems usually use UTF-8 encoding for Unicode file names, but do not enforce it. On such a system, a file name containing characters having codepoints between 128 and 255 may be named either as plain ISO-latin-1 or using UTF-8 encoding. As no consistency is enforced, the Erlang VM can do no consistent translation of all file names. If the VM would automatically select encoding based on heuristics, one could get unexpected behavior on these systems, therefore file names not being encoded in UTF-8 are returned as "raw file names" if Unicode file naming support is turned on.</p> +<p>A raw file name is not a list, but a binary. Many non core applications still does not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, does not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p> +</item> +</taglist> +<p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it's supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the kernel and stdlib modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p> + +<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, where completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p> + +<p>For most systems, turning on Unicode file name translation is no problem even if it uses transparent file naming. Very few systems have mixed file name encodings. A consistent UTF-8 named system will work perfectly in Unicode file name mode. It is still however considered experimental in R14B01. Unicode file name translation is turned on with the <c>+fnu</c> switch to the <c>erl</c> program. If the VM is started in Unicode file name translation mode, <c>file:native_name_encoding/0</c> will return the atom <c>utf8</c>.</p> + +<p>In Unicode file name mode, file names given to the BIF <c>open_port/2</c> with the option <c>{spawn_executable,...}</c> are also interpreted as Unicode. So is the parameter list given in the <c>args</c> option available when using <c>spawn_executable</c>. The UTF-8 translation of arguments can be avoided using binaries, see the discussion about raw file names below.</p> + +<p>It is worth noting that the file <c>encoding</c> options given when opening a file has nothing to do with the file <em>name</em> encoding convention. You can very well open files containing UTF-8 but having file names in ISO-latin-1 or vice versa.</p> + +<note>Erlang drivers and NIF shared objects still can not be named with names containing codepoints beyond 127. This is a known limitation to be removed in a future release. Erlang modules however can, but it is definitely not a good idea and is still considered experimental.</note> + +<section> +<title>Notes about raw file names and automatic file name conversion</title> +<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason the "raw file names" is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 when to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named "bj�rn" in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and has the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named <<"bj�rn"/utf8>>. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named "bj�rn", one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as "raw" file names, i.e. as binaries.</p> +<p>The core system of Erlang (kernel and stdlib) accept raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p> +<p>To force Unicode file name translation mode on systems where this is not the default is considered experimental in OTP R14B01 due to the raw file names possibly being a new experience to the programmer and that the non core applications of OTP are not tested for compliance with raw file names yet. Unicode file name translation is expected to be default in future releases.</p> +<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, an VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The "UTF-8 list" is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p> +<p>Even if you are operating without Unicode file naming translation automatically done by the VM, you can access and create files with names in UTF-8 encoding by using raw file names encoded as UTF-8. Enforcing the UTF-8 encoding regardless of the mode the Erlang VM is started in might, in some circumstances be a good idea, as the convention of using UTF-8 file names is spreading.</p> +</section> +<section> +<title>Notes about MacOSX</title> +<p>MacOSXs vfs layer enforces UTF-8 file names in a quite aggressive way. Older versions did this by simply refusing to create non UTF-8 conforming file names, while newer versions replace offending bytes with the sequence "%HH", where HH is the original character in hexadecimal notation. As Unicode translation is enabled by default on MacOSX, the only way to come up against this is to either start the VM with the <c>+fnl</c> flag or to use a raw file name in <c>latin1</c> encoding. In that case, the file can not be opened with the same name as the one used to create this. The problem is by design in newer versions of MacOSX.</p> +<p>MacOSX also reorganizes the names of files so that the representation of accents etc is denormalized, i.e. the character <c>�</c> is represented as the codepoints [111,776], where 111 is the character <c>o</c> and 776 is a special accent character. This type of denormalized Unicode is otherwise very seldom used and Erlang normalizes those file names on retrieval, so that denormalized file names is not passed up to the Erlang application. In Erlang the file name "bj�rn" is retrieved as [98,106,246,114,110], not as [98,106,117,776,114,110], even though the file system might think differently.</p> +</section> +</section> +<section> <title>Unicode-aware modules</title> <p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p> <p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p> diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index 6d50a575eb..d04d8f191f 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -659,7 +659,7 @@ portformat(Name, Id, Cmd) -> pwd() -> case file:get_cwd() of {ok, Str} -> - ok = io:format("~s\n", [Str]); + ok = io:format("~ts\n", [fixup_one_bin(Str)]); {error, _} -> ok = io:format("Cannot determine current directory\n") end. @@ -684,11 +684,27 @@ ls() -> ls(Dir) -> case file:list_dir(Dir) of {ok, Entries} -> - ls_print(sort(Entries)); + ls_print(sort(fixup_bin(Entries))); {error,_E} -> format("Invalid directory\n") end. +fixup_one_bin(X) when is_binary(X) -> + L = binary_to_list(X), + [ if + El > 127 -> + $?; + true -> + El + end || El <- L]; +fixup_one_bin(X) -> + X. +fixup_bin([H|T]) -> + [fixup_one_bin(H) | fixup_bin(T)]; +fixup_bin([]) -> + []. + + ls_print([]) -> ok; ls_print(L) -> Width = min([max(lengths(L, [])), 40]) + 5, @@ -698,7 +714,7 @@ ls_print(X, Width, Len) when Width + Len >= 80 -> io:nl(), ls_print(X, Width, 0); ls_print([H|T], Width, Len) -> - io:format("~-*s",[Width,H]), + io:format("~-*ts",[Width,H]), ls_print(T, Width, Len+Width); ls_print([], _, _) -> io:nl(). diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index d5ddf9ed7e..c845b61204 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.erl @@ -47,14 +47,14 @@ wildcard(Pattern) when is_list(Pattern) -> ?HANDLE_ERROR(do_wildcard(Pattern, file)). -spec wildcard(file:name(), file:name() | atom()) -> [file:filename()]. -wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) -> +wildcard(Pattern, Cwd) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file)); wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) -> ?HANDLE_ERROR(do_wildcard(Pattern, Mod)). -spec wildcard(file:name(), file:name(), atom()) -> [file:filename()]. wildcard(Pattern, Cwd, Mod) - when is_list(Pattern), is_list(Cwd), is_atom(Mod) -> + when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)). -spec is_dir(file:name()) -> boolean(). @@ -118,7 +118,7 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) -> do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) -> do_wildcard_1([Base], Rest, Mod). -do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), is_list(Cwd) -> +do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) -> do_wildcard_comp(do_compile_wildcard(Pattern), Cwd, Mod). do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) -> @@ -127,9 +127,18 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) -> _ -> [] end; do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) -> - Cwd = filename:join([Cwd0]), %Slash away redundant slashes. - PrefixLen = length(Cwd)+1, - [lists:nthtail(PrefixLen, N) || N <- do_wildcard_1([Cwd], Rest, Mod)]; + {Cwd,PrefixLen} = case filename:join([Cwd0]) of + Bin when is_binary(Bin) -> {Bin,byte_size(Bin)+1}; + Other -> {Other,length(Other)+1} + end, %Slash away redundant slashes. + [ + if + is_binary(N) -> + <<_:PrefixLen/binary,Res/binary>> = N, + Res; + true -> + lists:nthtail(PrefixLen, N) + end || N <- do_wildcard_1([Cwd], Rest, Mod)]; do_wildcard_comp({compiled_wildcard,[Base|Rest]}, _Cwd, Mod) -> do_wildcard_1([Base], Rest, Mod). @@ -166,36 +175,44 @@ do_is_regular(File, Mod) -> %% If <Recursive> is true all sub-directories to <Dir> are processed do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) -> - {ok, Re1} = re:compile(RegExp), - do_fold_files1(Dir, Re1, Recursive, Fun, Acc, Mod). + {ok, Re1} = re:compile(RegExp,[unicode]), + do_fold_files1(Dir, Re1, RegExp, Recursive, Fun, Acc, Mod). -do_fold_files1(Dir, RegExp, Recursive, Fun, Acc, Mod) -> +do_fold_files1(Dir, RegExp, OrigRE, Recursive, Fun, Acc, Mod) -> case eval_list_dir(Dir, Mod) of - {ok, Files} -> do_fold_files2(Files, Dir, RegExp, Recursive, Fun, Acc, Mod); + {ok, Files} -> do_fold_files2(Files, Dir, RegExp, OrigRE, + Recursive, Fun, Acc, Mod); {error, _} -> Acc end. -do_fold_files2([], _Dir, _RegExp, _Recursive, _Fun, Acc, _Mod) -> +%% OrigRE is not to be compiled as it's for non conforming filenames, +%% i.e. for filenames that does not comply to the current encoding, which should +%% be very rare. We use it only in those cases and do not want to precompile. +do_fold_files2([], _Dir, _RegExp, _OrigRE, _Recursive, _Fun, Acc, _Mod) -> Acc; -do_fold_files2([File|T], Dir, RegExp, Recursive, Fun, Acc0, Mod) -> +do_fold_files2([File|T], Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod) -> FullName = filename:join(Dir, File), case do_is_regular(FullName, Mod) of true -> - case re:run(File, RegExp, [{capture,none}]) of + case (catch re:run(File, if is_binary(File) -> OrigRE; + true -> RegExp end, + [{capture,none}])) of match -> Acc = Fun(FullName, Acc0), - do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc, Mod); + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc, Mod); + {'EXIT',_} -> + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod); nomatch -> - do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod) + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod) end; false -> case Recursive andalso do_is_dir(FullName, Mod) of true -> - Acc1 = do_fold_files1(FullName, RegExp, Recursive, + Acc1 = do_fold_files1(FullName, RegExp, OrigRE, Recursive, Fun, Acc0, Mod), - do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc1, Mod); + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc1, Mod); false -> - do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod) + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod) end end. @@ -268,6 +285,13 @@ do_wildcard_3(Base, [Pattern|Rest], Result, Mod) -> do_wildcard_3(Base, [], Result, _Mod) -> [Base|Result]. +wildcard_4(Pattern, [File|Rest], Base, Result) when is_binary(File) -> + case wildcard_5(Pattern, binary_to_list(File)) of + true -> + wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]); + false -> + wildcard_4(Pattern, Rest, Base, Result) + end; wildcard_4(Pattern, [File|Rest], Base, Result) -> case wildcard_5(Pattern, File) of true -> diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index 01c06e4596..e38b8957f2 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -41,6 +41,9 @@ -include_lib("kernel/include/file.hrl"). +-define(IS_DRIVELETTER(Letter),(((Letter >= $A) andalso (Letter =< $Z)) orelse + ((Letter >= $a) andalso (Letter =< $z)))). + %% Converts a relative filename to an absolute filename %% or the filename itself if it already is an absolute filename %% Note that no attempt is made to create the most beatiful @@ -57,12 +60,18 @@ %% (for Unix) : absname("/") -> "/" %% (for WIN32): absname("/") -> "D:/" --spec absname(file:name()) -> string(). + +-spec absname(file:name()) -> file:filename(). absname(Name) -> {ok, Cwd} = file:get_cwd(), absname(Name, Cwd). --spec absname(file:name(), string()) -> string(). +-spec absname(file:name(), file:filename()) -> file:filename(). +absname(Name, AbsBase) when is_binary(Name), is_list(AbsBase) -> + absname(Name,filename_string_to_binary(AbsBase)); +absname(Name, AbsBase) when is_list(Name), is_binary(AbsBase) -> + absname(filename_string_to_binary(Name),AbsBase); + absname(Name, AbsBase) -> case pathtype(Name) of relative -> @@ -77,6 +86,20 @@ absname(Name, AbsBase) -> %% Handles volumerelative names (on Windows only). +absname_vr([<<"/">>|Rest1], [Volume|_], _AbsBase) -> + %% Absolute path on current drive. + join([Volume|Rest1]); +absname_vr([<<X, $:>>|Rest1], [<<X,_/binary>>|_], AbsBase) -> + %% Relative to current directory on current drive. + absname(join(Rest1), AbsBase); +absname_vr([<<X, $:>>|Name], _, _AbsBase) -> + %% Relative to current directory on another drive. + Dcwd = + case file:get_cwd([X, $:]) of + {ok, Dir} -> filename_string_to_binary(Dir); + {error, _} -> <<X, $:, $/>> + end, + absname(join(Name), Dcwd); absname_vr(["/"|Rest1], [Volume|_], _AbsBase) -> %% Absolute path on current drive. join([Volume|Rest1]); @@ -92,41 +115,13 @@ absname_vr([[X, $:]|Name], _, _AbsBase) -> end, absname(join(Name), Dcwd). -%% Joins a relative filename to an absolute base. For VxWorks the -%% resulting name is fixed to minimize the length by collapsing -%% ".." directories. -%% For other systems this is just a join/2, but assumes that +%% Joins a relative filename to an absolute base. +%% This is just a join/2, but assumes that %% AbsBase must be absolute and Name must be relative. --spec absname_join(string(), file:name()) -> string(). +-spec absname_join(file:filename(), file:name()) -> file:filename(). absname_join(AbsBase, Name) -> - case major_os_type() of - vxworks -> - absname_pretty(AbsBase, split(Name), lists:reverse(split(AbsBase))); - _Else -> - join(AbsBase, flatten(Name)) - end. - -%% Handles absolute filenames for VxWorks - these are 'pretty-printed', -%% since a C function call chdir("/erlang/lib/../bin") really sets -%% cwd to '/erlang/lib/../bin' which also works, but the long term -%% effect is potentially not so good ... -%% -%% absname_pretty("../bin", "/erlang/lib") -> "/erlang/bin" -%% absname_pretty("../../../..", "/erlang") -> "/erlang" - -absname_pretty(Abspath, Relpath, []) -> - %% AbsBase _must_ begin with a vxworks device name - {device, _Rest, Dev} = vxworks_first(Abspath), - absname_pretty(Abspath, Relpath, [lists:reverse(Dev)]); -absname_pretty(_Abspath, [], AbsBase) -> - join(lists:reverse(AbsBase)); -absname_pretty(Abspath, [[$.]|Rest], AbsBase) -> - absname_pretty(Abspath, Rest, AbsBase); -absname_pretty(Abspath, [[$.,$.]|Rest], [_|AbsRest]) -> - absname_pretty(Abspath, Rest, AbsRest); -absname_pretty(Abspath, [First|Rest], AbsBase) -> - absname_pretty(Abspath, Rest, [First|AbsBase]). + join(AbsBase, flatten(Name)). %% Returns the part of the filename after the last directory separator, %% or the filename itself if it has no separators. @@ -136,12 +131,36 @@ absname_pretty(Abspath, [First|Rest], AbsBase) -> %% basename("/usr/foo/") -> "foo" (trailing slashes ignored) %% basename("/") -> [] --spec basename(file:name()) -> string(). +-spec basename(file:name()) -> file:filename(). +basename(Name) when is_binary(Name) -> + case os:type() of + {win32,_} -> + win_basenameb(Name); + _ -> + basenameb(Name,[<<"/">>]) + end; + basename(Name0) -> Name = flatten(Name0), {DirSep2, DrvSep} = separators(), basename1(skip_prefix(Name, DrvSep), [], DirSep2). +win_basenameb(<<Letter,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter) -> + basenameb(Rest,[<<"/">>,<<"\\">>]); +win_basenameb(O) -> + basenameb(O,[<<"/">>,<<"\\">>]). +basenameb(Bin,Sep) -> + Parts = [ X || X <- binary:split(Bin,Sep,[global]), + X =/= <<>> ], + if + Parts =:= [] -> + <<>>; + true -> + lists:last(Parts) + end. + + + basename1([$/|[]], Tail, DirSep2) -> basename1([], Tail, DirSep2); basename1([$/|Rest], _Tail, DirSep2) -> @@ -155,26 +174,11 @@ basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) -> basename1([], Tail, _DirSep2) -> lists:reverse(Tail). -skip_prefix(Name, false) -> % No prefix for unix, but for VxWorks. - case major_os_type() of - vxworks -> - case vxworks_first(Name) of - {device, Rest, _Device} -> - Rest; - {not_device, _Rest, _First} -> - Name - end; - _Else -> - Name - end; -skip_prefix(Name, DrvSep) -> - skip_prefix1(Name, DrvSep). - -skip_prefix1([L, DrvSep|Name], DrvSep) when is_integer(L) -> +skip_prefix(Name, false) -> Name; -skip_prefix1([L], _) when is_integer(L) -> - [L]; -skip_prefix1(Name, _) -> +skip_prefix([L, DrvSep|Name], DrvSep) when ?IS_DRIVELETTER(L) -> + Name; +skip_prefix(Name, _) -> Name. %% Returns the last component of the filename, with the given @@ -190,7 +194,29 @@ skip_prefix1(Name, _) -> %% rootname(basename("xxx.jam")) -> "xxx" %% rootname(basename("xxx.erl")) -> "xxx" --spec basename(file:name(), file:name()) -> string(). +-spec basename(file:name(), file:name()) -> file:filename(). +basename(Name, Ext) when is_binary(Name), is_list(Ext) -> + basename(Name,filename_string_to_binary(Ext)); +basename(Name, Ext) when is_list(Name), is_binary(Ext) -> + basename(filename_string_to_binary(Name),Ext); +basename(Name, Ext) when is_binary(Name), is_binary(Ext) -> + BName = basename(Name), + LAll = byte_size(Name), + LN = byte_size(BName), + LE = byte_size(Ext), + case LN - LE of + Neg when Neg < 0 -> + BName; + Pos -> + StartLen = LAll - Pos - LE, + case Name of + <<_:StartLen/binary,Part:Pos/binary,Ext/binary>> -> + Part; + _Other -> + BName + end + end; + basename(Name0, Ext0) -> Name = flatten(Name0), Ext = flatten(Ext0), @@ -204,7 +230,7 @@ basename([$/|[]], Ext, Tail, DrvSep2) -> basename([], Ext, Tail, DrvSep2); basename([$/|Rest], Ext, _Tail, DrvSep2) -> basename(Rest, Ext, [], DrvSep2); -basename([$\\|Rest], Ext, Tail, DirSep2) when is_integer(DirSep2) -> +basename([DirSep2|Rest], Ext, Tail, DirSep2) when is_integer(DirSep2) -> basename([$/|Rest], Ext, Tail, DirSep2); basename([Char|Rest], Ext, Tail, DrvSep2) when is_integer(Char) -> basename(Rest, Ext, [Char|Tail], DrvSep2); @@ -216,21 +242,43 @@ basename([], _Ext, Tail, _DrvSep2) -> %% Example: dirname("/usr/src/kalle.erl") -> "/usr/src", %% dirname("kalle.erl") -> "." --spec dirname(file:name()) -> string(). +-spec dirname(file:name()) -> file:filename(). +dirname(Name) when is_binary(Name) -> + {Dsep,Drivesep} = separators(), + SList = case Dsep of + Sep when is_integer(Sep) -> + [ <<Sep>> ]; + _ -> + [] + end, + {XPart0,Dirs} = case Drivesep of + X when is_integer(X) -> + case Name of + <<DL,X,Rest/binary>> when ?IS_DRIVELETTER(DL) -> + {<<DL,X>>,Rest}; + _ -> + {<<>>,Name} + end; + _ -> + {<<>>,Name} + end, + Parts0 = binary:split(Dirs,[<<"/">>|SList],[global]), + %% Fairly short lists of parts, OK to reverse twice... + Parts = case Parts0 of + [] -> []; + _ -> lists:reverse(fstrip(tl(lists:reverse(Parts0)))) + end, + XPart = case {Parts,XPart0} of + {[],<<>>} -> + <<".">>; + _ -> + XPart0 + end, + dirjoin(Parts,XPart,<<"/">>); + dirname(Name0) -> Name = flatten(Name0), - case os:type() of - vxworks -> - {Devicep, Restname, FirstComp} = vxworks_first(Name), - case Devicep of - device -> - dirname(Restname, FirstComp, [], separators()); - _ -> - dirname(Name, [], [], separators()) - end; - _ -> - dirname(Name, [], [], separators()) - end. + dirname(Name, [], [], separators()). dirname([[_|_]=List|Rest], Dir, File, Seps) -> dirname(List++Rest, Dir, File, Seps); @@ -258,6 +306,26 @@ dirname([], [DrvSep,Dl], File, {_,DrvSep}) -> end; dirname([], Dir, _, _) -> lists:reverse(Dir). + +%% Compatibility with lists variant, remove trailing slashes +fstrip([<<>>,X|Y]) -> + fstrip([X|Y]); +fstrip(A) -> + A. + + +dirjoin([<<>>|T],Acc,Sep) -> + dirjoin1(T,<<Acc/binary,"/">>,Sep); +dirjoin(A,B,C) -> + dirjoin1(A,B,C). + +dirjoin1([],Acc,_) -> + Acc; +dirjoin1([One],Acc,_) -> + <<Acc/binary,One/binary>>; +dirjoin1([H|T],Acc,Sep) -> + dirjoin(T,<<Acc/binary,H/binary,Sep/binary>>,Sep). + %% Given a filename string, returns the file extension, %% including the period. Returns an empty list if there @@ -268,7 +336,31 @@ dirname([], Dir, _, _) -> %% %% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src" --spec extension(file:name()) -> string(). +-spec extension(file:name()) -> file:filename(). +extension(Name) when is_binary(Name) -> + {Dsep,_} = separators(), + SList = case Dsep of + Sep when is_integer(Sep) -> + [ <<Sep>> ]; + _ -> + [] + end, + case binary:matches(Name,[<<".">>]) of + nomatch -> % Bug in binary workaround :( + <<>>; + [] -> + <<>>; + List -> + {Pos,_} = lists:last(List), + <<_:Pos/binary,Part/binary>> = Name, + case binary:match(Part,[<<"/">>|SList]) of + nomatch -> + Part; + _ -> + <<>> + end + end; + extension(Name0) -> Name = flatten(Name0), extension(Name, [], major_os_type()). @@ -281,8 +373,6 @@ extension([$/|Rest], _Result, OsType) -> extension(Rest, [], OsType); extension([$\\|Rest], _Result, win32) -> extension(Rest, [], win32); -extension([$\\|Rest], _Result, vxworks) -> - extension(Rest, [], vxworks); extension([Char|Rest], Result, OsType) when is_integer(Char) -> extension(Rest, [Char|Result], OsType); extension([], Result, _OsType) -> @@ -290,23 +380,36 @@ extension([], Result, _OsType) -> %% Joins a list of filenames with directory separators. --spec join([string()]) -> string(). +-spec join([file:filename()]) -> file:filename(). join([Name1, Name2|Rest]) -> join([join(Name1, Name2)|Rest]); join([Name]) when is_list(Name) -> join1(Name, [], [], major_os_type()); +join([Name]) when is_binary(Name) -> + join1b(Name, <<>>, [], major_os_type()); join([Name]) when is_atom(Name) -> join([atom_to_list(Name)]). %% Joins two filenames with directory separators. --spec join(string(), string()) -> string(). +-spec join(file:filename(), file:filename()) -> file:filename(). join(Name1, Name2) when is_list(Name1), is_list(Name2) -> OsType = major_os_type(), case pathtype(Name2) of relative -> join1(Name1, Name2, [], OsType); _Other -> join1(Name2, [], [], OsType) end; +join(Name1, Name2) when is_binary(Name1), is_list(Name2) -> + join(Name1,filename_string_to_binary(Name2)); +join(Name1, Name2) when is_list(Name1), is_binary(Name2) -> + join(filename_string_to_binary(Name1),Name2); +join(Name1, Name2) when is_binary(Name1), is_binary(Name2) -> + OsType = major_os_type(), + case pathtype(Name2) of + relative -> join1b(Name1, Name2, [], OsType); + _Other -> join1b(Name2, <<>>, [], OsType) + end; + join(Name1, Name2) when is_atom(Name1) -> join(atom_to_list(Name1), Name2); join(Name1, Name2) when is_atom(Name2) -> @@ -321,8 +424,6 @@ when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z -> join1(Rest, RelativeName, [$:, UcLetter+$a-$A], win32); join1([$\\|Rest], RelativeName, Result, win32) -> join1([$/|Rest], RelativeName, Result, win32); -join1([$\\|Rest], RelativeName, Result, vxworks) -> - join1([$/|Rest], RelativeName, Result, vxworks); join1([$/|Rest], RelativeName, [$., $/|Result], OsType) -> join1(Rest, RelativeName, [$/|Result], OsType); join1([$/|Rest], RelativeName, [$/|Result], OsType) -> @@ -344,6 +445,26 @@ join1([Char|Rest], RelativeName, Result, OsType) when is_integer(Char) -> join1([Atom|Rest], RelativeName, Result, OsType) when is_atom(Atom) -> join1(atom_to_list(Atom)++Rest, RelativeName, Result, OsType). +join1b(<<UcLetter, $:, Rest/binary>>, RelativeName, [], win32) +when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z -> + join1b(Rest, RelativeName, [$:, UcLetter+$a-$A], win32); +join1b(<<$\\,Rest/binary>>, RelativeName, Result, win32) -> + join1b(<<$/,Rest/binary>>, RelativeName, Result, win32); +join1b(<<$/,Rest/binary>>, RelativeName, [$., $/|Result], OsType) -> + join1b(Rest, RelativeName, [$/|Result], OsType); +join1b(<<$/,Rest/binary>>, RelativeName, [$/|Result], OsType) -> + join1b(Rest, RelativeName, [$/|Result], OsType); +join1b(<<>>, <<>>, Result, OsType) -> + list_to_binary(maybe_remove_dirsep(Result, OsType)); +join1b(<<>>, RelativeName, [$:|Rest], win32) -> + join1b(RelativeName, <<>>, [$:|Rest], win32); +join1b(<<>>, RelativeName, [$/|Result], OsType) -> + join1b(RelativeName, <<>>, [$/|Result], OsType); +join1b(<<>>, RelativeName, Result, OsType) -> + join1b(RelativeName, <<>>, [$/|Result], OsType); +join1b(<<Char,Rest/binary>>, RelativeName, Result, OsType) when is_integer(Char) -> + join1b(Rest, RelativeName, [Char|Result], OsType). + maybe_remove_dirsep([$/, $:, Letter], win32) -> [Letter, $:, $/]; maybe_remove_dirsep([$/], _) -> @@ -357,7 +478,7 @@ maybe_remove_dirsep(Name, _) -> %% a given base directory, which is is assumed to be normalised %% by a previous call to join/{1,2}. --spec append(string(), file:name()) -> string(). +-spec append(file:filename(), file:name()) -> file:filename(). append(Dir, Name) -> Dir ++ [$/|Name]. @@ -376,19 +497,14 @@ append(Dir, Name) -> -spec pathtype(file:name()) -> 'absolute' | 'relative' | 'volumerelative'. pathtype(Atom) when is_atom(Atom) -> pathtype(atom_to_list(Atom)); -pathtype(Name) when is_list(Name) -> +pathtype(Name) when is_list(Name) or is_binary(Name) -> case os:type() of {unix, _} -> unix_pathtype(Name); - {win32, _} -> win32_pathtype(Name); - vxworks -> case vxworks_first(Name) of - {device, _Rest, _Dev} -> - absolute; - _ -> - relative - end; - {ose,_} -> unix_pathtype(Name) + {win32, _} -> win32_pathtype(Name) end. +unix_pathtype(<<$/,_/binary>>) -> + absolute; unix_pathtype([$/|_]) -> absolute; unix_pathtype([List|Rest]) when is_list(List) -> @@ -404,6 +520,15 @@ win32_pathtype([Atom|Rest]) when is_atom(Atom) -> win32_pathtype(atom_to_list(Atom)++Rest); win32_pathtype([Char, List|Rest]) when is_list(List) -> win32_pathtype([Char|List++Rest]); +win32_pathtype(<<$/, $/, _/binary>>) -> absolute; +win32_pathtype(<<$\\, $/, _/binary>>) -> absolute; +win32_pathtype(<<$/, $\\, _/binary>>) -> absolute; +win32_pathtype(<<$\\, $\\, _/binary>>) -> absolute; +win32_pathtype(<<$/, _/binary>>) -> volumerelative; +win32_pathtype(<<$\\, _/binary>>) -> volumerelative; +win32_pathtype(<<_Letter, $:, $/, _/binary>>) -> absolute; +win32_pathtype(<<_Letter, $:, $\\, _/binary>>) -> absolute; +win32_pathtype(<<_Letter, $:, _/binary>>) -> volumerelative; win32_pathtype([$/, $/|_]) -> absolute; win32_pathtype([$\\, $/|_]) -> absolute; win32_pathtype([$/, $\\|_]) -> absolute; @@ -422,7 +547,9 @@ win32_pathtype(_) -> relative. %% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle" %% rootname("/jam.src/foo.erl") -> "/jam.src/foo" --spec rootname(file:name()) -> string(). +-spec rootname(file:name()) -> file:filename(). +rootname(Name) when is_binary(Name) -> + list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128 rootname(Name0) -> Name = flatten(Name0), rootname(Name, [], [], major_os_type()). @@ -431,8 +558,6 @@ rootname([$/|Rest], Root, Ext, OsType) -> rootname(Rest, [$/]++Ext++Root, [], OsType); rootname([$\\|Rest], Root, Ext, win32) -> rootname(Rest, [$/]++Ext++Root, [], win32); -rootname([$\\|Rest], Root, Ext, vxworks) -> - rootname(Rest, [$/]++Ext++Root, [], vxworks); rootname([$.|Rest], Root, [], OsType) -> rootname(Rest, Root, ".", OsType); rootname([$.|Rest], Root, Ext, OsType) -> @@ -451,7 +576,13 @@ rootname([], Root, _Ext, _OsType) -> %% Examples: rootname("/jam.src/kalle.jam", ".erl") -> "/jam.src/kalle.jam" %% rootname("/jam.src/foo.erl", ".erl") -> "/jam.src/foo" --spec rootname(file:name(), file:name()) -> string(). +-spec rootname(file:name(), file:name()) -> file:filename(). +rootname(Name, Ext) when is_binary(Name), is_binary(Ext) -> + list_to_binary(rootname(binary_to_list(Name),binary_to_list(Ext))); +rootname(Name, Ext) when is_binary(Name) -> + rootname(Name,filename_string_to_binary(Ext)); +rootname(Name, Ext) when is_binary(Ext) -> + rootname(filename_string_to_binary(Name),Ext); rootname(Name0, Ext0) -> Name = flatten(Name0), Ext = flatten(Ext0), @@ -471,27 +602,55 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) -> %% split("foo/bar") -> ["foo", "bar"] %% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"] --spec split(file:name()) -> [string()]. +-spec split(file:name()) -> [file:filename()]. +split(Name) when is_binary(Name) -> + case os:type() of + {win32, _} -> win32_splitb(Name); + _ -> unix_splitb(Name) + end; + split(Name0) -> Name = flatten(Name0), case os:type() of - {unix, _} -> unix_split(Name); {win32, _} -> win32_split(Name); - vxworks -> vxworks_split(Name); - {ose,_} -> unix_split(Name) + _ -> unix_split(Name) end. -%% If a VxWorks filename starts with '[/\].*[^/\]' '[/\].*:' or '.*:' -%% that part of the filename is considered a device. -%% The rest of the name is interpreted exactly as for win32. -%% XXX - dirty solution to make filename:split([]) return the same thing on -%% VxWorks as on unix and win32. -vxworks_split([]) -> - []; -vxworks_split(L) -> - {_Devicep, Rest, FirstComp} = vxworks_first(L), - split(Rest, [], [lists:reverse(FirstComp)], win32). +unix_splitb(Name) -> + L = binary:split(Name,[<<"/">>],[global]), + LL = case L of + [<<>>|Rest] -> + [<<"/">>|Rest]; + _ -> + L + end, + [ X || X <- LL, X =/= <<>>]. + + +fix_driveletter(Letter0) -> + if + Letter0 >= $A, Letter0 =< $Z -> + Letter0+$a-$A; + true -> + Letter0 + end. +win32_splitb(<<Letter0,$:, Slash, Rest/binary>>) when (((Slash =:= $\\) orelse (Slash =:= $/)) andalso + ?IS_DRIVELETTER(Letter0)) -> + Letter = fix_driveletter(Letter0), + L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]), + [<<Letter,$:,$/>> | [ X || X <- L, X =/= <<>> ]]; +win32_splitb(<<Letter0,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter0) -> + Letter = fix_driveletter(Letter0), + L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]), + [<<Letter,$:>> | [ X || X <- L, X =/= <<>> ]]; +win32_splitb(<<Slash,Rest/binary>>) when ((Slash =:= $\\) orelse (Slash =:= $/)) -> + L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]), + [<<$/>> | [ X || X <- L, X =/= <<>> ]]; +win32_splitb(Name) -> + L = binary:split(Name,[<<"/">>,<<"\\">>],[global]), + [ X || X <- L, X =/= <<>> ]. + unix_split(Name) -> split(Name, [], unix). @@ -502,8 +661,6 @@ win32_split([X, $\\|Rest]) when is_integer(X) -> win32_split([X, $/|Rest]); win32_split([X, Y, $\\|Rest]) when is_integer(X), is_integer(Y) -> win32_split([X, Y, $/|Rest]); -win32_split([$/, $/|Rest]) -> - split(Rest, [], [[$/, $/]]); win32_split([UcLetter, $:|Rest]) when UcLetter >= $A, UcLetter =< $Z -> win32_split([UcLetter+$a-$A, $:|Rest]); win32_split([Letter, $:, $/|Rest]) -> @@ -540,7 +697,7 @@ split([], Comp, Components, OsType) -> %% will be converted to backslashes. On all platforms, the %% name will be normalized as done by join/1. --spec nativename(string()) -> string(). +-spec nativename(file:filename()) -> file:filename(). nativename(Name0) -> Name = join([Name0]), %Normalize. case os:type() of @@ -557,13 +714,12 @@ win32_nativename([]) -> separators() -> case os:type() of - {unix, _} -> {false, false}; {win32, _} -> {$\\, $:}; - vxworks -> {$\\, false}; - {ose,_} -> {false, false} + _ -> {false, false} end. + %% find_src(Module) -- %% find_src(Module, Rules) -- %% @@ -733,45 +889,12 @@ major_os_type() -> OsT -> OsT end. -%% Need to take care of the first pathname component separately -%% due to VxWorks less than good device naming rules. -%% (i.e. this is VxWorks specific ...) -%% The following four all starts with device names -%% elrond:/foo -> elrond: -%% elrond:\\foo.bar -> elrond: -%% /DISK1:foo -> /DISK1: -%% /usr/include -> /usr -%% This one doesn't: -%% foo/bar - -vxworks_first([]) -> - {not_device, [], []}; -vxworks_first([$/|T]) -> - vxworks_first2(device, T, [$/]); -vxworks_first([$\\|T]) -> - vxworks_first2(device, T, [$/]); -vxworks_first([H|T]) when is_list(H) -> - vxworks_first(H++T); -vxworks_first([H|T]) -> - vxworks_first2(not_device, T, [H]). - -vxworks_first2(Devicep, [], FirstComp) -> - {Devicep, [], FirstComp}; -vxworks_first2(Devicep, [$/|T], FirstComp) -> - {Devicep, [$/|T], FirstComp}; -vxworks_first2(Devicep, [$\\|T], FirstComp) -> - {Devicep, [$/|T], FirstComp}; -vxworks_first2(_Devicep, [$:|T], FirstComp)-> - {device, T, [$:|FirstComp]}; -vxworks_first2(Devicep, [H|T], FirstComp) when is_list(H) -> - vxworks_first2(Devicep, H++T, FirstComp); -vxworks_first2(Devicep, [H|T], FirstComp) -> - vxworks_first2(Devicep, T, [H|FirstComp]). - %% flatten(List) %% Flatten a list, also accepting atoms. --spec flatten(file:name()) -> string(). +-spec flatten(file:name()) -> file:filename(). +flatten(Bin) when is_binary(Bin) -> + Bin; flatten(List) -> do_flatten(List, []). @@ -785,3 +908,12 @@ do_flatten([], Tail) -> Tail; do_flatten(Atom, Tail) when is_atom(Atom) -> atom_to_list(Atom) ++ flatten(Tail). + +filename_string_to_binary(List) -> + case unicode:characters_to_binary(flatten(List),unicode,file:native_name_encoding()) of + {error,_,_} -> + erlang:error(badarg); + Bin when is_binary(Bin) -> + Bin + end. + diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl index 16ed9a2c26..e4cdcf6125 100644 --- a/lib/stdlib/test/binary_module_SUITE.erl +++ b/lib/stdlib/test/binary_module_SUITE.erl @@ -186,7 +186,7 @@ badargs(Config) when is_list(Config) -> binary:match(<<1,2,3>>, {ac,ets:match_spec_compile([{'_',[],['$_']}])}, [{scope,{0,1}}])), - ?line nomatch = + ?line [] = ?MASK_ERROR(binary:matches(<<1,2,3>>,<<1>>,[{scope,{0,0}}])), ?line badarg = ?MASK_ERROR(binary:matches(<<1,2,3>>,{bm,<<>>},[{scope,{0,1}}])), diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl index d54741051f..5a279609c6 100644 --- a/lib/stdlib/test/filelib_SUITE.erl +++ b/lib/stdlib/test/filelib_SUITE.erl @@ -53,8 +53,11 @@ wildcard_one(Config) when is_list(Config) -> wildcard_two(Config) when is_list(Config) -> ?line Dir = filename:join(?config(priv_dir, Config), "wildcard_two"), + ?line DirB = unicode:characters_to_binary(Dir, file:native_name_encoding()), ?line ok = file:make_dir(Dir), - ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir) end), + ?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,Dir, X = filelib:wildcard(Wc, Dir)}]),X end), + ?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,DirB, X = filelib:wildcard(Wc, DirB)}]), + [unicode:characters_to_list(Y,file:native_name_encoding()) || Y <- X] end), ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/") end), case os:type() of {win32,_} -> @@ -253,5 +256,7 @@ ensure_dir_eexist(Config) when is_list(Config) -> %% There already is a file with the name of the directory %% we want to create. ?line NeedFile = filename:join(Name, "file"), + ?line NeedFileB = filename:join(Name, <<"file">>), ?line {error, eexist} = filelib:ensure_dir(NeedFile), + ?line {error, eexist} = filelib:ensure_dir(NeedFileB), ok. diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl index ab6521f37b..dbce93600b 100644 --- a/lib/stdlib/test/filename_SUITE.erl +++ b/lib/stdlib/test/filename_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,12 +22,19 @@ basename_1/1, basename_2/1, dirname/1, extension/1, join/1, t_nativename/1]). -export([pathtype/1,rootname/1,split/1,find_src/1]). +-export([absname_bin/1, absname_bin_2/1, + basename_bin_1/1, basename_bin_2/1, + dirname_bin/1, extension_bin/1, join_bin/1]). +-export([pathtype_bin/1,rootname_bin/1,split_bin/1]). -include("test_server.hrl"). all(suite) -> [absname, absname_2, basename_1, basename_2, dirname, extension, - join, pathtype, rootname, split, t_nativename, find_src]. + join, pathtype, rootname, split, t_nativename, find_src, + absname_bin, absname_bin_2, basename_bin_1, basename_bin_2, dirname_bin, + extension_bin, + join_bin, pathtype_bin, rootname_bin, split_bin]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -457,3 +464,307 @@ find_src(Config) when is_list(Config) -> %% Try to find the source for a preloaded module. ?line {error,{preloaded,init}} = filename:find_src(init), ok. + +%% +%% +%% With binaries +%% +%% + +absname_bin(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + ?line [Drive|_] = ?config(priv_dir, Config), + ?line Temp = filename:join([Drive|":/"], "temp"), + ?line case file:make_dir(Temp) of + ok -> ok; + {error,eexist} -> ok + end, + ?line {ok,Cwd} = file:get_cwd(), + ?line ok = file:set_cwd(Temp), + ?line <<Drive:8,":/temp/foo">> = filename:absname(<<"foo">>), + ?line <<Drive:8,":/temp/../ebin">> = filename:absname(<<"../ebin">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\src">>), + ?line <<Drive:8,":/temp/erlang">> = filename:absname(<<Drive:8,":erlang">>), + ?line <<Drive:8,":/temp/erlang/src">> = + filename:absname(<<Drive:8,":erlang/src">>), + ?line <<Drive:8,":/temp/erlang/src">> = + filename:absname(<<Drive:8,":erlang\\src\\">>), + ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>), + + ?line file:set_cwd(<<Drive:8,":/">>), + ?line <<Drive:8,":/foo">> = filename:absname(<<"foo">>), + ?line <<Drive:8,":/../ebin">> = filename:absname(<<"../ebin">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\\\src">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<Drive:8,":erlang">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>), + ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>), + + ?line file:set_cwd(Cwd), + ok; + {unix, _} -> + ?line ok = file:set_cwd(<<"/usr">>), + ?line <<"/usr/foo">> = filename:absname(<<"foo">>), + ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>), + + ?line file:set_cwd(<<"/">>), + ?line <<"/foo">> = filename:absname(<<"foo">>), + ?line <<"/../ebin">> = filename:absname(<<"../ebin">>), + ?line <<"/erlang">> = filename:absname(<<"/erlang">>), + ?line <<"/erlang/src">> = filename:absname(<<"/erlang/src">>), + ?line <<"/erlang/src">> = filename:absname(<<"/erlang///src">>), + ok + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +absname_bin_2(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + ?line [Drive|_] = ?config(priv_dir, Config), + ?line <<Drive:8,":/temp/foo">> = filename:absname(<<"foo">>, <<Drive:8,":/temp">>), + ?line <<Drive:8,":/temp/../ebin">> = filename:absname(<<"../ebin">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>, <<Drive:8,":/temp">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\src">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/temp/erlang">> = filename:absname(<<Drive:8,":erlang">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/temp/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/temp/erlang/src">> = + filename:absname(<<Drive:8,":erlang\\src\\">>, <<Drive:8,":/temp">>), + ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/temp">>), + + ?line file:set_cwd(<<Drive:8,":/">>), + ?line <<Drive:8,":/foo">> = filename:absname(foo, <<Drive:8,":/">>), + ?line <<Drive:8,":/foo">> = filename:absname(<<"foo">>, <<Drive:8,":/">>), + ?line <<Drive:8,":/../ebin">> = filename:absname(<<"../ebin">>, <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>, <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>, + <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\\\src">>, + <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<Drive:8,":erlang">>, + <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>, + <<Drive:8,":/">>), + ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/">>), + + ok; + {unix, _} -> + ?line <<"/usr/foo">> = filename:absname(<<"foo">>, <<"/usr">>), + ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>, <<"/usr">>), + + ?line <<"/foo">> = filename:absname(<<"foo">>, <<"/">>), + ?line <<"/../ebin">> = filename:absname(<<"../ebin">>, <<"/">>), + ?line <<"/erlang">> = filename:absname(<<"/erlang">>, <<"/">>), + ?line <<"/erlang/src">> = filename:absname(<<"/erlang/src">>, <<"/">>), + ?line <<"/erlang/src">> = filename:absname(<<"/erlang///src">>, <<"/">>), + ok + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +basename_bin_1(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(10)), + ?line <<".">> = filename:basename(<<".">>), + ?line <<"foo">> = filename:basename(<<"foo">>), + ?line <<"foo">> = filename:basename(<<"/usr/foo">>), + ?line <<"foo.erl">> = filename:basename(<<"A:usr/foo.erl">>), + ?line case os:type() of + {win32, _} -> + ?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>), + ?line <<"foo">> = filename:basename(<<"A:foo">>); + {unix, _} -> + ?line <<"strange\\but\\true">> = + filename:basename(<<"strange\\but\\true">>) + end, + ?line test_server:timetrap_cancel(Dog), + ok. + +basename_bin_2(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(10)), + ?line <<".">> = filename:basename(<<".">>, <<".erl">>), + ?line <<"foo">> = filename:basename(<<"foo.erl">>, <<".erl">>), + ?line <<"foo.erl">> = filename:basename(<<"/usr/foo.erl">>, <<".hrl">>), + ?line <<"foo.erl">> = filename:basename(<<"/usr.hrl/foo.erl">>, <<".hrl">>), + ?line <<"foo">> = filename:basename(<<"/usr.hrl/foo">>, <<".hrl">>), + ?line <<"foo">> = filename:basename(<<"usr/foo/">>, <<".erl">>), + ?line <<"foo.erl">> = filename:basename(<<"usr/foo.erl/">>, <<".erl">>), + ?line case os:type() of + {win32, _} -> + ?line <<"foo">> = filename:basename(<<"A:foo">>, <<".erl">>), + ?line <<"foo.erl">> = filename:basename(<<"a:\\usr\\foo.erl">>, + <<".hrl">>), + ?line <<"foo.erl">> = filename:basename(<<"c:\\usr.hrl\\foo.erl">>, + <<".hrl">>), + ?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>, <<".hrl">>); + {unix, _} -> + ?line <<"strange\\but\\true">> = + filename:basename(<<"strange\\but\\true.erl">>, <<".erl">>), + ?line <<"strange\\but\\true">> = + filename:basename(<<"strange\\but\\true">>, <<".erl">>) + end, + ?line test_server:timetrap_cancel(Dog), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +dirname_bin(Config) when is_list(Config) -> + case os:type() of + {win32,_} -> + ?line <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>), + ?line <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>), + ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>), + ?line <<"/">> = filename:dirname(<<"\\usr">>), + ?line <<"A:">> = filename:dirname(<<"A:">>); + vxworks -> + ?line <<"net:/usr">> = filename:dirname(<<"net:/usr/foo.erl">>), + ?line <<"/disk0:/usr">> = filename:dirname(<<"/disk0:/usr/foo.erl">>), + ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>), + ?line <<"/usr">> = filename:dirname(<<"\\usr">>), + ?line <<"net:">> = filename:dirname(<<"net:">>); + _ -> true + end, + ?line <<"usr">> = filename:dirname(<<"usr///foo.erl">>), + ?line <<".">> = filename:dirname(<<"foo.erl">>), + ?line <<".">> = filename:dirname(<<".">>), + case os:type() of + vxworks -> + ?line <<"/">> = filename:dirname(<<"/">>), + ?line <<"/usr">> = filename:dirname(<<"/usr">>); + _ -> + ?line <<"/">> = filename:dirname(<<"/">>), + ?line <<"/">> = filename:dirname(<<"/usr">>) + end, + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +extension_bin(Config) when is_list(Config) -> + ?line <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>), + ?line <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>), + ?line <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>), + ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>), + ?line <<"">> = filename:extension(<<"A:/usr/foo">>), + ?line case os:type() of + {win32, _} -> + ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>), + ?line <<".erl">> = + filename:extension(<<"A:/usr.bar/foo.nisse.erl">>), + ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>), + ok; + vxworks -> + ?line <<"">> = filename:extension(<<"/disk0:\\usr\\foo">>), + ?line <<".erl">> = + filename:extension(<<"net:/usr.bar/foo.nisse.erl">>), + ?line <<"">> = filename:extension(<<"net:/usr.bar/foo">>), + ok; + _ -> ok + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +join_bin(Config) when is_list(Config) -> + ?line <<"/">> = filename:join([<<"/">>]), + ?line <<"/">> = filename:join([<<"//">>]), + ?line <<"usr/foo.erl">> = filename:join(<<"usr">>,<<"foo.erl">>), + ?line <<"/src/foo.erl">> = filename:join(usr, <<"/src/foo.erl">>), + ?line <<"/src/foo.erl">> = filename:join([<<"/src/">>,'foo.erl']), + ?line <<"/src/foo.erl">> = filename:join(<<"usr">>, ["/sr", 'c/foo.erl']), + ?line <<"/src/foo.erl">> = filename:join(<<"usr">>, <<"/src/foo.erl">>), + + %% Make sure that redundant slashes work too. + ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/////d//e/f/g">>]), + ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/">>, <<"d//e/f/g">>]), + ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"d//e/f/g">>]), + ?line <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"/d//e/f/g">>]), + ?line <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]), + + ?line <<"foo/bar">> = filename:join([$f,$o,$o,$/,[]], <<"bar">>), + + ?line case os:type() of + {win32, _} -> + ?line <<"d:/">> = filename:join([<<"D:/">>]), + ?line <<"d:/">> = filename:join([<<"D:\\">>]), + ?line <<"d:/abc">> = filename:join([<<"D:/">>, <<"abc">>]), + ?line <<"d:abc">> = filename:join([<<"D:">>, <<"abc">>]), + ?line <<"a/b/c/d/e/f/g">> = + filename:join([<<"a//b\\c//\\/\\d/\\e/f\\g">>]), + ?line <<"a:usr/foo.erl">> = + filename:join([<<"A:">>,<<"usr">>,<<"foo.erl">>]), + ?line <<"/usr/foo.erl">> = + filename:join([<<"A:">>,<<"/usr">>,<<"foo.erl">>]), + ?line <<"c:usr">> = filename:join(<<"A:">>,<<"C:usr">>), + ?line <<"a:usr">> = filename:join(<<"A:">>,<<"usr">>), + ?line <<"c:/usr">> = filename:join(<<"A:">>, <<"C:/usr">>), + ?line <<"c:/usr/foo.erl">> = + filename:join([<<"A:">>,<<"C:/usr">>,<<"foo.erl">>]), + ?line <<"c:usr/foo.erl">> = + filename:join([<<"A:">>,<<"C:usr">>,<<"foo.erl">>]), + ?line <<"d:/foo">> = filename:join([$D, $:, $/, []], <<"foo">>), + ok; + {unix, _} -> + ok + end. + +pathtype_bin(Config) when is_list(Config) -> + ?line relative = filename:pathtype(<<"..">>), + ?line relative = filename:pathtype(<<"foo">>), + ?line relative = filename:pathtype(<<"foo/bar">>), + ?line relative = filename:pathtype('foo/bar'), + case os:type() of + {win32, _} -> + ?line volumerelative = filename:pathtype(<<"/usr/local/bin">>), + ?line volumerelative = filename:pathtype(<<"A:usr/local/bin">>), + ok; + {unix, _} -> + ?line absolute = filename:pathtype(<<"/">>), + ?line absolute = filename:pathtype(<<"/usr/local/bin">>), + ok + end. + +rootname_bin(Config) when is_list(Config) -> + ?line <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>), + ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>), + ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>), + ?line <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>), + ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>), + ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>), + ok. + +split_bin(Config) when is_list(Config) -> + case os:type() of + vxworks -> + ?line [<<"/usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>); + _ -> + ?line [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>) + end, + ?line [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>), + ?line [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>), + case os:type() of + {win32,_} -> + ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + filename:split(<<"a:/msdev/include">>), + ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + filename:split(<<"A:/msdev/include">>), + ?line [<<"msdev">>,<<"include">>] = + filename:split(<<"msdev\\include">>), + ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + filename:split(<<"a:\\msdev\\include">>), + ?line [<<"a:">>,<<"msdev">>,<<"include">>] = + filename:split(<<"a:msdev\\include">>), + ok; + _ -> + ok + end. + diff --git a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc index 9d184152d1..3b9eaa309c 100644 --- a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc +++ b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc @@ -1,7 +1,7 @@ %%-*-erlang-*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -934,6 +934,13 @@ parse_att_value(?STRING_REST("&", Rest), State, Stop, Acc) -> parse_att_value(Rest1, State2, Stop, ParsedValue ++ Acc); {external_general, Name, _} -> ?fatal_error(State1, "External parsed entity reference in attribute value: " ++ Name); + {not_found, Name} -> + case State#xmerl_sax_parser_state.skip_external_dtd of + false -> + ?fatal_error(State1, "Entity not declared: " ++ Name); %%VC: Entity Declared + true -> + parse_att_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc) + end; {unparsed, Name, _} -> ?fatal_error(State1, "Unparsed entity reference in attribute value: " ++ Name) end; @@ -1098,6 +1105,13 @@ parse_content(?STRING_REST("&", Rest), State, Acc, _IgnorableWS) -> {external_general, _, {PubId, SysId}} -> State2 = parse_external_entity(State1, PubId, SysId), parse_content(Rest1, State2, Acc, false); + {not_found, Name} -> + case State#xmerl_sax_parser_state.skip_external_dtd of + false -> + ?fatal_error(State1, "Entity not declared: " ++ Name); %%VC: Entity Declared + true -> + parse_content(Rest1, State1, ";" ++ lists:reverse(Name) ++ "&" ++ Acc, false) + end; {unparsed, Name, _} -> ?fatal_error(State1, "Unparsed entity reference in content: " ++ Name) end; @@ -1357,7 +1371,7 @@ look_up_reference(Name, HaveToExist, State) -> yes -> ?fatal_error(State, "Entity not declared: " ++ Name); %%WFC: Entity Declared no -> - ?fatal_error(State, "Entity not declared: " ++ Name) %%VC: Entity Declared + {not_found, Name} %%VC: Entity Declared end; false -> {not_found, Name} @@ -1869,7 +1883,14 @@ parse_doctype_decl(?STRING_REST("%", Rest), State) -> parse_doctype_decl(?APPEND_STRING(IValue, Rest1), State1); {external_parameter, _, {PubId, SysId}} -> State2 = parse_external_entity(State1#xmerl_sax_parser_state{file_type = entity}, PubId, SysId), - parse_doctype_decl(Rest1, State2) + parse_doctype_decl(Rest1, State2); + {not_found, Name} -> + case State#xmerl_sax_parser_state.skip_external_dtd of + false -> + ?fatal_error(State1, "Entity not declared: " ++ Name); %%WFC: Entity Declared + true -> + parse_doctype_decl(Rest1, State1) + end end; parse_doctype_decl(?STRING_REST("<!", Rest1), State) -> parse_doctype_decl_1(Rest1, State); @@ -2443,7 +2464,7 @@ parse_ndata(Bytes, State) -> %% Acc = string() %% Result : {Value, Rest, State} %% Value = string() -%% Description: Parse an attribute value +%% Description: Parse an entity value %%---------------------------------------------------------------------- parse_entity_value(?STRING_EMPTY, State, undefined, Acc) -> {Acc, [], State}; %% stop clause when parsing references @@ -2473,7 +2494,7 @@ parse_entity_value(?STRING_REST("&", Rest), State, Stop, Acc) -> {external_general, Name, _} -> parse_entity_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc); {not_found, Name} -> - parse_entity_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc); + parse_entity_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc); {unparsed, Name, _} -> ?fatal_error(State1, "Unparsed entity reference in entity value: " ++ Name) end; @@ -2490,7 +2511,15 @@ parse_entity_value(?STRING_REST("%", Rest), #xmerl_sax_parser_state{file_type=Ty IValue = ?TO_INPUT_FORMAT(" " ++ RefValue ++ " "), parse_entity_value(?APPEND_STRING(IValue, Rest1), State1, Stop, Acc); {external_parameter, _, {_PubId, _SysId}} -> - ?fatal_error(State1, "Parameter references in entity value not supported yet.") + ?fatal_error(State1, "Parameter references in entity value not supported yet."); + {not_found, Name} -> + case State#xmerl_sax_parser_state.skip_external_dtd of + false -> + ?fatal_error(State1, "Entity not declared: " ++ Name); %%VC: Entity Declared + true -> + parse_entity_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc) + end + end end; parse_entity_value(?STRING_UNBOUND_REST(Stop, Rest), State, Stop, Acc) -> |