1%% ===================================================================== 2%% Licensed under the Apache License, Version 2.0 (the "License"); you may 3%% not use this file except in compliance with the License. You may obtain 4%% a copy of the License at <http://www.apache.org/licenses/LICENSE-2.0> 5%% 6%% Unless required by applicable law or agreed to in writing, software 7%% distributed under the License is distributed on an "AS IS" BASIS, 8%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9%% See the License for the specific language governing permissions and 10%% limitations under the License. 11%% 12%% Alternatively, you may use this file under the terms of the GNU Lesser 13%% General Public License (the "LGPL") as published by the Free Software 14%% Foundation; either version 2.1, or (at your option) any later version. 15%% If you wish to allow use of your version of this file only under the 16%% terms of the LGPL, you should delete the provisions above and replace 17%% them with the notice and other provisions required by the LGPL; see 18%% <http://www.gnu.org/licenses/>. If you do not delete the provisions 19%% above, a recipient may use your version of this file under the terms of 20%% either the Apache License or the LGPL. 21%% 22%% @copyright 2001-2003 Richard Carlsson 23%% @author Richard Carlsson <carlsson.richard@gmail.com> 24%% @see edoc 25%% @end 26%% ===================================================================== 27 28%% @doc Utility functions for EDoc. 29 30-module(edoc_lib). 31 32-export([count/2, lines/1, split_at/2, split_at_stop/1, 33 split_at_space/1, filename/1, transpose/1, segment/2, 34 get_first_sentence/1, is_space/1, strip_space/1, parse_expr/2, 35 parse_contact/2, escape_uri/1, join_uri/2, 36 is_name/1, to_label/1, find_doc_dirs/0, find_sources/2, 37 find_file/2, try_subdir/2, unique/1, 38 write_file/3, write_file/4, write_info_file/3, 39 read_info_file/1, get_doc_env/1, get_doc_env/3, copy_file/2, 40 run_doclet/2, run_layout/2, 41 simplify_path/1, timestr/1, datestr/1, read_encoding/2, 42 infer_module_app/1]). 43 44-import(edoc_report, [report/2, warning/2]). 45 46-include("edoc.hrl"). 47-include_lib("xmerl/include/xmerl.hrl"). 48 49-type filename() :: file:filename(). 50-type proplist() :: proplists:proplist(). 51 52-define(FILE_BASE, "/"). 53 54 55%% --------------------------------------------------------------------- 56%% List and string utilities 57 58%% @private 59timestr({H,M,Sec}) -> 60 lists:flatten(io_lib:fwrite("~2.2.0w:~2.2.0w:~2.2.0w",[H,M,Sec])). 61 62%% @private 63datestr({Y,M,D}) -> 64 Ms = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", 65 "Oct", "Nov", "Dec"], 66 lists:flatten(io_lib:fwrite("~s ~w ~w",[lists:nth(M, Ms),D,Y])). 67 68%% @private 69read_encoding(File, Options) -> 70 case epp:read_encoding(File, Options) of 71 none -> epp:default_encoding(); 72 Encoding -> Encoding 73 end. 74 75%% @doc Infer application containing the given module. 76%% 77%% It's expected that modules which are not preloaded 78%% and don't match the `<app>/ebin/<mod>.beam' path pattern 79%% will NOT have an app name inferred properly. 80%% `no_app' is returned in such cases. 81-spec infer_module_app(module()) -> no_app | {app, atom()}. 82infer_module_app(Mod) -> 83 case code:which(Mod) of 84 ModPath when is_list(ModPath) -> 85 case lists:reverse(string:tokens(ModPath, "/")) of 86 [_BeamFile, "ebin", AppVer | _] -> 87 [App | _] = string:tokens(AppVer, "-"), 88 {app, list_to_atom(App)}; 89 _ -> 90 no_app 91 end; 92 preloaded -> 93 {app, erts}; 94 _ -> 95 no_app 96 end. 97 98%% @private 99count(X, Xs) -> 100 count(X, Xs, 0). 101 102count(X, [X | Xs], N) -> 103 count(X, Xs, N + 1); 104count(X, [_ | Xs], N) -> 105 count(X, Xs, N); 106count(_X, [], N) -> 107 N. 108 109%% @private 110lines(Cs) -> 111 lines(Cs, [], []). 112 113lines([$\n | Cs], As, Ls) -> 114 lines(Cs, [], [lists:reverse(As) | Ls]); 115lines([C | Cs], As, Ls) -> 116 lines(Cs, [C | As], Ls); 117lines([], As, Ls) -> 118 lists:reverse([lists:reverse(As) | Ls]). 119 120%% @private 121split_at(Cs, K) -> 122 split_at(Cs, K, []). 123 124split_at([K | Cs], K, As) -> 125 {lists:reverse(As), Cs}; 126split_at([C | Cs], K, As) -> 127 split_at(Cs, K, [C | As]); 128split_at([], _K, As) -> 129 {lists:reverse(As), []}. 130 131%% @private 132split_at_stop(Cs) -> 133 split_at_stop(Cs, []). 134 135split_at_stop([$., $\s | Cs], As) -> 136 {lists:reverse(As), Cs}; 137split_at_stop([$., $\t | Cs], As) -> 138 {lists:reverse(As), Cs}; 139split_at_stop([$., $\n | Cs], As) -> 140 {lists:reverse(As), Cs}; 141split_at_stop([$.], As) -> 142 {lists:reverse(As), []}; 143split_at_stop([C | Cs], As) -> 144 split_at_stop(Cs, [C | As]); 145split_at_stop([], As) -> 146 {lists:reverse(As), []}. 147 148%% @private 149split_at_space(Cs) -> 150 split_at_space(Cs, []). 151 152split_at_space([$\s | Cs], As) -> 153 {lists:reverse(As), Cs}; 154split_at_space([$\t | Cs], As) -> 155 {lists:reverse(As), Cs}; 156split_at_space([$\n | Cs], As) -> 157 {lists:reverse(As), Cs}; 158split_at_space([C | Cs], As) -> 159 split_at_space(Cs, [C | As]); 160split_at_space([], As) -> 161 {lists:reverse(As), []}. 162 163%% @private 164is_space([$\s | Cs]) -> is_space(Cs); 165is_space([$\t | Cs]) -> is_space(Cs); 166is_space([$\n | Cs]) -> is_space(Cs); 167is_space([_C | _Cs]) -> false; 168is_space([]) -> true. 169 170%% @private 171strip_space([$\s | Cs]) -> strip_space(Cs); 172strip_space([$\t | Cs]) -> strip_space(Cs); 173strip_space([$\n | Cs]) -> strip_space(Cs); 174strip_space(Cs) -> Cs. 175 176%% @private 177segment(Es, N) -> 178 segment(Es, [], [], 0, N). 179 180segment([E | Es], As, Cs, N, M) when N < M -> 181 segment(Es, [E | As], Cs, N + 1, M); 182segment([_ | _] = Es, As, Cs, _N, M) -> 183 segment(Es, [], [lists:reverse(As) | Cs], 0, M); 184segment([], [], Cs, _N, _M) -> 185 lists:reverse(Cs); 186segment([], As, Cs, _N, _M) -> 187 lists:reverse([lists:reverse(As) | Cs]). 188 189%% @private 190transpose([]) -> []; 191transpose([[] | Xss]) -> transpose(Xss); 192transpose([[X | Xs] | Xss]) -> 193 [[X | [H || [H | _T] <- Xss]] 194 | transpose([Xs | [T || [_H | T] <- Xss]])]. 195 196%% Note that the parser will not produce two adjacent text segments; 197%% thus, if a text segment ends with a period character, it marks the 198%% end of the summary sentence only if it is also the last segment in 199%% the list, or is followed by a 'p' or 'br' ("whitespace") element. 200 201%% @private 202get_first_sentence([#xmlElement{name = p, content = Es} | _]) -> 203 %% Descend into initial paragraph. 204 get_first_sentence_1(Es); 205get_first_sentence(Es) -> 206 get_first_sentence_1(Es). 207 208get_first_sentence_1([E = #xmlText{value = Txt} | Es]) -> 209 Last = case Es of 210 [#xmlElement{name = p} | _] -> true; 211 [#xmlElement{name = br} | _] -> true; 212 [] -> true; 213 _ -> false 214 end, 215 case end_of_sentence(Txt, Last) of 216 {value, Txt1} -> 217 [E#xmlText{value = Txt1}]; 218 none -> 219 [E | get_first_sentence_1(Es)] 220 end; 221get_first_sentence_1([E | Es]) -> 222 % Skip non-text segments - don't descend further 223 [E | get_first_sentence_1(Es)]; 224get_first_sentence_1([]) -> 225 []. 226 227end_of_sentence(Cs, Last) -> 228 end_of_sentence(Cs, Last, []). 229 230%% We detect '.' and '!' as end-of-sentence markers. 231 232end_of_sentence([C=$., $\s | _], _, As) -> 233 end_of_sentence_1(C, true, As); 234end_of_sentence([C=$., $\t | _], _, As) -> 235 end_of_sentence_1(C, true, As); 236end_of_sentence([C=$., $\n | _], _, As) -> 237 end_of_sentence_1(C, true, As); 238end_of_sentence([C=$.], Last, As) -> 239 end_of_sentence_1(C, Last, As); 240end_of_sentence([C=$!, $\s | _], _, As) -> 241 end_of_sentence_1(C, true, As); 242end_of_sentence([C=$!, $\t | _], _, As) -> 243 end_of_sentence_1(C, true, As); 244end_of_sentence([C=$!, $\n | _], _, As) -> 245 end_of_sentence_1(C, true, As); 246end_of_sentence([C=$!], Last, As) -> 247 end_of_sentence_1(C, Last, As); 248end_of_sentence([C | Cs], Last, As) -> 249 end_of_sentence(Cs, Last, [C | As]); 250end_of_sentence([], Last, As) -> 251 end_of_sentence_1($., Last, strip_space(As)). % add a '.' 252 253end_of_sentence_1(C, true, As) -> 254 {value, lists:reverse([C | As])}; 255end_of_sentence_1(_, false, _) -> 256 none. 257 258%% For handling ISO 8859-1 (Latin-1) we use the following information: 259%% 260%% 000 - 037 NUL - US control 261%% 040 - 057 SPC - / punctuation 262%% 060 - 071 0 - 9 digit 263%% 072 - 100 : - @ punctuation 264%% 101 - 132 A - Z uppercase 265%% 133 - 140 [ - ` punctuation 266%% 141 - 172 a - z lowercase 267%% 173 - 176 { - ~ punctuation 268%% 177 DEL control 269%% 200 - 237 control 270%% 240 - 277 NBSP - ¿ punctuation 271%% 300 - 326 À - Ö uppercase 272%% 327 × punctuation 273%% 330 - 336 Ø - Þ uppercase 274%% 337 - 366 ß - ö lowercase 275%% 367 ÷ punctuation 276%% 370 - 377 ø - ÿ lowercase 277 278%% Names must begin with a lowercase letter and contain only 279%% alphanumerics and underscores. 280 281%% @private 282is_name([C | Cs]) when C >= $a, C =< $z -> 283 is_name_1(Cs); 284is_name([C | Cs]) when C >= $\337, C =< $\377, C =/= $\367 -> 285 is_name_1(Cs); 286is_name(_) -> false. 287 288is_name_1([C | Cs]) when C >= $a, C =< $z -> 289 is_name_1(Cs); 290is_name_1([C | Cs]) when C >= $A, C =< $Z -> 291 is_name_1(Cs); 292is_name_1([C | Cs]) when C >= $0, C =< $9 -> 293 is_name_1(Cs); 294is_name_1([C | Cs]) when C >= $\300, C =< $\377, C =/= $\327, C =/= $\367 -> 295 is_name_1(Cs); 296is_name_1([$_ | Cs]) -> 297 is_name_1(Cs); 298is_name_1([]) -> true; 299is_name_1(_) -> false. 300 301%% @private 302unique([X | Xs]) -> [X | unique(Xs, X)]; 303unique([]) -> []. 304 305unique([X | Xs], X) -> unique(Xs, X); 306unique([X | Xs], _) -> [X | unique(Xs, X)]; 307unique([], _) -> []. 308 309 310%% --------------------------------------------------------------------- 311%% Parsing utilities 312 313%% @doc EDoc Erlang expression parsing. For parsing things like the 314%% content of <a href="overview-summary.html#ftag-equiv">`@equiv'</a> 315%% tags, and strings denoting file names, e.g. in @headerfile. Also used 316%% by {@link edoc_run}. 317%% @private 318 319parse_expr(S, L) -> 320 case erl_scan:string(S ++ ".", L) of 321 {ok, Ts, _} -> 322 case erl_parse:parse_exprs(Ts) of 323 {ok, [Expr]} -> 324 Expr; 325 {error, {999999, erl_parse, _}} -> 326 throw_error(eof, L); 327 {error, E} -> 328 throw_error(E, L) 329 end; 330 {error, E, _} -> 331 throw_error(E, L) 332 end. 333 334 335-record(info, {name = "", 336 email = "", 337 uri = ""}). 338 339%-type info() :: #info{name :: string(), 340% email :: string(), 341% uri :: string()}. 342 343%% @doc EDoc "contact information" parsing. This is the type of the 344%% content in e.g. 345%% <a href="overview-summary.html#mtag-author">`@author'</a> tags. 346%% @private 347 348parse_contact(S, L) -> 349 I = scan_name(S, L, #info{}, []), 350 {I#info.name, I#info.email, I#info.uri}. 351 352%% The name is taken as the first non-whitespace-only string before, 353%% between, or following the e-mail/URI sections. Subsequent text that 354%% is not e/mail or URI is ignored. 355 356scan_name([$< | Cs], L, I, As) -> 357 case I#info.email of 358 "" -> 359 {Cs1, I1} = scan_email(Cs, L, set_name(I, As), []), 360 scan_name(Cs1, L, I1, []); 361 _ -> 362 throw_error("multiple '<...>' sections.", L) 363 end; 364scan_name([$[ | Cs], L, I, As) -> 365 case I#info.uri of 366 "" -> 367 {Cs1, I1} = scan_uri(Cs, L, set_name(I, As), []), 368 scan_name(Cs1, L, I1, []); 369 _ -> 370 throw_error("multiple '[...]' sections.", L) 371 end; 372scan_name([$\n | Cs], L, I, As) -> 373 scan_name(Cs, L + 1, I, [$\n | As]); 374scan_name([C | Cs], L, I, As) -> 375 scan_name(Cs, L, I, [C | As]); 376scan_name([], _L, I, As) -> 377 set_name(I, As). 378 379scan_uri([$] | Cs], _L, I, As) -> 380 {Cs, I#info{uri = strip_and_reverse(As)}}; 381scan_uri([$\n | Cs], L, I, As) -> 382 scan_uri(Cs, L + 1, I, [$\n | As]); 383scan_uri([C | Cs], L, I, As) -> 384 scan_uri(Cs, L, I, [C | As]); 385scan_uri([], L, _I, _As) -> 386 throw_error({missing, $]}, L). 387 388scan_email([$> | Cs], _L, I, As) -> 389 {Cs, I#info{email = strip_and_reverse(As)}}; 390scan_email([$\n | Cs], L, I, As) -> 391 scan_email(Cs, L + 1, I, [$\n | As]); 392scan_email([C | Cs], L, I, As) -> 393 scan_email(Cs, L, I, [C | As]); 394scan_email([], L, _I, _As) -> 395 throw_error({missing, $>}, L). 396 397set_name(I, As) -> 398 case I#info.name of 399 "" -> I#info{name = strip_and_reverse(As)}; 400 _ -> I 401 end. 402 403strip_and_reverse(As) -> 404 edoc_lib:strip_space(lists:reverse(edoc_lib:strip_space(As))). 405 406 407%% --------------------------------------------------------------------- 408%% URI and Internet 409 410%% This is a conservative URI escaping, which escapes anything that may 411%% not appear in an NMTOKEN ([a-zA-Z0-9]|'.'|'-'|'_'), including ':'. 412%% Characters are first encoded in UTF-8. 413%% 414%% Note that this should *not* be applied to complete URI, but only to 415%% segments that may need escaping, when forming a complete URI. 416%% 417%% TODO: general utf-8 encoding for all of Unicode (0-16#10ffff) 418 419%% @private 420escape_uri([C | Cs]) when C >= $a, C =< $z -> 421 [C | escape_uri(Cs)]; 422escape_uri([C | Cs]) when C >= $A, C =< $Z -> 423 [C | escape_uri(Cs)]; 424escape_uri([C | Cs]) when C >= $0, C =< $9 -> 425 [C | escape_uri(Cs)]; 426escape_uri([C = $. | Cs]) -> 427 [C | escape_uri(Cs)]; 428escape_uri([C = $- | Cs]) -> 429 [C | escape_uri(Cs)]; 430escape_uri([C = $_ | Cs]) -> 431 [C | escape_uri(Cs)]; 432escape_uri([C | Cs]) when C > 16#7f -> 433 %% This assumes that characters are at most 16 bits wide. 434 escape_byte(((C band 16#c0) bsr 6) + 16#c0) 435 ++ escape_byte(C band 16#3f + 16#80) 436 ++ escape_uri(Cs); 437escape_uri([C | Cs]) -> 438 escape_byte(C) ++ escape_uri(Cs); 439escape_uri([]) -> 440 []. 441 442escape_byte(C) when C >= 0, C =< 255 -> 443 [$%, hex_digit(C bsr 4), hex_digit(C band 15)]. 444 445hex_digit(N) when N >= 0, N =< 9 -> 446 N + $0; 447hex_digit(N) when N > 9, N =< 15 -> 448 N + $a - 10. 449 450% utf8([C | Cs]) when C > 16#7f -> 451% [((C band 16#c0) bsr 6) + 16#c0, C band 16#3f ++ 16#80 | utf8(Cs)]; 452% utf8([C | Cs]) -> 453% [C | utf8(Cs)]; 454% utf8([]) -> 455% []. 456 457%% Please note that URI are *not* file names. Don't use the stdlib 458%% 'filename' module for operations on (any parts of) URI. 459 460%% @private 461join_uri(Base, "") -> 462 Base; 463join_uri("", Path) -> 464 Path; 465join_uri(Base, Path) -> 466 Base ++ "/" ++ Path. 467 468%% @private 469to_label([$\s | Cs]) -> 470 to_label(Cs); 471to_label([$\t | Cs]) -> 472 to_label(Cs); 473to_label([$\n | Cs]) -> 474 to_label(Cs); 475to_label([]) -> 476 []; 477to_label(Cs) -> 478 to_label_1(Cs). 479 480to_label_1([$\s | Cs]) -> 481 to_label_2([$\s | Cs]); 482to_label_1([$\t | Cs]) -> 483 to_label_2([$\s | Cs]); 484to_label_1([$\n | Cs]) -> 485 to_label_2([$\s | Cs]); 486to_label_1([C | Cs]) -> 487 [C | to_label_1(Cs)]; 488to_label_1([]) -> 489 []. 490 491to_label_2(Cs) -> 492 case to_label(Cs) of 493 [] -> []; 494 Cs1 -> [$_ | Cs1] 495 end. 496 497 498%% --------------------------------------------------------------------- 499%% Files 500 501%% @private 502filename([C | T]) when is_integer(C), C > 0 -> 503 [C | filename(T)]; 504filename([H|T]) -> 505 filename(H) ++ filename(T); 506filename([]) -> 507 []; 508filename(N) when is_atom(N) -> 509 atom_to_list(N); 510filename(N) -> 511 report("bad filename: `~tP'.", [N, 25]), 512 exit(error). 513 514%% @private 515copy_file(From, To) -> 516 case file:copy(From, To) of 517 {ok, _} -> ok; 518 {error, R} -> 519 R1 = file:format_error(R), 520 report("error copying '~ts' to '~ts': ~ts.", [From, To, R1]), 521 exit(error) 522 end. 523 524list_dir(Dir, Error) -> 525 case file:list_dir(Dir) of 526 {ok, Fs} -> 527 Fs; 528 {error, R} -> 529 F = case Error of 530 %% true -> 531 %% fun (S, As) -> report(S, As), exit(error) end; 532 false -> 533 fun (S, As) -> warning(S, As), [] end 534 end, 535 R1 = file:format_error(R), 536 F("could not read directory '~ts': ~ts.", [filename(Dir), R1]) 537 end. 538 539%% @private 540simplify_path(P) -> 541 case filename:basename(P) of 542 "." -> 543 simplify_path(filename:dirname(P)); 544 ".." -> 545 simplify_path(filename:dirname(filename:dirname(P))); 546 _ -> 547 P 548 end. 549 550%% The directories From and To are assumed to exist. 551 552%% copy_dir(From, To) -> 553%% Es = list_dir(From, true), % error if listing fails 554%% lists:foreach(fun (E) -> copy_dir(From, To, E) end, Es). 555 556%% copy_dir(From, To, Entry) -> 557%% From1 = filename:join(From, Entry), 558%% To1 = filename:join(To, Entry), 559%% case filelib:is_dir(From1) of 560%% true -> 561%% make_dir(To1), 562%% copy_dir(From1, To1); 563%% false -> 564%% copy_file(From1, To1) 565%% end. 566 567%% make_dir(Dir) -> 568%% case file:make_dir(Dir) of 569%% ok -> ok; 570%% {error, R} -> 571%% R1 = file:format_error(R), 572%% report("cannot create directory '~ts': ~ts.", [Dir, R1]), 573%% exit(error) 574%% end. 575 576%% @private 577try_subdir(Dir, Subdir) -> 578 D = filename:join(Dir, Subdir), 579 case filelib:is_dir(D) of 580 true -> D; 581 false -> Dir 582 end. 583 584%% @doc Write the given `Text' to the file named by `Name' in directory 585%% `Dir'. If the target directory does not exist, it will be created. 586%% @private 587 588-spec write_file(Text, Dir, Name) -> ok when 589 Text :: unicode:chardata(), 590 Dir :: filename(), 591 Name :: filename(). 592write_file(Text, Dir, Name) -> 593 write_file(Text, Dir, Name, [{encoding,latin1}]). 594 595write_file(Text, Dir, Name, Options) -> 596 File = filename:join([Dir, Name]), 597 ok = filelib:ensure_dir(File), 598 case file:open(File, [write] ++ Options) of 599 {ok, FD} -> 600 io:put_chars(FD, Text), 601 ok = file:close(FD); 602 {error, R} -> 603 R1 = file:format_error(R), 604 report("could not write file '~ts': ~ts.", [File, R1]), 605 exit(error) 606 end. 607 608%% @private 609write_info_file(App, Modules, Dir) -> 610 Ts = [{modules, Modules}], 611 Ts1 = if App =:= no_app -> Ts; 612 true -> [{application, App} | Ts] 613 end, 614 S0 = [io_lib:fwrite("~p.\n", [T]) || T <- Ts1], 615 S = ["%% encoding: UTF-8\n" | S0], 616 write_file(S, Dir, ?INFO_FILE, [{encoding,unicode}]). 617 618%% @doc Reads text from the file named by `Name'. 619 620-spec read_file(filename()) -> {ok, string()} | {error, term()}. 621read_file(File) -> 622 case file:read_file(File) of 623 {ok, Bin} -> 624 Enc = edoc_lib:read_encoding(File, []), 625 case catch unicode:characters_to_list(Bin, Enc) of 626 String when is_list(String) -> 627 {ok, String}; 628 _ -> 629 {error, invalid_unicode} 630 end; 631 {error, Reason} -> {error, Reason} 632 end. 633 634 635%% --------------------------------------------------------------------- 636%% Info files 637 638info_file_data(Ts) -> 639 App = proplists:get_value(application, Ts, no_app), 640 Ms = proplists:append_values(modules, Ts), 641 {App, Ms}. 642 643%% Local file access - don't complain if file does not exist. 644 645%% @private 646read_info_file(Dir) -> 647 File = filename:join(Dir, ?INFO_FILE), 648 case filelib:is_file(File) of 649 true -> 650 case read_file(File) of 651 {ok, Text} -> 652 parse_info_file(Text, File); 653 {error, R} -> 654 R1 = file:format_error(R), 655 warning("could not read '~ts': ~ts.", [File, R1]), 656 {no_app, []} 657 end; 658 false -> 659 {no_app, []} 660 end. 661 662parse_info_file(Text, Name) -> 663 case parse_terms(Text) of 664 {ok, Vs} -> 665 info_file_data(Vs); 666 {error, eof} -> 667 warning("unexpected end of file in '~ts'.", [Name]), 668 {no_app, []}; 669 {error, {_Line,Module,R}} -> 670 warning("~ts: ~ts.", [Module:format_error(R), Name]), 671 {no_app, []} 672 end. 673 674parse_terms(Text) -> 675 case erl_scan:string(Text) of 676 {ok, Ts, _Line} -> 677 parse_terms_1(Ts, [], []); 678 {error, R, _Line} -> 679 {error, R} 680 end. 681 682parse_terms_1([T={dot, _L} | Ts], As, Vs) -> 683 case erl_parse:parse_term(lists:reverse([T | As])) of 684 {ok, V} -> 685 parse_terms_1(Ts, [], [V | Vs]); 686 {error, R} -> 687 {error, R} 688 end; 689parse_terms_1([T | Ts], As, Vs) -> 690 parse_terms_1(Ts, [T | As], Vs); 691parse_terms_1([], [], Vs) -> 692 {ok, lists:reverse(Vs)}; 693parse_terms_1([], _As, _Vs) -> 694 {error, eof}. 695 696 697%% --------------------------------------------------------------------- 698%% Source files 699 700%% @doc See {@link edoc:run/2} for a description of the options 701%% `subpackages', `source_suffix'. 702%% @private 703 704%% NEW-OPTIONS: subpackages, source_suffix 705%% DEFER-OPTIONS: edoc:run/2 706 707find_sources(Path, Opts) -> 708 Rec = proplists:get_bool(subpackages, Opts), 709 Ext = proplists:get_value(source_suffix, Opts, ?DEFAULT_SOURCE_SUFFIX), 710 find_sources(Path, Rec, Ext, Opts). 711 712find_sources(Path, Rec, Ext, _Opts) -> 713 lists:flatten(find_sources_1(Path, Rec, Ext)). 714 715find_sources_1([P | Ps], Rec, Ext) -> 716 Dir = P, 717 Fs1 = find_sources_1(Ps, Rec, Ext), 718 case filelib:is_dir(Dir) of 719 true -> 720 [find_sources_2(Dir, Rec, Ext) | Fs1]; 721 false -> 722 Fs1 723 end; 724find_sources_1([], _Rec, _Ext) -> 725 []. 726 727find_sources_2(Dir, Rec, Ext) -> 728 Es = list_dir(Dir, false), % just warn if listing fails 729 Es1 = [{E, Dir} || E <- Es, is_source_file(E, Ext)], 730 case Rec of 731 true -> 732 [find_sources_3(Es, Dir, Rec, Ext) | Es1]; 733 false -> 734 Es1 735 end. 736 737find_sources_3(Es, Dir, Rec, Ext) -> 738 [find_sources_2(filename:join(Dir, E), 739 Rec, Ext) 740 || E <- Es, is_source_dir(E, Dir)]. 741 742is_source_file(Name, Ext) -> 743 (filename:extension(Name) == Ext) 744 andalso is_name(filename:rootname(Name, Ext)). 745 746is_source_dir(Name, Dir) -> 747 filelib:is_dir(filename:join(Dir, Name)). 748 749%% @private 750find_file([P | Ps], Name) -> 751 File = filename:join(P, Name), 752 case filelib:is_file(File) of 753 true -> 754 File; 755 false -> 756 find_file(Ps, Name) 757 end; 758find_file([], _Name) -> 759 "". 760 761%% @private 762find_doc_dirs() -> 763 find_doc_dirs(code:get_path()). 764 765find_doc_dirs([P0 | Ps]) -> 766 P = filename:absname(P0), 767 P1 = case filename:basename(P) of 768 ?EBIN_DIR -> 769 filename:dirname(P); 770 _ -> 771 P 772 end, 773 Dir = try_subdir(P1, ?EDOC_DIR), 774 File = filename:join(Dir, ?INFO_FILE), 775 case filelib:is_file(File) of 776 true -> 777 [Dir | find_doc_dirs(Ps)]; 778 false -> 779 find_doc_dirs(Ps) 780 end; 781find_doc_dirs([]) -> 782 []. 783 784%% All names with "internal linkage" are mapped to the empty string, so 785%% that relative references will be created. For apps, the empty string 786%% implies that we use the default app-path. 787 788%% NEW-OPTIONS: doc_path 789%% DEFER-OPTIONS: get_doc_env/3 790 791get_doc_links(App, Modules, Opts) -> 792 Path = proplists:append_values(doc_path, Opts) ++ find_doc_dirs(), 793 Ds = [{P, read_info_file(P)} || P <- Path], 794 Ds1 = [{"", {App, Modules}} | Ds], 795 D = dict:new(), 796 make_links(Ds1, D, D). 797 798make_links([{Dir, {App, Ms}} | Ds], A, M) -> 799 A1 = if App == no_app -> A; 800 true -> add_new(App, Dir, A) 801 end, 802 F = fun (K, D) -> add_new(K, Dir, D) end, 803 M1 = lists:foldl(F, M, Ms), 804 make_links(Ds, A1, M1); 805make_links([], A, M) -> 806 F = fun (D) -> 807 fun (K) -> 808 case dict:find(K, D) of 809 {ok, V} -> V; 810 error -> "" 811 end 812 end 813 end, 814 {F(A), F(M)}. 815 816add_new(K, V, D) -> 817 case dict:is_key(K, D) of 818 true -> 819 D; 820 false -> 821 dict:store(K, V, D) 822 end. 823 824%% @equiv get_doc_env([], [], Opts) 825%% @private 826 827-spec get_doc_env(proplist()) -> edoc:env(). 828get_doc_env(Opts) -> 829 get_doc_env(no_app, [], Opts). 830 831%% @doc Creates an environment data structure used by parts of EDoc for 832%% generating references, etc. See {@link edoc:run/2} for a description 833%% of the options `file_suffix', `app_default' and `doc_path'. 834%% 835%% @see edoc_extract:source/4 836%% @see edoc:get_doc/3 837 838%% NEW-OPTIONS: file_suffix, app_default 839%% INHERIT-OPTIONS: get_doc_links/4 840%% DEFER-OPTIONS: edoc:run/2 841 842-spec get_doc_env(App, Modules, Options) -> edoc:env() when 843 App :: atom() | no_app, 844 Modules :: [module()], 845 Options :: proplist(). 846get_doc_env(App, Modules, Opts) -> 847 Suffix = proplists:get_value(file_suffix, Opts, 848 ?DEFAULT_FILE_SUFFIX), 849 AppDefault = proplists:get_value(app_default, Opts, ?APP_DEFAULT), 850 Includes = proplists:append_values(includes, Opts), 851 852 {A, M} = get_doc_links(App, Modules, Opts), 853 #env{file_suffix = Suffix, 854 apps = A, 855 modules = M, 856 app_default = AppDefault, 857 includes = Includes}. 858 859%% --------------------------------------------------------------------- 860%% Plug-in modules 861 862%% @doc See {@link edoc:run/2} for a description of the `doclet' option. 863 864%% NEW-OPTIONS: doclet 865%% DEFER-OPTIONS: edoc:run/2 866 867%% @private 868run_doclet(Fun, Opts) -> 869 run_plugin(doclet, ?DEFAULT_DOCLET, Fun, Opts). 870 871%% @doc See {@link edoc:layout/2} for a description of the `layout' 872%% option. 873 874%% NEW-OPTIONS: layout 875%% DEFER-OPTIONS: edoc:layout/2 876 877%% @private 878run_layout(Fun, Opts) -> 879 run_plugin(layout, ?DEFAULT_LAYOUT, Fun, Opts). 880 881run_plugin(Name, Default, Fun, Opts) -> 882 run_plugin(Name, Name, Default, Fun, Opts). 883 884run_plugin(Name, Key, Default, Fun, Opts) when is_atom(Name) -> 885 Module = get_plugin(Key, Default, Opts), 886 case catch {ok, Fun(Module)} of 887 {ok, Value} -> 888 Value; 889 R -> 890 report("error in ~ts '~w': ~tP.", [Name, Module, R, 20]), 891 exit(error) 892 end. 893 894get_plugin(Key, Default, Opts) -> 895 case proplists:get_value(Key, Opts, Default) of 896 M when is_atom(M) -> 897 M; 898 Other -> 899 report("bad value for option '~w': ~tP.", [Key, Other, 10]), 900 exit(error) 901 end. 902 903 904%% --------------------------------------------------------------------- 905%% Error handling 906 907-type line() :: erl_anno:line(). 908-type err() :: 'eof' 909 | {'missing', char()} 910 | {line(), atom(), string()} 911 | string(). 912 913-spec throw_error(err(), line()) -> no_return(). 914 915throw_error({missing, C}, L) -> 916 throw_error({"missing '~c'.", [C]}, L); 917throw_error(eof, L) -> 918 throw({error,L,"unexpected end of expression."}); 919throw_error({L, M, D}, _L) -> 920 throw({error,L,{format_error,M,D}}); 921throw_error(D, L) -> 922 throw({error, L, D}). 923