aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/doc/src/erl_driver.xml10
-rw-r--r--erts/doc/src/erl_nif.xml26
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl227
3 files changed, 146 insertions, 117 deletions
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 4fd74b783e..187c263b60 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -1067,7 +1067,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>ErlDrvBinary*</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
+ <name><ret>ErlDrvBinary *</ret><nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name>
<fsummary>Allocate a driver binary</fsummary>
<desc>
<marker id="driver_alloc_binary"></marker>
@@ -1087,7 +1087,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>ErlDrvBinary*</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name>
+ <name><ret>ErlDrvBinary *</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)</nametext></name>
<fsummary>Resize a driver binary</fsummary>
<desc>
<marker id="driver_realloc_binary"></marker>
@@ -1277,7 +1277,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>SysIOVec*</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name>
+ <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name>
<fsummary>Get the driver queue as a vector</fsummary>
<desc>
<marker id="driver_peekq"></marker>
@@ -1481,7 +1481,7 @@ typedef struct ErlIOVec {
</desc>
</func>
<func>
- <name><ret>char*</ret><nametext>erl_errno_id(int error)</nametext></name>
+ <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name>
<fsummary>Get erlang error atom name from error number</fsummary>
<desc>
<marker id="erl_errno_id"></marker>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 5fc6508aad..f484e9eaf7 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2011</year>
+ <year>2001</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -472,7 +472,7 @@ typedef enum {
</section>
<funcs>
- <func><name><ret>void*</ret><nametext>enif_alloc(size_t size)</nametext></name>
+ <func><name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name>
<fsummary>Allocate dynamic memory.</fsummary>
<desc><p>Allocate memory of <c>size</c> bytes. Return NULL if allocation failed.</p></desc>
</func>
@@ -489,7 +489,7 @@ typedef enum {
<p>Return true on success or false if allocation failed.</p>
</desc>
</func>
- <func><name><ret>ErlNifEnv*</ret><nametext>enif_alloc_env()</nametext></name>
+ <func><name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name>
<fsummary>Create a new environment</fsummary>
<desc><p>Allocate a new process independent environment. The environment can
be used to hold terms that is not bound to any process. Such terms can
@@ -499,7 +499,7 @@ typedef enum {
<p>Return pointer to the new environment.</p>
</desc>
</func>
- <func><name><ret>void*</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name>
+ <func><name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name>
<fsummary>Allocate a memory managed resource object</fsummary>
<desc><p>Allocate a memory managed resource object of type <c>type</c> and size <c>size</c> bytes.</p></desc>
</func>
@@ -522,7 +522,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_broadcast">erl_drv_cond_broadcast</seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifCond*</ret><nametext>enif_cond_create(char *name)</nametext></name>
+ <func><name><ret>ErlNifCond *</ret><nametext>enif_cond_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_cond_create">erl_drv_cond_create</seealso>.
</p></desc>
@@ -840,7 +840,7 @@ typedef enum {
<fsummary>Create an integer term from a long int</fsummary>
<desc><p>Create an integer term from a <c>long int</c>.</p></desc>
</func>
- <func><name><ret>unsigned char*</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
+ <func><name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name>
<fsummary>Allocate and create a new binary term</fsummary>
<desc><p>Allocate a binary of size <c>size</c> bytes and create an owning
term. The binary data is mutable until the calling NIF returns. This is a
@@ -951,7 +951,7 @@ typedef enum {
<fsummary>Create an integer term from an unsigned long int</fsummary>
<desc><p>Create an integer term from an <c>unsigned long int</c>.</p></desc>
</func>
- <func><name><ret>ErlNifMutex*</ret><nametext>enif_mutex_create(char *name)</nametext></name>
+ <func><name><ret>ErlNifMutex *</ret><nametext>enif_mutex_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">erl_drv_mutex_create</seealso>.
</p></desc>
@@ -976,7 +976,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_unlock">erl_drv_mutex_unlock</seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifResourceType*</ret><nametext>enif_open_resource_type(ErlNifEnv* env,
+ <func><name><ret>ErlNifResourceType *</ret><nametext>enif_open_resource_type(ErlNifEnv* env,
const char* module_str, const char* name,
ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext></name>
<fsummary>Create or takeover a resource type</fsummary>
@@ -1005,7 +1005,7 @@ typedef enum {
and <seealso marker="#upgrade">upgrade</seealso>.</p>
</desc>
</func>
- <func><name><ret>void*</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
+ <func><name><ret>void *</ret><nametext>enif_priv_data(ErlNifEnv* env)</nametext></name>
<fsummary>Get the private data of a NIF library</fsummary>
<desc><p>Return the pointer to the private data that was set by <c>load</c>,
<c>reload</c> or <c>upgrade</c>.</p>
@@ -1033,7 +1033,7 @@ typedef enum {
References made by <seealso marker="#enif_make_resource">enif_make_resource</seealso>
can only be removed by the garbage collector.</p></desc>
</func>
- <func><name><ret>ErlNifRWLock*</ret><nametext>enif_rwlock_create(char *name)</nametext></name>
+ <func><name><ret>ErlNifRWLock *</ret><nametext>enif_rwlock_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_create">erl_drv_rwlock_create</seealso>.
</p></desc>
@@ -1073,7 +1073,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock</seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifPid*</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
+ <func><name><ret>ErlNifPid *</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
<fsummary>Get the pid of the calling process.</fsummary>
<desc><p>Initialize the pid variable <c>*pid</c> to represent the
calling process. Return <c>pid</c>.</p></desc>
@@ -1129,7 +1129,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_join">erl_drv_thread_join </seealso>.
</p></desc>
</func>
- <func><name><ret>ErlNifThreadOpts*</ret><nametext>enif_thread_opts_create(char *name)</nametext></name>
+ <func><name><ret>ErlNifThreadOpts *</ret><nametext>enif_thread_opts_create(char *name)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_opts_create">erl_drv_thread_opts_create</seealso>.
</p></desc>
@@ -1154,7 +1154,7 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy</seealso>.
</p></desc>
</func>
- <func><name><ret>void*</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name>
+ <func><name><ret>void *</ret><nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name>
<fsummary></fsummary>
<desc><p>Same as <seealso marker="erl_driver#erl_drv_tsd_get">erl_drv_tsd_get</seealso>.
</p></desc>
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 14a7a2bf20..eb59cbe6f6 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2012. 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
@@ -824,16 +824,17 @@ prim_set_primary_archive(PS, undefined, undefined, undefined) ->
debug(PS2, {return, Res}),
{Res, PS2}
end;
-prim_set_primary_archive(PS, ArchiveFile, ArchiveBin, #file_info{} = FileInfo)
- when is_list(ArchiveFile), is_binary(ArchiveBin) ->
+prim_set_primary_archive(PS, ArchiveFile0, ArchiveBin, #file_info{} = FileInfo)
+ when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
%% Try the archive file
- debug(PS, {set_primary_archive, ArchiveFile, byte_size(ArchiveBin)}),
+ debug(PS, {set_primary_archive, ArchiveFile0, byte_size(ArchiveBin)}),
+ ArchiveFile = absname(ArchiveFile0),
{Res3, PS3} =
case PS#prim_state.primary_archive of
undefined ->
Fun =
- fun({Funny, _GI, _GB}, A) ->
- case Funny of
+ fun({Components, _GI, _GB}, A) ->
+ case Components of
["", "nibe", RevApp] -> % Reverse ebin
%% Collect ebin directories in archive
Ebin = reverse(RevApp) ++ "/ebin",
@@ -873,11 +874,11 @@ prim_get_file(PS, File) ->
{Res, PS};
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_get_file, ArchiveFile, FileInArchive}),
- FunnyFile = funny_split(FileInArchive, $/),
+ FileComponents = path_split(FileInArchive),
Fun =
- fun({Funny, _GetInfo, GetBin}, Acc) ->
+ fun({Components, _GetInfo, GetBin}, Acc) ->
if
- Funny =:= FunnyFile ->
+ Components =:= FileComponents ->
{false, {ok, GetBin()}};
true ->
{true, Acc}
@@ -900,11 +901,11 @@ prim_list_dir(PS, Dir) ->
{Res, PS};
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_list_dir, ArchiveFile, FileInArchive}),
- FunnyDir = funny_split(FileInArchive, $/),
+ DirComponents = path_split(FileInArchive),
Fun =
- fun({Funny, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
- case Funny of
- [RevName | FD] when FD =:= FunnyDir ->
+ fun({Components, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
+ case Components of
+ [RevName | DC] when DC =:= DirComponents ->
case RevName of
"" ->
%% The listed directory
@@ -914,16 +915,16 @@ prim_list_dir(PS, Dir) ->
Name = reverse(RevName),
{true, {Status, [Name | Names]}}
end;
- ["", RevName | FD] when FD =:= FunnyDir ->
+ ["", RevName | DC] when DC =:= DirComponents ->
%% Directory
Name = reverse(RevName),
{true, {Status, [Name | Names]}};
- [RevName] when FunnyDir =:= [""] ->
- %% Top file
+ [RevName] when DirComponents =:= [""] ->
+ %% File in top directory
Name = reverse(RevName),
{true, {ok, [Name | Names]}};
- ["", RevName] when FunnyDir =:= [""] ->
- %% Top file
+ ["", RevName] when DirComponents =:= [""] ->
+ %% Directory in top directory
Name = reverse(RevName),
{true, {ok, [Name | Names]}};
_ ->
@@ -962,15 +963,14 @@ prim_read_file_info(PS, File) ->
end;
{archive, ArchiveFile, FileInArchive} ->
debug(PS, {archive_read_file_info, File}),
- FunnyFile = funny_split(FileInArchive, $/),
+ FileComponents = path_split(FileInArchive),
Fun =
- fun({Funny, GetInfo, _GetBin}, Acc) ->
- case Funny of
- [H | T] when H =:= "",
- T =:= FunnyFile ->
+ fun({Components, GetInfo, _GetBin}, Acc) ->
+ case Components of
+ ["" | F] when F =:= FileComponents ->
%% Directory
{false, {ok, GetInfo()}};
- F when F =:= FunnyFile ->
+ F when F =:= FileComponents ->
%% Plain file
{false, {ok, GetInfo()}};
_ ->
@@ -1063,50 +1063,69 @@ open_archive(Archive, Acc, Fun) ->
{error, Reason}
end.
+%% Open the given archive and iterate through all files with an own
+%% wrapper fun in order to identify each file as a component list as
+%% returned from path_split/1.
+%%
+%% In the archive (zip) file, directory elements might or might not be
+%% present. To ensure consistency, a directory element is added if it
+%% does not already exist (ensure_virual_dir/6). NOTE that there will
+%% be no such directory element for the top directory of the archive.
open_archive(Archive, FileInfo, Acc, Fun) ->
FakeFI = FileInfo#file_info{type = directory},
Wrapper =
- fun({N, GI, GB}, {A, I, FunnyDirs}) -> % Full iteration at open
- Funny = funny_split(N, $/),
- FunnyDirs2 =
- case Funny of
- ["" | FunnyDir] ->
- [FunnyDir | FunnyDirs];
+ fun({N, GI, GB}, {A, I, Dirs}) ->
+ Components = path_split(N),
+ Dirs2 =
+ case Components of
+ ["" | Dir] ->
+ %% This is a directory
+ [Dir | Dirs];
_ ->
- FunnyDirs
+ %% This is a regular file
+ Dirs
end,
- {Includes, FunnyDirs3, A2} =
- ensure_virtual_dirs(Funny, Fun, FakeFI, [{true, Funny}], FunnyDirs2, A),
- {_Continue, A3} = Fun({Funny, GI, GB}, A2),
- {true, Includes, {A3, I, FunnyDirs3}}
+ {Includes, Dirs3, A2} =
+ ensure_virtual_dirs(Components, Fun, FakeFI,
+ [{true, Components}], Dirs2, A),
+ {_Continue, A3} = Fun({Components, GI, GB}, A2),
+ {true, Includes, {A3, I, Dirs3}}
end,
prim_zip:open(Wrapper, {Acc, FakeFI, []}, Archive).
-ensure_virtual_dirs(Funny, Fun, FakeFI, Includes, FunnyDirs, Acc) ->
- case Funny of
- [_ | FunnyDir] ->
- case lists:member(FunnyDir, FunnyDirs) of % BIF
+ensure_virtual_dirs(Components, Fun, FakeFI, Includes, Dirs, Acc) ->
+ case Components of
+ [_] ->
+ %% Don't add virtual dir for top directory
+ {Includes, Dirs, Acc};
+ [_ | Dir] ->
+ case lists:member(Dir, Dirs) of % BIF
false ->
+ %% The directory does not yet exist - add it
GetInfo = fun() -> FakeFI end,
GetBin = fun() -> <<>> end,
- VirtualDir = ["" | FunnyDir],
+ VirtualDir = ["" | Dir],
Includes2 = [{true, VirtualDir, GetInfo, GetBin} | Includes],
- FunnyDirs2 = [FunnyDir | FunnyDirs],
- {I, F, Acc2} = ensure_virtual_dirs(FunnyDir, Fun, FakeFI, Includes2, FunnyDirs2, Acc),
+ Dirs2 = [Dir | Dirs],
+
+ %% Recursively ensure dir elements on all levels
+ {I, F, Acc2} = ensure_virtual_dirs(Dir, Fun, FakeFI,
+ Includes2, Dirs2, Acc),
+
{_Continue, Acc3} = Fun({VirtualDir, GetInfo, GetBin}, Acc2),
{I, F, Acc3};
true ->
- {reverse(Includes), FunnyDirs, Acc}
- end;
- [] ->
- {reverse(Includes), FunnyDirs, Acc}
+ %% The directory element does already exist
+ %% Recursivly ensure dir elements on all levels
+ ensure_virtual_dirs(Dir,Fun,FakeFI,Includes,Dirs,Acc)
+ end
end.
foldl_archive(PrimZip, Acc, Fun) ->
Wrapper =
- fun({Funny, GI, GB}, A) ->
+ fun({Components, GI, GB}, A) ->
%% Allow partial iteration at foldl
- {Continue, A2} = Fun({Funny, GI, GB}, A),
+ {Continue, A2} = Fun({Components, GI, GB}, A),
{Continue, true, A2}
end,
prim_zip:foldl(Wrapper, Acc, PrimZip).
@@ -1202,15 +1221,19 @@ reverse([A, B]) ->
reverse([A, B | L]) ->
lists:reverse(L, [B, A]). % BIF
-%% Returns all lists in reverse order
-funny_split(List, Sep) ->
- funny_split(List, Sep, [], []).
-
-funny_split([Sep | Tail], Sep, Path, Paths) ->
- funny_split(Tail, Sep, [], [Path | Paths]);
-funny_split([Head | Tail], Sep, Path, Paths) ->
- funny_split(Tail, Sep, [Head | Path], Paths);
-funny_split([], _Sep, Path, Paths) ->
+%% Returns a reversed list of path components, each component itself a
+%% reversed list (string), e.g.
+%% /path/to/file -> ["elif","ot","htap",""]
+%% /path/to/dir/ -> ["","rid","ot","htap",""]
+%% Note the "" marking leading and trailing / (slash).
+path_split(List) ->
+ path_split(List, [], []).
+
+path_split([$/ | Tail], Path, Paths) ->
+ path_split(Tail, [], [Path | Paths]);
+path_split([Head | Tail], Path, Paths) ->
+ path_split(Tail, [Head | Path], Paths);
+path_split([], Path, Paths) ->
[Path | Paths].
name_split(ArchiveFile, File0) ->
@@ -1235,26 +1258,22 @@ do_name_split(undefined, File) ->
%% False match. Assume plain file
{file, File}
end;
-do_name_split(ArchiveFile0, File) ->
+do_name_split(ArchiveFile, File) ->
%% Look first in primary archive
- ArchiveFile = absname(ArchiveFile0),
case string_match(File, ArchiveFile, []) of
no_match ->
%% Archive or plain file
do_name_split(undefined, File);
{match, _RevPrimArchiveFile, FileInArchive} ->
%% Primary archive
- case FileInArchive of
- [$/ | FileInArchive2] ->
- {archive, ArchiveFile, FileInArchive2};
- _ ->
- {archive, ArchiveFile, FileInArchive}
- end
+ {archive, ArchiveFile, FileInArchive}
end.
string_match([Char | File], [Char | Archive], RevTop) ->
string_match(File, Archive, [Char | RevTop]);
-string_match(File, [], RevTop) ->
+string_match([] = File, [], RevTop) ->
+ {match, RevTop, File};
+string_match([$/ | File], [], RevTop) ->
{match, RevTop, File};
string_match(_File, _Archive, _RevTop) ->
no_match.
@@ -1307,24 +1326,26 @@ ipv4_addr([], D, [C,B,A]) when D < 256 -> {A,B,C,D}.
%% A simplified version of filename:absname/1
absname(Name) ->
Name2 = normalize(Name, []),
- case pathtype(Name2) of
- absolute ->
- Name2;
- relative ->
- case prim_file:get_cwd() of
- {ok, Cwd} ->
- Cwd ++ "/" ++ Name2;
- {error, _} ->
- Name2
- end;
- volumerelative ->
- case prim_file:get_cwd() of
- {ok, Cwd} ->
- absname_vr(Name2, Cwd);
- {error, _} ->
- Name2
- end
- end.
+ Name3 =
+ case pathtype(Name2) of
+ absolute ->
+ Name2;
+ relative ->
+ case prim_file:get_cwd() of
+ {ok, Cwd} ->
+ Cwd ++ "/" ++ Name2;
+ {error, _} ->
+ Name2
+ end;
+ volumerelative ->
+ case prim_file:get_cwd() of
+ {ok, Cwd} ->
+ absname_vr(Name2, Cwd);
+ {error, _} ->
+ Name2
+ end
+ end,
+ path_flatten(Name3).
%% Assumes normalized name
absname_vr([$/ | NameRest], [Drive, $\: | _]) ->
@@ -1380,22 +1401,12 @@ win32_pathtype(Name) ->
win32_pathtype([Char | List++Rest]);
[$/, $/|_] ->
absolute;
- [$\\, $/|_] ->
- absolute;
- [$/, $\\|_] ->
- absolute;
- [$\\, $\\|_] ->
- absolute;
[$/|_] ->
volumerelative;
- [$\\|_] ->
- volumerelative;
[C1, C2, List | Rest] when is_list(List) ->
- pathtype([C1, C2|List ++ Rest]);
+ win32_pathtype([C1, C2|List ++ Rest]);
[_Letter, $:, $/|_] ->
absolute;
- [_Letter, $:, $\\|_] ->
- absolute;
[_Letter, $:|_] ->
volumerelative;
_ ->
@@ -1408,8 +1419,6 @@ vxworks_first(Name) ->
{not_device, [], []};
[$/ | T] ->
vxworks_first2(device, T, [$/]);
- [$\\ | T] ->
- vxworks_first2(device, T, [$/]);
[H | T] when is_list(H) ->
vxworks_first(H ++ T);
[H | T] ->
@@ -1422,8 +1431,6 @@ vxworks_first2(Devicep, Name, FirstComp) ->
{Devicep, [], FirstComp};
[$/ |T ] ->
{Devicep, [$/ | T], FirstComp};
- [$\\ | T] ->
- {Devicep, [$/ | T], FirstComp};
[$: | T]->
{device, T, [$: | FirstComp]};
[H | T] when is_list(H) ->
@@ -1445,3 +1452,25 @@ normalize(Name, Acc) ->
[] ->
reverse(Acc)
end.
+
+%% Remove .. and . from the path, e.g.
+%% /path/./to/this/../file -> /path/to/file
+path_flatten(Name) ->
+ path_flatten(Name,[],[]).
+
+path_flatten([$/,$.,$.,$/|Rest],_RevLast,RevTop) ->
+ path_flatten(Rest,[],RevTop);
+path_flatten([$/,$.,$/|Rest],RevLast,RevTop) ->
+ path_flatten([$/|Rest],RevLast,RevTop);
+path_flatten([$/,$.,$.],_RevLast,RevTop) ->
+ path_flatten([],[],RevTop);
+path_flatten([$/,$.],RevLast,RevTop) ->
+ path_flatten([],RevLast,RevTop);
+path_flatten([$/],RevLast,RevTop) ->
+ path_flatten([],RevLast,RevTop);
+path_flatten([$/|Rest],RevLast,RevTop) ->
+ path_flatten(Rest,[],[$/|RevLast++RevTop]);
+path_flatten([Ch|Rest],RevLast,RevTop) ->
+ path_flatten(Rest,[Ch|RevLast],RevTop);
+path_flatten([],RevLast,RevTop) ->
+ reverse(RevLast++RevTop).