PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/stdlib/src/filelib.erl

http://github.com/mfoemmel/erlang-otp
Erlang | 443 lines | 344 code | 66 blank | 33 comment | 2 complexity | aafaf6d62379cd4da9ebf369479c0ca6 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. %%
  2. %% %CopyrightBegin%
  3. %%
  4. %% Copyright Ericsson AB 1997-2009. All Rights Reserved.
  5. %%
  6. %% The contents of this file are subject to the Erlang Public License,
  7. %% Version 1.1, (the "License"); you may not use this file except in
  8. %% compliance with the License. You should have received a copy of the
  9. %% Erlang Public License along with this software. If not, it can be
  10. %% retrieved online at http://www.erlang.org/.
  11. %%
  12. %% Software distributed under the License is distributed on an "AS IS"
  13. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  14. %% the License for the specific language governing rights and limitations
  15. %% under the License.
  16. %%
  17. %% %CopyrightEnd%
  18. -module(filelib).
  19. %% File utilities.
  20. -export([wildcard/1, wildcard/2, is_dir/1, is_file/1, is_regular/1,
  21. compile_wildcard/1]).
  22. -export([fold_files/5, last_modified/1, file_size/1, ensure_dir/1]).
  23. -export([wildcard/3, is_dir/2, is_file/2, is_regular/2]).
  24. -export([fold_files/6, last_modified/2, file_size/2]).
  25. -include_lib("kernel/include/file.hrl").
  26. -define(HANDLE_ERROR(Expr),
  27. try
  28. Expr
  29. catch
  30. error:{badpattern,_}=UnUsUalVaRiAbLeNaMe ->
  31. %% Get the stack backtrace correct.
  32. erlang:error(UnUsUalVaRiAbLeNaMe)
  33. end).
  34. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  35. -spec wildcard(name()) -> [file:filename()].
  36. wildcard(Pattern) when is_list(Pattern) ->
  37. ?HANDLE_ERROR(do_wildcard(Pattern, file)).
  38. -spec wildcard(name(), name() | atom()) -> [file:filename()].
  39. wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) ->
  40. ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file));
  41. wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) ->
  42. ?HANDLE_ERROR(do_wildcard(Pattern, Mod)).
  43. -spec wildcard(name(), name(), atom()) -> [file:filename()].
  44. wildcard(Pattern, Cwd, Mod)
  45. when is_list(Pattern), is_list(Cwd), is_atom(Mod) ->
  46. ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)).
  47. -spec is_dir(name()) -> boolean().
  48. is_dir(Dir) ->
  49. do_is_dir(Dir, file).
  50. -spec is_dir(name(), atom()) -> boolean().
  51. is_dir(Dir, Mod) when is_atom(Mod) ->
  52. do_is_dir(Dir, Mod).
  53. -spec is_file(name()) -> boolean().
  54. is_file(File) ->
  55. do_is_file(File, file).
  56. -spec is_file(name(), atom()) -> boolean().
  57. is_file(File, Mod) when is_atom(Mod) ->
  58. do_is_file(File, Mod).
  59. -spec is_regular(name()) -> boolean().
  60. is_regular(File) ->
  61. do_is_regular(File, file).
  62. -spec is_regular(name(), atom()) -> boolean().
  63. is_regular(File, Mod) when is_atom(Mod) ->
  64. do_is_regular(File, Mod).
  65. -spec fold_files(name(), string(), boolean(), fun((_,_) -> _), _) -> _.
  66. fold_files(Dir, RegExp, Recursive, Fun, Acc) ->
  67. do_fold_files(Dir, RegExp, Recursive, Fun, Acc, file).
  68. -spec fold_files(name(), string(), boolean(), fun((_,_) -> _), _, atom()) -> _.
  69. fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) when is_atom(Mod) ->
  70. do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod).
  71. -spec last_modified(name()) -> date_time() | 0.
  72. last_modified(File) ->
  73. do_last_modified(File, file).
  74. -spec last_modified(name(), atom()) -> date_time() | 0.
  75. last_modified(File, Mod) when is_atom(Mod) ->
  76. do_last_modified(File, Mod).
  77. -spec file_size(name()) -> non_neg_integer().
  78. file_size(File) ->
  79. do_file_size(File, file).
  80. -spec file_size(name(), atom()) -> non_neg_integer().
  81. file_size(File, Mod) when is_atom(Mod) ->
  82. do_file_size(File, Mod).
  83. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  84. do_wildcard(Pattern, Mod) when is_list(Pattern) ->
  85. do_wildcard_comp(do_compile_wildcard(Pattern), Mod).
  86. do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) ->
  87. case eval_read_file_info(File, Mod) of
  88. {ok,_} -> [File];
  89. _ -> []
  90. end;
  91. do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) ->
  92. do_wildcard_1([Base], Rest, Mod).
  93. do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), is_list(Cwd) ->
  94. do_wildcard_comp(do_compile_wildcard(Pattern), Cwd, Mod).
  95. do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) ->
  96. case eval_read_file_info(filename:absname(File, Cwd), Mod) of
  97. {ok,_} -> [File];
  98. _ -> []
  99. end;
  100. do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) ->
  101. Cwd = filename:join([Cwd0]), %Slash away redundant slashes.
  102. PrefixLen = length(Cwd)+1,
  103. [lists:nthtail(PrefixLen, N) || N <- do_wildcard_1([Cwd], Rest, Mod)];
  104. do_wildcard_comp({compiled_wildcard,[Base|Rest]}, _Cwd, Mod) ->
  105. do_wildcard_1([Base], Rest, Mod).
  106. do_is_dir(Dir, Mod) ->
  107. case eval_read_file_info(Dir, Mod) of
  108. {ok, #file_info{type=directory}} ->
  109. true;
  110. _ ->
  111. false
  112. end.
  113. do_is_file(File, Mod) ->
  114. case eval_read_file_info(File, Mod) of
  115. {ok, #file_info{type=regular}} ->
  116. true;
  117. {ok, #file_info{type=directory}} ->
  118. true;
  119. _ ->
  120. false
  121. end.
  122. do_is_regular(File, Mod) ->
  123. case eval_read_file_info(File, Mod) of
  124. {ok, #file_info{type=regular}} ->
  125. true;
  126. _ ->
  127. false
  128. end.
  129. %% fold_files(Dir, RegExp, Recursive, Fun, AccIn).
  130. %% folds the function Fun(F, Acc) -> Acc1 over
  131. %% all files <F> in <Dir> that match the regular expression <RegExp>
  132. %% If <Recursive> is true all sub-directories to <Dir> are processed
  133. do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) ->
  134. {ok, Re1} = re:compile(RegExp),
  135. do_fold_files1(Dir, Re1, Recursive, Fun, Acc, Mod).
  136. do_fold_files1(Dir, RegExp, Recursive, Fun, Acc, Mod) ->
  137. case eval_list_dir(Dir, Mod) of
  138. {ok, Files} -> do_fold_files2(Files, Dir, RegExp, Recursive, Fun, Acc, Mod);
  139. {error, _} -> Acc
  140. end.
  141. do_fold_files2([], _Dir, _RegExp, _Recursive, _Fun, Acc, _Mod) ->
  142. Acc;
  143. do_fold_files2([File|T], Dir, RegExp, Recursive, Fun, Acc0, Mod) ->
  144. FullName = filename:join(Dir, File),
  145. case do_is_regular(FullName, Mod) of
  146. true ->
  147. case re:run(File, RegExp, [{capture,none}]) of
  148. match ->
  149. Acc = Fun(FullName, Acc0),
  150. do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc, Mod);
  151. nomatch ->
  152. do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod)
  153. end;
  154. false ->
  155. case Recursive andalso do_is_dir(FullName, Mod) of
  156. true ->
  157. Acc1 = do_fold_files1(FullName, RegExp, Recursive,
  158. Fun, Acc0, Mod),
  159. do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc1, Mod);
  160. false ->
  161. do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod)
  162. end
  163. end.
  164. do_last_modified(File, Mod) ->
  165. case eval_read_file_info(File, Mod) of
  166. {ok, Info} ->
  167. Info#file_info.mtime;
  168. _ ->
  169. 0
  170. end.
  171. do_file_size(File, Mod) ->
  172. case eval_read_file_info(File, Mod) of
  173. {ok, Info} ->
  174. Info#file_info.size;
  175. _ ->
  176. 0
  177. end.
  178. %%----------------------------------------------------------------------
  179. %% +type ensure_dir(X) -> ok | {error, Reason}.
  180. %% +type X = filename() | dirname()
  181. %% ensures that the directory name required to create D exists
  182. -spec ensure_dir(name()) -> 'ok' | {'error', posix()}.
  183. ensure_dir("/") ->
  184. ok;
  185. ensure_dir(F) ->
  186. Dir = filename:dirname(F),
  187. case do_is_dir(Dir, file) of
  188. true ->
  189. ok;
  190. false ->
  191. ensure_dir(Dir),
  192. file:make_dir(Dir)
  193. end.
  194. %%%
  195. %%% Pattern matching using a compiled wildcard.
  196. %%%
  197. do_wildcard_1(Files, Pattern, Mod) ->
  198. do_wildcard_2(Files, Pattern, [], Mod).
  199. do_wildcard_2([File|Rest], Pattern, Result, Mod) ->
  200. do_wildcard_2(Rest, Pattern, do_wildcard_3(File, Pattern, Result, Mod), Mod);
  201. do_wildcard_2([], _, Result, _Mod) ->
  202. Result.
  203. do_wildcard_3(Base, [Pattern|Rest], Result, Mod) ->
  204. case do_list_dir(Base, Mod) of
  205. {ok, Files0} ->
  206. Files = lists:sort(Files0),
  207. Matches = wildcard_4(Pattern, Files, Base, []),
  208. do_wildcard_2(Matches, Rest, Result, Mod);
  209. _ ->
  210. Result
  211. end;
  212. do_wildcard_3(Base, [], Result, _Mod) ->
  213. [Base|Result].
  214. wildcard_4(Pattern, [File|Rest], Base, Result) ->
  215. case wildcard_5(Pattern, File) of
  216. true ->
  217. wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]);
  218. false ->
  219. wildcard_4(Pattern, Rest, Base, Result)
  220. end;
  221. wildcard_4(_Patt, [], _Base, Result) ->
  222. Result.
  223. wildcard_5([question|Rest1], [_|Rest2]) ->
  224. wildcard_5(Rest1, Rest2);
  225. wildcard_5([accept], _) ->
  226. true;
  227. wildcard_5([star|Rest], File) ->
  228. do_star(Rest, File);
  229. wildcard_5([{one_of, Ordset}|Rest], [C|File]) ->
  230. case ordsets:is_element(C, Ordset) of
  231. true -> wildcard_5(Rest, File);
  232. false -> false
  233. end;
  234. wildcard_5([{alt, Alts}], File) ->
  235. do_alt(Alts, File);
  236. wildcard_5([C|Rest1], [C|Rest2]) when is_integer(C) ->
  237. wildcard_5(Rest1, Rest2);
  238. wildcard_5([X|_], [Y|_]) when is_integer(X), is_integer(Y) ->
  239. false;
  240. wildcard_5([], []) ->
  241. true;
  242. wildcard_5([], [_|_]) ->
  243. false;
  244. wildcard_5([_|_], []) ->
  245. false.
  246. do_star(Pattern, [X|Rest]) ->
  247. case wildcard_5(Pattern, [X|Rest]) of
  248. true -> true;
  249. false -> do_star(Pattern, Rest)
  250. end;
  251. do_star(Pattern, []) ->
  252. wildcard_5(Pattern, []).
  253. do_alt([Alt|Rest], File) ->
  254. case wildcard_5(Alt, File) of
  255. true -> true;
  256. false -> do_alt(Rest, File)
  257. end;
  258. do_alt([], _File) ->
  259. false.
  260. do_list_dir(current, Mod) -> eval_list_dir(".", Mod);
  261. do_list_dir(Dir, Mod) -> eval_list_dir(Dir, Mod).
  262. join(current, File) -> File;
  263. join(Base, File) -> filename:join(Base, File).
  264. %%% Compiling a wildcard.
  265. compile_wildcard(Pattern) ->
  266. ?HANDLE_ERROR(do_compile_wildcard(Pattern)).
  267. do_compile_wildcard(Pattern) ->
  268. {compiled_wildcard,compile_wildcard_1(Pattern)}.
  269. compile_wildcard_1(Pattern) ->
  270. [Root|Rest] = filename:split(Pattern),
  271. case filename:pathtype(Root) of
  272. relative ->
  273. compile_wildcard_2([Root|Rest], current);
  274. _ ->
  275. compile_wildcard_2(Rest, [Root])
  276. end.
  277. compile_wildcard_2([Part|Rest], Root) ->
  278. case compile_part(Part) of
  279. Part ->
  280. compile_wildcard_2(Rest, join(Root, Part));
  281. Pattern ->
  282. compile_wildcard_3(Rest, [Pattern,Root])
  283. end;
  284. compile_wildcard_2([], Root) -> {exists,Root}.
  285. compile_wildcard_3([Part|Rest], Result) ->
  286. compile_wildcard_3(Rest, [compile_part(Part)|Result]);
  287. compile_wildcard_3([], Result) ->
  288. lists:reverse(Result).
  289. compile_part(Part) ->
  290. compile_part(Part, false, []).
  291. compile_part_to_sep(Part) ->
  292. compile_part(Part, true, []).
  293. compile_part([], true, _) ->
  294. error(missing_delimiter);
  295. compile_part([$,|Rest], true, Result) ->
  296. {ok, $,, lists:reverse(Result), Rest};
  297. compile_part([$}|Rest], true, Result) ->
  298. {ok, $}, lists:reverse(Result), Rest};
  299. compile_part([$?|Rest], Upto, Result) ->
  300. compile_part(Rest, Upto, [question|Result]);
  301. compile_part([$*], Upto, Result) ->
  302. compile_part([], Upto, [accept|Result]);
  303. compile_part([$*|Rest], Upto, Result) ->
  304. compile_part(Rest, Upto, [star|Result]);
  305. compile_part([$[|Rest], Upto, Result) ->
  306. case compile_charset(Rest, ordsets:new()) of
  307. {ok, Charset, Rest1} ->
  308. compile_part(Rest1, Upto, [Charset|Result]);
  309. error ->
  310. compile_part(Rest, Upto, [$[|Result])
  311. end;
  312. compile_part([${|Rest], Upto, Result) ->
  313. case compile_alt(Rest) of
  314. {ok, Alt} ->
  315. lists:reverse(Result, [Alt]);
  316. error ->
  317. compile_part(Rest, Upto, [${|Result])
  318. end;
  319. compile_part([X|Rest], Upto, Result) ->
  320. compile_part(Rest, Upto, [X|Result]);
  321. compile_part([], _Upto, Result) ->
  322. lists:reverse(Result).
  323. compile_charset([$]|Rest], Ordset) ->
  324. compile_charset1(Rest, ordsets:add_element($], Ordset));
  325. compile_charset([$-|Rest], Ordset) ->
  326. compile_charset1(Rest, ordsets:add_element($-, Ordset));
  327. compile_charset([], _Ordset) ->
  328. error;
  329. compile_charset(List, Ordset) ->
  330. compile_charset1(List, Ordset).
  331. compile_charset1([Lower, $-, Upper|Rest], Ordset) when Lower =< Upper ->
  332. compile_charset1(Rest, compile_range(Lower, Upper, Ordset));
  333. compile_charset1([$]|Rest], Ordset) ->
  334. {ok, {one_of, Ordset}, Rest};
  335. compile_charset1([X|Rest], Ordset) ->
  336. compile_charset1(Rest, ordsets:add_element(X, Ordset));
  337. compile_charset1([], _Ordset) ->
  338. error.
  339. compile_range(Lower, Current, Ordset) when Lower =< Current ->
  340. compile_range(Lower, Current-1, ordsets:add_element(Current, Ordset));
  341. compile_range(_, _, Ordset) ->
  342. Ordset.
  343. compile_alt(Pattern) ->
  344. compile_alt(Pattern, []).
  345. compile_alt(Pattern, Result) ->
  346. case compile_part_to_sep(Pattern) of
  347. {ok, $,, AltPattern, Rest} ->
  348. compile_alt(Rest, [AltPattern|Result]);
  349. {ok, $}, AltPattern, Rest} ->
  350. NewResult = [AltPattern|Result],
  351. RestPattern = compile_part(Rest),
  352. {ok, {alt, [Alt++RestPattern || Alt <- NewResult]}};
  353. Pattern ->
  354. error
  355. end.
  356. error(Reason) ->
  357. erlang:error({badpattern,Reason}).
  358. eval_read_file_info(File, file) ->
  359. file:read_file_info(File);
  360. eval_read_file_info(File, erl_prim_loader) ->
  361. case erl_prim_loader:read_file_info(File) of
  362. error -> {error, erl_prim_loader};
  363. Res-> Res
  364. end;
  365. eval_read_file_info(File, Mod) ->
  366. Mod:read_file_info(File).
  367. eval_list_dir(Dir, file) ->
  368. file:list_dir(Dir);
  369. eval_list_dir(Dir, erl_prim_loader) ->
  370. case erl_prim_loader:list_dir(Dir) of
  371. error -> {error, erl_prim_loader};
  372. Res-> Res
  373. end;
  374. eval_list_dir(Dir, Mod) ->
  375. Mod:list_dir(Dir).