aboutsummaryrefslogtreecommitdiffstats
path: root/lib/edoc
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/edoc
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/edoc')
-rw-r--r--lib/edoc/COPYING504
-rw-r--r--lib/edoc/Makefile127
-rw-r--r--lib/edoc/doc/Makefile91
-rw-r--r--lib/edoc/doc/html/.gitignore0
-rw-r--r--lib/edoc/doc/man3/.gitignore0
-rw-r--r--lib/edoc/doc/overview.edoc1026
-rw-r--r--lib/edoc/doc/pdf/.gitignore0
-rw-r--r--lib/edoc/doc/src/Makefile139
-rw-r--r--lib/edoc/doc/src/book.xml48
-rw-r--r--lib/edoc/doc/src/fascicules.xml15
-rw-r--r--lib/edoc/doc/src/make.dep21
-rw-r--r--lib/edoc/doc/src/notes.xml221
-rw-r--r--lib/edoc/doc/src/part.xml38
-rw-r--r--lib/edoc/doc/src/part_notes.xml38
-rw-r--r--lib/edoc/doc/src/ref_man.xml43
-rw-r--r--lib/edoc/ebin/.gitignore0
-rw-r--r--lib/edoc/include/Makefile58
-rw-r--r--lib/edoc/include/edoc_doclet.hrl63
-rw-r--r--lib/edoc/info3
-rw-r--r--lib/edoc/priv/Makefile54
-rw-r--r--lib/edoc/priv/edoc.dtd139
-rw-r--r--lib/edoc/priv/edoc_generate.src76
-rw-r--r--lib/edoc/priv/erlang.pngbin0 -> 2109 bytes
-rw-r--r--lib/edoc/priv/stylesheet.css55
-rw-r--r--lib/edoc/src/Makefile91
-rw-r--r--lib/edoc/src/edoc.app.src24
-rw-r--r--lib/edoc/src/edoc.appup.src1
-rw-r--r--lib/edoc/src/edoc.erl771
-rw-r--r--lib/edoc/src/edoc.hrl100
-rw-r--r--lib/edoc/src/edoc_data.erl545
-rw-r--r--lib/edoc/src/edoc_doclet.erl521
-rw-r--r--lib/edoc/src/edoc_extract.erl584
-rw-r--r--lib/edoc/src/edoc_layout.erl875
-rw-r--r--lib/edoc/src/edoc_lib.erl998
-rw-r--r--lib/edoc/src/edoc_macros.erl327
-rw-r--r--lib/edoc/src/edoc_parser.yrl423
-rw-r--r--lib/edoc/src/edoc_refs.erl217
-rw-r--r--lib/edoc/src/edoc_report.erl96
-rw-r--r--lib/edoc/src/edoc_run.erl225
-rw-r--r--lib/edoc/src/edoc_scanner.erl358
-rw-r--r--lib/edoc/src/edoc_tags.erl373
-rw-r--r--lib/edoc/src/edoc_types.erl204
-rw-r--r--lib/edoc/src/edoc_types.hrl130
-rw-r--r--lib/edoc/src/edoc_wiki.erl456
-rw-r--r--lib/edoc/src/otpsgml_layout.erl853
-rw-r--r--lib/edoc/test/Makefile66
-rw-r--r--lib/edoc/test/edoc.spec1
-rw-r--r--lib/edoc/test/edoc_SUITE.erl52
-rw-r--r--lib/edoc/test/edoc_SUITE_data/overview.edoc910
-rw-r--r--lib/edoc/test/edoc_SUITE_data/overview.syntax_tools76
-rw-r--r--lib/edoc/vsn.mk1
51 files changed, 12037 insertions, 0 deletions
diff --git a/lib/edoc/COPYING b/lib/edoc/COPYING
new file mode 100644
index 0000000000..223ede7de3
--- /dev/null
+++ b/lib/edoc/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/lib/edoc/Makefile b/lib/edoc/Makefile
new file mode 100644
index 0000000000..2b011b55cc
--- /dev/null
+++ b/lib/edoc/Makefile
@@ -0,0 +1,127 @@
+# ``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 via the world wide web 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.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+#
+# Macros
+#
+
+SUB_DIRECTORIES = src include priv doc/src
+
+include vsn.mk
+VSN = $(EDOC_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/xmerl-$(VSN)
+
+# ----------------------------------------------------
+# Help application directory specification
+# ----------------------------------------------------
+DIR_NAME = edoc-$(VSN)
+ERL_DIR = src
+
+ifndef APP_RELEASE_DIR
+ APP_RELEASE_DIR = /tmp
+endif
+
+# ----------------------------------------------------
+
+EXTRA_FILES = \
+ edoc-info overview.edoc stylesheet.css
+
+EXTRA_HTML_FILES = \
+ modules-frame.html overview-summary.html \
+ packages-frame.html
+
+HTML_FILES = doc/*.html
+
+# ----------------------------------------------------
+ifndef APP_TAR_FILE
+ APP_TAR_FILE = $(APP_RELEASE_DIR)/$(DIR_NAME).tgz
+endif
+
+APP_DIR = $(APP_RELEASE_DIR)/$(DIR_NAME)
+
+
+SPECIAL_TARGETS =
+
+#
+# Default Subdir Targets
+#
+
+include $(ERL_TOP)/make/otp_subdir.mk
+
+
+.PHONY: info version
+
+
+version:
+ @echo "$(VSN)"
+
+
+APPNAME=edoc
+BINDIR=$(ERL_TOP)/lib/edoc/ebin
+DOCDIR=$(ERL_TOP)/lib/edoc/doc
+DOC_OPTS=[{def,{version,"$(VSN)"}},todo]
+
+SYNTAX_TOOLS_DIR=$(ERL_TOP)/lib/syntax_tools
+XMERL_DIR=$(ERL_TOP)/lib/xmerl
+INCDIR=$(XMERL_DIR)/include
+
+docs:
+ erl -noshell -pa $(BINDIR) -pa $(SYNTAX_TOOLS_DIR)/ebin \
+ -pa $(XMERL_DIR)/ebin -run edoc_run application \
+ "'$(APPNAME)'" '"."' '$(DOC_OPTS)'
+
+edocs: docs
+
+info:
+ @echo $(HTML_FILES)
+
+
+app_release: tar
+
+app_dir: $(APP_DIR)
+
+
+$(APP_DIR):
+ cat TAR.exclude > TAR.exclude2; \
+ echo "edoc/TAR.exclude2" >> TAR.exclude2; \
+ (cd ..; find edoc -name 'findmerge.*' >> edoc/TAR.exclude2)
+ (cd ..; find edoc -name '*.contrib*' >> edoc/TAR.exclude2)
+ (cd ..; find edoc -name '*.keep*' >> edoc/TAR.exclude2)
+ (cd ..; find edoc -name '*~' >> edoc/TAR.exclude2)
+ (cd ..; find edoc -name 'erl_crash.dump' >> edoc/TAR.exclude2)
+ (cd ..; find edoc -name '*.log' >> edoc/TAR.exclude2)
+ (cd ..; find edoc -name 'core' >> edoc/TAR.exclude2)
+ (cd ..; find edoc -name '.cmake.state' >> edoc/TAR.exclude2)
+ mkdir $(APP_DIR); \
+ (cd ..; tar cfX - edoc/TAR.exclude2 edoc) | \
+ (cd $(APP_DIR); tar xf -); \
+ mv $(APP_DIR)/edoc/* $(APP_DIR)/; \
+ rmdir $(APP_DIR)/edoc
+ mkdir $(APP_DIR)/doc; \
+ (cd doc; tar cf - man3 html) | (cd $(APP_DIR)/doc; tar xf -)
+
+tar: $(APP_TAR_FILE)
+
+$(APP_TAR_FILE): $(APP_DIR)
+ (cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
diff --git a/lib/edoc/doc/Makefile b/lib/edoc/doc/Makefile
new file mode 100644
index 0000000000..a0f6484382
--- /dev/null
+++ b/lib/edoc/doc/Makefile
@@ -0,0 +1,91 @@
+# ``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 via the world wide web 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.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id: Makefile,v 1.1.1.1 2004/10/04 13:53:33 richardc Exp $
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(EDOC_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/edoc-$(VSN)
+
+# ----------------------------------------------------
+# Help application directory specification
+# ----------------------------------------------------
+
+APPNAME=edoc
+DOC_TITLE="Welcome to EDoc"
+
+HTML_FILES = *.html
+INFO_FILE = ../info
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+
+docs:
+ (cd ..; \
+ edoc_generate -app '$(APPNAME)' -vsn '$(VSN)')
+
+
+info:
+ @echo "HTML_FILES:" $(HTML_FILES)
+ @echo "HTMLDIR: $(HTMLDIR)"
+
+
+
+debug opt:
+
+
+clean:
+ rm -f $(HTML_FILES) stylesheet.css edoc-info
+ rm -f errs core *~
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+
+
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(HTML_FILES) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+
+
+release_spec:
+
+
+
+# ----------------------------------------------------
+# Include dependency
+# ----------------------------------------------------
+#-include make.dep
+
+
diff --git a/lib/edoc/doc/html/.gitignore b/lib/edoc/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/edoc/doc/html/.gitignore
diff --git a/lib/edoc/doc/man3/.gitignore b/lib/edoc/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/edoc/doc/man3/.gitignore
diff --git a/lib/edoc/doc/overview.edoc b/lib/edoc/doc/overview.edoc
new file mode 100644
index 0000000000..9b25c17b1f
--- /dev/null
+++ b/lib/edoc/doc/overview.edoc
@@ -0,0 +1,1026 @@
+ -*- html -*-
+
+ EDoc overview page
+
+
+@author Richard Carlsson <[email protected]>
+@copyright 2003-2006 Richard Carlsson
+@version {@version}
+@title Welcome to EDoc
+
+@doc EDoc is the Erlang program documentation generator. Inspired by the
+Javadoc<sup><font size="-3">TM</font></sup> tool for the Java<sup><font
+size="-3">TM</font></sup> programming language, EDoc is adapted to the
+conventions of the Erlang world, and has several features not found in
+Javadoc.
+
+== Contents ==
+
+<ol>
+ <li>{@section Introduction}</li>
+ <li>{@section Running EDoc}</li>
+ <li>{@section The overview page}</li>
+ <li>{@section Generic tags}</li>
+ <li>{@section Overview tags}</li>
+ <li>{@section Module tags}</li>
+ <li>{@section Function tags}</li>
+ <li>{@section References}</li>
+ <li>{@section Notes on XHTML}</li>
+ <li>{@section Wiki notation}</li>
+ <li>{@section Macro expansion}</li>
+ <li>{@section Type specifications}</li>
+ <li>{@section Acknowledgements}</li>
+</ol>
+
+== Introduction ==
+
+EDoc lets you write the documentation of an Erlang program as
+comments in the source code itself, using <em>tags</em> on the form
+"`@Name ...'". A source file does not have to contain tags
+for EDoc to generate its documentation, but without tags the result will
+only contain the basic available information that can be extracted from
+the module.
+
+A tag must be the first thing on a comment line, except for leading
+'`%'' characters and whitespace. The comment must be between
+program declarations, and not on the same line as any program text. All
+the following text - including consecutive comment lines - up until the
+end of the comment or the next tagged line, is taken as the
+<em>content</em> of the tag.
+
+Tags are associated with the nearest following program construct "of
+significance" (the module name declaration and function
+definitions). Other constructs are ignored; e.g., in:
+```
+ %% @doc Prints the value X.
+
+ -record(foo, {x, y, z}).
+
+ print(X) -> ...
+'''
+the `@doc' tag is associated with the function `print/1'.
+
+Note that in a comment such as:
+```% % @doc ...'''
+the tag is ignored, because only the first '`%'' character is
+considered "leading". This allows tags to be "commented out".
+
+Some tags, such as `@type', do not need to be associated
+with any program construct. These may be placed at the end of the file,
+in the "footer".
+
+
+== Running EDoc ==
+
+The following are the main functions for running EDoc:
+ <ul>
+ <li>{@link edoc:application/2}: Creates documentation for a
+ typical Erlang application.</li>
+ <li>{@link edoc:packages/2}: Creates documentation for one or
+ more packages, automatically locating source files.</li>
+ <li>{@link edoc:files/2}: Creates documentation for a
+ specified set of source files.</li>
+ <li>{@link edoc:run/3}: General interface function; the common
+ back-end for the above functions. Options are documented here.</li>
+ </ul>
+
+Note that the function {@link edoc:file/2} belongs to the old, deprecated
+interface (from EDoc version 0.1), and should not be used.
+
+
+== The overview page ==
+
+When documentation is generated for an entire application, an overview
+page, or "front page", is generated. (The page you are now reading is an
+overview page.) This should contain the high-level description or user
+manual for the application, leaving the finer details to the
+documentation for individual modules. By default, the overview page is
+generated from the file `overview.edoc' in the target directory
+(typically, this is the `doc' subdirectory of the application
+directory); see {@link edoc_doclet} for details.
+
+The format of the overview file is the same as for EDoc documentation
+comments (see {@section Introduction}), except that the lines do not
+have leading '`%'' characters. Furthermore, all lines before the first
+tag line are ignored, and can be used as a comment. All tags in the
+overview file, such as `@@doc', `@@version', etc., refer to the
+application as a whole; see {@section Overview tags} for details.
+
+Here is an example of the contents of an overview file:
+```** this is the overview.doc file for the application 'frob' **
+
+ @@author R. J. Hacker <[email protected]>
+ @@copyright 2007 R. J. Hacker
+ @@version 1.0.0
+ @@title Welcome to the `frob' application!
+ @@doc `frob' is a highly advanced frobnicator with low latency,
+ ...
+'''
+
+
+== Generic tags ==
+
+The following tags can be used anywhere within a module:
+<dl>
+ <dt><a name="gtag-clear">`@clear'</a></dt>
+
+ <dd>This tag causes all tags above it (up to the previous program
+ construct), to be discarded, including the `@clear'
+ tag itself. The text following the tag
+ is also ignored. <em>This is typically only useful in code
+ containing conditional compilation, when preprocessing is turned
+ on.</em> (Preprocessing is turned off by default.) E.g., in
+```-ifdef(DEBUG).
+ %% @doc ...
+ foo(...) -> ...
+ -endif.
+ %% @clear
+
+ %% @doc ...
+ bar(...) -> ...'''
+ the `@clear' tag makes sure that EDoc does not see
+ two `@doc' tags before the function `bar',
+ even if the code for function `foo' is removed by
+ preprocessing. (There is no way for EDoc to see what the first
+ `@doc' tag "really" belongs to, since preprocessing
+ strips away all such information.)</dd>
+
+ <dt><a name="gtag-docfile">`@docfile'</a></dt>
+ <dd>Reads a plain documentation file (on the same format as an
+ overview file - see {@section The overview page} for details), and
+ uses the tags in that file as if they had been written in place of
+ the `@docfile' tag. The content is the name of the file to be
+ read; leading and trailing whitespace is ignored. See also <a
+ href="#gtag-headerfile">`@headerfile'</a>.</dd>
+
+ <dt><a name="gtag-end">`@end'</a></dt>
+ <dd>The text following this tag is always ignored. Use this to
+ mark the end of the previous tag, when necessary, as e.g. in:
+```%% ----------------------------------
+ %% ...
+ %% @doc ...
+ %% ...
+ %% @end
+ %% ----------------------------------'''
+ to avoid including the last "ruler" line in the
+ `@doc' tag.
+
+ <em>Note: using some other "dummy" `@'-tag for
+ the same purpose might work in a particular implementation of
+ EDoc, but is not guaranteed to. Always use `@end'
+ to ensure future compatibility.</em></dd>
+
+ <dt><a name="gtag-headerfile">`@headerfile'</a></dt>
+ <dd>Similar to the <a href="#gtag-docfile">`@docfile' tag</a>, but
+ reads a file containing Erlang source code - generally this should
+ be a header file (with the extension `.hrl'). If the file turns
+ out to contain one or more function definitions or a module
+ declaration, all tags that occur above the last such definition or
+ module declaration are ignored, and EDoc will print a
+ warning. This tag allows you to write documentation in a header
+ file and insert it at a specific place in the documentation, even
+ if the header file is used (i.e., included) by several
+ modules. The `includes' option can be used to specify a search
+ path (see {@link edoc:read_source/2}).</dd>
+
+ <dt><a name="gtag-todo">`@todo' (or `@TODO')</a></dt>
+ <dd>Attaches a To-Do note to a function, module, package, or
+ overview-page. The content can be any XHTML text describing
+ the issue, e.g.:
+```%% @TODO Finish writing the documentation.'''
+ or
+```%% @todo Implement <a href="http://www.ietf.org/rfc/rfc2549.txt">RFC 2549</a>.'''
+ These tags can also be written as "`TODO:'", e.g.:
+```%% TODO: call your mother'''
+ see {@section Wiki notation} for more information. To-Do notes are
+ normally not shown unless the `todo' option is turned on (see
+ {@link edoc:get_doc/2}).</dd>
+
+ <dt><a name="gtag-type">`@type'</a></dt>
+ <dd>Documents an abstract data type or type alias. The content
+ consists of a type declaration or definition, optionally
+ followed by a period ('`.'') separator and XHTML
+ text describing the type (i.e., its purpose, use, etc.). There
+ must be at least one whitespace character between the '`.'' and
+ the text. See {@section Type specifications} for syntax and
+ examples.
+ All data type descriptions are placed in a separate section of
+ the documentation, regardless of where the tags occur.</dd>
+
+</dl>
+
+
+== Overview tags ==
+
+The following tags can be used in an overview file.
+<dl>
+ <dt><a name="otag-author">`@author'</a></dt>
+ <dd>See the <a href="#mtag-author">`@author' module tag</a> for
+ details.</dd>
+
+ <dt><a name="otag-copyright">`@copyright'</a></dt>
+ <dd>See the <a href="#mtag-copyright">`@copyright' module tag</a>
+ for details.</dd>
+
+ <dt><a name="otag-doc">`@doc'</a></dt>
+ <dd>See the <a href="#mtag-doc">`@doc' module tag</a> for
+ details.</dd>
+
+ <dt><a name="otag-reference">`@reference'</a></dt>
+ <dd>See the <a href="#mtag-reference">`@reference' module tag</a>
+ for details.</dd>
+
+ <dt><a name="otag-see">`@see'</a></dt>
+ <dd>See the <a href="#mtag-see">`@see' module tag</a> for
+ details.</dd>
+
+ <dt><a name="otag-since">`@since'</a></dt>
+ <dd>See the <a href="#mtag-since">`@since' module tag</a> for
+ details.</dd>
+
+ <dt><a name="otag-title">`@title'</a></dt>
+ <dd>Specifies a title for the overview page. This tag can
+ <em>only</em> be used in an overview file. The content can be
+ arbitrary text.</dd>
+
+ <dt><a name="otag-version">`@version'</a></dt>
+ <dd>See the <a href="#mtag-version">`@version' module
+ tag</a> for details.</dd>
+
+</dl>
+
+
+== Module tags ==
+
+The following tags can be used before a module declaration:
+<dl>
+ <dt><a name="mtag-author">`@author'</a></dt>
+ <dd>Specifies the name of an author, along with contact
+ information. An e-mail address can be given within `<...>'
+ delimiters, and a URI within `[...]' delimiters. Both e-mail and
+ URI are optional, and any surrounding whitespace is stripped from
+ all strings.
+
+ The name is the first nonempty string that is not within `<...>'
+ or `[...]', and does not contain only whitespace. (In other words,
+ the name can come before, between, or after the e-mail and URI,
+ but cannot be split up; any sections after the first are ignored.)
+ If an e-mail address is given, but no name, the e-mail string will
+ be used also for the name. If no `<...>' section is present, but
+ the name string contains an '`@'' character, it is assumed to be
+ an e-mail address. Not both name and e-mail may be left out.
+
+ Examples:
+```%% @author Richard Carlsson'''
+
+```%% @author Richard Carlsson <[email protected]>
+ %% [http://user.it.uu.se/~richardc/]'''
+
+```%% @author <[email protected]>'''
+
+```%% @author [email protected] [http://user.it.uu.se/~richardc/]'''
+ </dd>
+
+<dt><a name="mtag-copyright">`@copyright'</a></dt>
+ <dd>Specifies the module copyrights. The content can be
+ arbitrary text; for example:
+```
+ %% @copyright 2001-2003 Richard Carlsson'''
+ </dd>
+
+ <dt><a name="mtag-deprecated">`@deprecated'</a></dt>
+ <dd>Mark the module as deprecated, indicating that it should no
+ longer be used. The content must be well-formed XHTML, and should
+ preferably include a `@{@link}' reference to a
+ replacement; as in:
+```
+ %% @deprecated Please use the module @{@link foo} instead.'''
+ </dd>
+
+ <dt><a name="mtag-doc">`@doc'</a></dt>
+ <dd>Describes the module, using well-formed XHTML text. The
+ first sentence is used as a summary (see the
+ <a href="#ftag-doc">`@doc' function tag</a> for details). For
+ example.:
+```%% @doc This is a <em>very</em> useful module. It is ...'''</dd>
+
+ <dt><a name="mtag-hidden">`@hidden'</a></dt>
+ <dd>Marks the module so that it will not appear in the
+ documentation (even if "private" documentation is generated).
+ Useful for sample code, test modules, etc. The content can be
+ used as a comment; it is ignored by EDoc.</dd>
+
+ <dt><a name="mtag-private">`@private'</a></dt>
+ <dd>Marks the module as private (i.e., not part of the public
+ interface), so that it will not appear in the normal
+ documentation. (If "private" documentation is generated, the
+ module will be included.) The content can be used as a comment; it
+ is ignored by EDoc.</dd>
+
+ <dt><a name="mtag-reference">`@reference'</a></dt>
+ <dd>Specifies a reference to some arbitrary external resource,
+ such as an article, book, or web site. The content must be
+ well-formed XHTML text. Examples:
+```%% @reference Pratchett, T., <em>Interesting Times</em>,
+ %% Victor Gollancz Ltd, 1994.'''
+
+```%% @reference See <a href="www.google.com">Google</a> for
+ %% more information.'''
+ </dd>
+
+ <dt><a name="mtag-see">`@see'</a></dt>
+ <dd>See the <a href="#ftag-see">`@see' function tag</a>
+ for details.</dd>
+
+ <dt><a name="mtag-since">`@since'</a></dt>
+ <dd>Specifies when the module was introduced, with respect to
+ the application, package, release or distribution it is part
+ of. The content can be arbitrary text.</dd>
+
+ <dt><a name="mtag-version">`@version'</a></dt>
+ <dd>Specifies the module version. The content can be arbitrary
+ text.</dd>
+
+</dl>
+
+
+
+== Function tags ==
+
+The following tags can be used before a function definition:
+<dl>
+ <dt><a name="ftag-deprecated">`@deprecated'</a></dt>
+ <dd>See the <a href="#mtag-deprecated">`@deprecated'
+ module tag</a> for details.</dd>
+
+ <dt><a name="ftag-doc">`@doc'</a></dt>
+ <dd>XHTML text describing the function. The first
+ sentence of the text is used as a quick summary; this ends at
+ the first period character ('`.'') or exclamation mark
+ ('`!'') that is followed by a whitespace character, a
+ line break, or the end of the tag text, and is not within XML
+ markup. (As an exception, the first sentence may be within an
+ initial paragraph element)</dd>
+
+ <dt><a name="ftag-equiv">`@equiv'</a></dt>
+ <dd>Specify equivalence to another function call/expression.
+ The content must be a proper Erlang expression. If the
+ expression is a function call, a cross-reference to the called
+ function is created automatically. Typically, this tag is used
+ instead of `@doc'. </dd>
+
+ <dt><a name="ftag-hidden">`@hidden'</a></dt>
+ <dd>Marks the function so that it will not appear in the
+ documentation (even if "private" documentation is generated).
+ Useful for debug/test functions, etc. The content can be
+ used as a comment; it is ignored by EDoc.</dd>
+
+ <dt><a name="ftag-private">`@private'</a></dt>
+ <dd>Marks the function as private (i.e., not part of the public
+ interface), so that it will not appear in the normal
+ documentation. (If "private" documentation is generated, the
+ function will be included.) Only useful for exported functions,
+ e.g. entry points for `spawn'. (Non-exported functions are
+ always "private".) The content can be used as a comment; it is
+ ignored by EDoc.</dd>
+
+ <dt><a name="ftag-see">`@see'</a></dt>
+ <dd>Make a reference to a module, function, datatype, or
+ application. (See {@section References}.)
+ The content consists of a reference, optionally followed by a
+ period ('`.''), one or more whitespace characters, and
+ XHTML text to be used for the label; for example "`@see edoc'" or
+ "`@see edoc. <b>EDoc</b>'". If no label text is specified, the
+ reference itself is used as the label.</dd>
+
+ <dt><a name="ftag-since">`@since'</a></dt>
+ <dd>Specifies in what version of the module the function was
+ introduced; cf. the
+ <a href="#mtag-version">`@version'
+ module tag</a>. The content can be arbitrary text.</dd>
+
+ <dt><a name="ftag-spec">`@spec'</a></dt>
+ <dd>Used to specify the function type; see {@section Type
+ specifications} for syntax details. If the function name is
+ included in the specification, it must match the name in the
+ actual code. When parameter names are not given in the
+ specification, suitable names will be taken from the source
+ code if possible, and otherwise synthesized.</dd>
+
+ <dt><a name="ftag-throws">`@throws'</a></dt>
+ <dd>Specifies which types of terms may be thrown by the
+ function, if its execution terminates abruptly due to a call to
+ `erlang:throw(Term)'. The content is a type expression (see {@section
+ Type specifications}), and can be a union type.
+
+ Note that exceptions of type `exit' (as caused by calls to
+ `erlang:exit(Term)') and `error' (run-time errors such as `badarg'
+ or `badarith') are not viewed as part of the normal interface of
+ the function, and cannot be documented with the `@throws' tag.</dd>
+
+ <dt><a name="ftag-type">`@type'</a></dt>
+ <dd>See the <a href="#gtag-type">`@type' generic tag</a>
+ for details. Placing a `@type' tag by a function
+ definition may be convenient, but does not affect where the
+ description is placed in the generated documentation.</dd>
+</dl>
+
+
+
+== References ==
+
+In several contexts (`@see' tags, `@link' macros, etc.), EDoc lets
+you refer to the generated documentation for modules, functions,
+datatypes, and applications, using a simple and compact syntax. The
+possible formats for references are:
+<table border="1" summary="reference syntax">
+ <tr><th>Reference syntax</th><th>Example</th><th>Scope</th></tr>
+ <tr><td>`Module'</td><td>{@link edoc_run}, `erl.lang.list'</td><td>Global</td></tr>
+ <tr><td>`Package.*'</td><td>`erl.lang.*'</td><td>Global</td></tr>
+ <tr><td>`Function/Arity'</td><td>`file/2'</td><td>Within module</td></tr>
+ <tr><td>`Module:Function/Arity'</td><td>{@link edoc:application/2}</td><td>Global</td></tr>
+ <tr><td>`Type()'</td><td>`filename()'</td><td>Within module</td></tr>
+ <tr><td>`Module:Type()'</td><td>{@link edoc:edoc_module()}</td><td>Global</td></tr>
+ <tr><td>`//Application'</td><td>{@link //edoc}</td><td>Global</td></tr>
+ <tr><td>`//Application/Module'</td><td>{@link //edoc/edoc_doclet}</td><td>Global</td></tr>
+ <tr><td>`//Application/Module:Function/Arity'</td><td>{@link //edoc/edoc_run:file/1}</td><td>Global</td></tr>
+ <tr><td>`//Application/Module:Type()'</td><td>{@link //edoc/edoc:edoc_module()}</td><td>Global</td></tr>
+</table>
+
+
+EDoc will resolve references using the information it finds in
+`edoc-info'-files at the locations specified with the `doc_path'
+option. EDoc will automatically (and somewhat intelligently) try to find
+any local `edoc-info'-files using the current code path, and add them to
+the end of the `doc_path' list. The target doc-directory is also
+searched for an existing info file; this allows documentation to be
+built incrementally. (Use the `new' option to ignore any old info
+file.)
+
+Note that if the name of a module, function or datatype is explicitly
+qualified with an application (as in "`//edoc/edoc_run'"), this
+overrides any other information about that name, and the reference will
+be made relative to the location of the application (if it can be
+found). This makes it possible to refer to e.g. a module "`fred'" as
+"`//foo/fred'" without accidentally getting a reference to
+"`//bar/fred'". You should not use this form of explicit references for
+names that are local to the application you are currently creating -
+they will always be resolved correctly.
+
+Note that module-local references such as `file/2' only work properly
+within a module. In an overview-page like this (i.e., the one you are
+currently reading), no module context is available.
+
+== Notes on XHTML ==
+
+In several places, XHTML markup can be used in the documentation
+text, in particular in `@doc' tags. The main differences from
+HTML are the following:
+<ul>
+ <li>All elements must have explicit start and end tags, and be
+ correctly nested. This means that you cannot e.g. write a
+ `<li>' tag without also writing a corresponding `</li>'
+ tag in the right place. This could be an annoyance
+ at times, but has the great advantage that EDoc can report all
+ malformed XHTML in your source code, rather than propagate the
+ errors to the generated documentation.</li>
+ <li>XHTML tag and attribute names should always be lower-case.</li>
+ <li>Attributes must be quoted, as in e.g. `<a
+ name="top">'.</li>
+</ul>
+To write an element like the HTML `<br>', which has no actual content,
+you can write either the full `<br></br>', or better, use the XHTML
+abbreviated form `<br/>'.
+
+Since the purpose of EDoc is to document programs, there is also a
+limited form of "wiki"-syntax available for making program code easier
+to write inline (and to make the doc-comments easier to read).
+See {@section Wiki notation} for details.
+
+The HTML heading tags `h1' and `h2' are reserved for use by EDoc.
+Headings in documentation source code should start at `h3'. There is
+however a special syntax for writing headings which avoids using
+specific level numbers altogether; see {@section Headings} for details.
+
+EDoc uses {@link //xmerl. XMerL} to parse and export XML markup.
+
+
+== Wiki notation ==
+
+When EDoc parses XHTML, it does additional pre- and post-processing of
+the text in order to expand certain notation specific to EDoc into
+proper XHTML markup. This "wiki" ([http://en.wikipedia.org/wiki/Wiki])
+notation is intended to make it easier to write source code
+documentation.
+
+ === Empty lines separate paragraphs ===
+
+Leaving an empty line in XHTML text (i.e., a line which except for
+any leading start-of-comment '<tt>%</tt>' characters contains only
+whitespace), will make EDoc split the text before and
+after the empty line into separate paragraphs. For example:
+```%% @doc This will all be part of the first paragraph.
+ %% It can stretch over several lines and contain <em>any
+ %% XHTML markup</em>.
+ %%
+ %% This is the second paragraph. The above line is
+ %% regarded as "empty" by EDoc, even though it ends with
+ %% a space.'''
+will generate the following text:
+<blockquote><p>This will all be part of the first paragraph. It can
+stretch over several lines and contain <em>any XHTML markup</em>.</p>
+This is the second paragraph. The above line is regarded as "empty" by
+EDoc, even though it ends with a space.</blockquote>
+
+Paragraph splitting takes place after the actual XHTML parsing. It only
+affects block-level text, and not e.g., text within `<pre>' markup, or
+text that is already within `<p>' markup.
+
+ === Headings ===
+
+Section headings, sub-headings, and sub-sub-headings, can be written
+using the following notation:
+```== Heading ==
+ === Sub-heading ===
+ ==== Sub-sub-heading ===='''
+Such a heading must be alone on a line, except for whitespace, and
+cannot be split over several lines. A link target is automatically
+created for the heading, by replacing any whitespace within the text by
+a single underscore character. E.g.,
+```== Concerning Hobbits =='''
+is equivalent to
+```<h3><a name="Concerning_Hobbits">Concerning Hobbits</a></h3>'''
+Thus, headings using this notation should not contain characters that
+may not be part of URL labels, except for whitespace. If you need to
+create such headings, you have to use the explicit XHTML markup.
+
+A hypertext link to a heading written this way can be created using the
+`@section' macro, which transforms the argument text into a label as
+described above. E.g.,
+```@{@section Concerning Hobbits}'''
+is equivalent to writing
+```<a href="#Concerning_Hobbits">Concerning Hobbits</a>'''
+
+The above expansions take place before XML parsing.
+
+ === External links ===
+
+Writing a URL within brackets, as in "`[http://www.w3c.org/]'", will
+generate a hyperlink such as [http://www.w3c.org/], using the URL both
+for the destination and the label of the reference, equivalent to writing
+"`<a href="http://www.w3c.org/"><tt>http://www.w3c.org/</tt></a>'". This
+short-hand keeps external URL references short and readable. The
+recognized protocols are `http', `ftp', and `file'. This expansion takes
+place before XML parsing.
+
+ === TODO-notes ===
+
+Lines that begin with the text "`TODO:'" (the colon is required) are
+recognized as tags, as if they had been written as "`@todo ...'" (see <a
+href="#gtag-todo">@todo tags</a> for further details).
+
+ === Verbatim quoting ===
+
+In XHTML text, the '<code>&#x60;</code>' character (Unicode `000060',
+known as "grave accent" or "back-quote") can be used for verbatim
+quoting. This expansion takes place before XML parsing.
+
+<ul>
+ <li>A character sequence "<code>&#x60;...'</code>" or
+ "<code>&#x60;&#x60;...''</code>" will be expanded to
+ "`<code>...</code>'", where all occurrences of the special XML
+ characters '`<'' and '`&'' (and for completeness, also '`>'') in
+ the quoted text have been escaped to "`&lt;'", "`&amp;'", and
+ "`&gt;'", respectively.
+ All whitespace is stripped from the beginning and end of the
+ quoted text.
+
+ Double back-quotes "<code>&#x60;&#x60;...''</code>" can be used
+ to quote text containing single '`` ' ''' characters. The automatic
+ stripping of any surrounding whitespace makes it possible to write
+ things like "<code>&#x60;&#x60; 'foo@bar' ''</code>".
+
+ To quote text containing "<code>''</code>" verbatim,
+ explicit `<code>' markup or similar must be used.</li>
+
+ <li>A character sequence "<code>&#x60;&#x60;&#x60;...'''</code>"
+ will be expanded to "`<pre><![CDATA[...]]></pre>'", which disables
+ all XML markup within the quoted text, and displays the result in
+ fixed-font with preserved indentation. Whitespace is stripped from
+ the end of the quoted text, but not from the beginning, except for
+ whole leading lines of whitespace. This is
+ useful for multi-line code examples, or displayed
+ one-liners.</li>
+
+ <li>To produce a single '<code>&#x60;</code>'-character in XML
+ without beginning a new quote, you can write "<code>&#x60;'</code>"
+ (no space between the '<code>&#x60;</code>' and the '<code>'</code>').
+ You can of course also use the XML character entity
+ "`&#x60;'".</li>
+</ul>
+
+Examples:
+ ```%% @doc ...where the variable `Foo' refers to... '''
+
+ ```%% @doc ...returns the atom `` '[email protected]' ''... '''
+
+ <pre>
+ %% @doc ...use the command &#x60;&#x60;&#x60;erl -name foo''' to...</pre>
+
+ <pre>
+ %% @doc ...as in the following code:
+ %% &#x60;&#x60;&#x60;f(X) ->
+ %% case X of
+ %% ...
+ %% end'''</pre>
+
+ <pre>
+ %% @doc ...or in the following:
+ %% &#x60;&#x60;&#x60;
+ %% g(X) ->
+ %% fun () -> ... end
+ %% '''</pre>
+
+
+== Macro expansion ==
+
+Before the content of a tag is parsed, the text undergoes <em>macro
+expansion</em>. The syntax for macro calls is:
+<pre>
+ @{@<em>name</em>}</pre>
+or
+<pre>
+ @{@<em>name</em> <em>argument</em>}</pre>
+where <em>name</em> and <em>argument</em> are separated by one or more
+whitespace characters. The argument can be any text, which may contain
+other macro calls. The number of non-escaped "<code>@{@</code>" and
+"`}'" delimiters must be balanced.
+
+ The argument text is first expanded in the current environment, and
+the result is bound to the <em>macro parameter</em>, written
+<code>@{@?}</code>. (If no argument is given, <code>@{@?}</code> is
+bound to the empty string.) The macro definition is then substituted
+for the call, and expansion continues over the resulting text. Recursive
+macro expansions are not allowed.
+
+ === User-defined macros ===
+
+Users can define their own macros by using the `def' EDoc
+option; see {@link edoc:file/2} and {@link edoc:get_doc/2} for more
+information. User-defined macros override predefined macros.
+
+ === Predefined macros ===
+
+<dl>
+ <dt><a name="predefmacro-date"><code>@{@date}</code></a></dt>
+ <dd>Expands to the current date, as "<tt>Month Day Year</tt>",
+ e.g. "{@date}".</dd>
+
+ <dt><a name="predefmacro-docRoot"><code>@{@docRoot}</code></a></dt>
+ <dd>Expands to the relative URL path (such as
+ `"../../.."') from the current page to the root
+ directory of the generated documentation. This can be used to
+ create XHTML references such as `<img
+ src="@{@docRoot}/images/logo.jpeg">' that are independent of how
+ deep down in a package structure they occur. If packages are not
+ used (i.e., if all modules are in the "empty" package),
+ <code>@{@docRoot}</code> will always resolve to the empty
+ string.</dd>
+
+ <dt><a name="predefmacro-link"><code>@{@link <em>reference</em>.
+ <em>description</em>}</code></a></dt>
+ <dd>This creates a hypertext link; cf. the
+ <a href="#ftag-see">`@see' function tag</a> above for
+ details. The description text (including the period separator)
+ is optional; if no text is given, the reference itself is
+ used. For example, <code>@{@link edoc:file/2}</code> creates the
+ link {@link edoc:file/2}, and `@{@link edoc:file/2. <em>this link</em>}'
+ creates {@link edoc:file/2. <em>this link</em>}.</dd>
+
+ <dt><a name="predefmacro-module"><code>@{@module}</code></a></dt>
+ <dd>Expands to the name of the current module. Only defined when a
+ module is being processed.</dd>
+
+ <dt><a name="predefmacro-package"><code>@{@package}</code></a></dt>
+ <dd>Expands to the name of the current package.</dd>
+
+ <dt><a name="predefmacro-section"><code>@{@section
+ <em>heading</em>}</code></a></dt>
+ <dd>Expands to a hypertext link to the specified section heading;
+ see {@section Headings} for more information.</dd>
+
+ <dt><a name="predefmacro-time"><code>@{@time}</code></a></dt>
+ <dd>Expands to the current time, as "<tt>Hr:Min:Sec</tt>",
+ e.g. "{@time}".</dd>
+
+ <dt><a name="predefmacro-type"><code>@{@type
+ <em>type-expression</em>}</code></a></dt>
+ <dd>Formats a type expression within `<code>...</code>'
+ markup and with hypertext links for data types. For example,
+ <code>@{@type {options, List::edoc:option_list()@@}}</code>
+ generates "{@type {options, List::edoc:option_list()@}}". (Cf.
+ {@section Escape sequences}.)</dd>
+
+ <dt><a name="predefmacro-version"><code>@{@version}</code></a></dt>
+ <dd>Intended for use in <a href="#mtag-version">`@version'
+ tags</a>. Defaults to a timestamp using `@{@date}' and `@{@time}'.
+ Typically, this macro is redefined by the user when an official
+ release of the application is generated.</dd>
+</dl>
+
+ === Escape sequences ===
+
+To prevent certain characters from being interpreted as delimiters,
+for example to produce the text "<code>@{@</code>" in the output, or use a
+'`}'' character in the argument text of a macro call, the
+following escape sequences may be used: <dl>
+ <dt><code>@@{</code></dt>
+ <dd>Expands to "`{'". Example:
+```
+ %% @doc A macro call starts with the sequence "@@@{@".'''
+ </dd>
+ <dt><code>@@}</code></dt>
+ <dd>Expands to "`}'". Example:
+```
+ %% @doc ...@{@foo ...{Key, Value@@}...}...'''
+ </dd>
+ <dt><code>@@@@</code></dt>
+ <dd>Expands to "`@'". Example:
+```
+ %% @doc Contact us at support@@@@@{@hostname}'''
+ Will generate the text "Contact us at [email protected]"
+ if the macro `hostname' is bound to
+ "`vaporware.acme.com'". Also:
+```
+ %% @doc You might want to write something like
+ %% @@@@foo that will expand to @@foo and does not start
+ %% a new tag even if it appears first in a line.'''
+ </dd>
+</dl>
+
+
+== Type specifications ==
+
+ === Function specifications ===
+
+The following grammar describes the form of the specifications following
+a `@spec' tag. A '`?'' suffix implies that the element is optional.
+Function types have higher precedence than union types; e.g., "`(atom())
+-> atom() | integer()'" is parsed as `((atom()) -> atom()) | integer()',
+not as `(atom()) -> (atom() | integer())'.
+
+<table summary="specification syntax grammar">
+<tbody valign="baseline">
+ <tr>
+ <td><code>Spec</code></td>
+ <td>::=</td>
+ <td><code>FunType "where"? DefList?
+ <br/>| FunctionName FunType "where"? DefList?</code></td>
+ </tr>
+ <tr>
+ <td><code>FunctionName</code></td>
+ <td>::=</td>
+ <td><code>Atom</code></td>
+ </tr>
+ <tr>
+ <td><code>FunType</code></td>
+ <td>::=</td>
+ <td><code>"(" UnionTypes? ")" "->" UnionType</code></td>
+ </tr>
+ <tr>
+ <td><code>UnionTypes</code></td>
+ <td>::=</td>
+ <td><code>UnionType
+ <br/>| UnionType "," UnionTypes</code></td>
+ </tr>
+ <tr>
+ <td><code>UnionType</code></td>
+ <td>::=</td>
+ <td><code>UnionList
+ <br/>| Name "::" UnionList</code></td>
+ </tr>
+ <tr>
+ <td><code>Name</code></td>
+ <td>::=</td>
+ <td><code>Variable</code></td>
+ </tr>
+ <tr>
+ <td><code>UnionList</code></td>
+ <td>::=</td>
+ <td><code>Type
+ <br/>| Type "+" UnionList
+ <br/>| Type "|" UnionList</code></td>
+ </tr>
+ <tr>
+ <td><code>Type</code></td>
+ <td>::=</td>
+ <td><code>TypeVariable
+ <br/>| Atom
+ <br/>| Integer
+ <br/>| Float
+ <br/>| FunType
+ <br/>| "{" UnionTypes? "}"
+ <br/>| "[" "]"
+ <br/>| "[" UnionType "]"
+ <br/>| "(" UnionType ")"
+ <br/>| TypeName "(" UnionTypes? ")"
+ <br/>| ModuleName ":" TypeName "(" UnionTypes? ")"
+ <br/>| "//" AppName "/" ModuleName ":" TypeName "(" UnionTypes? ")"</code></td>
+ </tr>
+ <tr>
+ <td><code>TypeVariable</code></td>
+ <td>::=</td>
+ <td><code>Variable</code></td>
+ </tr>
+ <tr>
+ <td><code>TypeName</code></td>
+ <td>::=</td>
+ <td><code>Atom</code></td>
+ </tr>
+ <tr>
+ <td><code>ModuleName</code></td>
+ <td>::=</td>
+ <td><code>Atom
+ <br/>| ModuleName "." Atom</code></td>
+ </tr>
+ <tr>
+ <td><code>AppName</code></td>
+ <td>::=</td>
+ <td><code>Atom</code></td>
+ </tr>
+ <tr>
+ <td><code>DefList</code></td>
+ <td>::=</td>
+ <td><code>Def
+ <br/>| DefList Def
+ <br/>| DefList "," Def</code></td>
+ </tr>
+ <tr>
+ <td><code>Def</code></td>
+ <td>::=</td>
+ <td><code>TypeVariable "=" UnionType
+ <br/>| TypeName "(" TypeVariables? ")" "=" UnionType</code></td>
+ </tr>
+ <tr>
+ <td><code>TypeVariables</code></td>
+ <td>::=</td>
+ <td><code>TypeVariable
+ <br/>| TypeVariable "," TypeVariables</code></td>
+ </tr>
+</tbody>
+</table>
+
+
+Examples:
+```
+ %% @spec my_function(X::integer()) -> integer()'''
+```
+ %% @spec (X::integer()) -> integer()'''
+```
+ %% @spec sqrt(float()) -> float()'''
+```
+ %% @spec pair(S, T) -> {S, T}'''
+```
+ %% @spec append(List, List) -> List
+ %% List = [term()]'''
+```
+ %% @spec append(A::List, B::List) -> List
+ %% List = [Item]
+ %% Item = term()'''
+```
+ %% @spec open(File::filename()) -> FileDescriptor
+ %% where
+ %% filename() = string() + atom(),
+ %% FileDescriptor = term()'''
+```
+ %% @spec close(graphics:window()) -> ok'''
+
+In the above examples, `X', `A', `B',
+and `File' are parameter names, used for referring to the
+parameters from the documentation text. The <em>type variables</em>
+`S', `T' and `List' are used to
+simplify the type specifications, and may be supplied with
+definitions. It is also possible to give definitions for named types,
+which means that the name is simply an alias. (Use the
+`@type' tag to document abstract data types.) If a named type
+is defined in another module, it can be referred to as
+`Module:TypeName(...)'. Note that the keyword '`where'' is optional
+before a list of definitions, and that the definitions in the list may
+optionally be separated by '`,''.
+
+Both the '`|'' and the '`+'' character may be
+used to separate alternatives in union types; there is no semantic
+difference. Note that the notation `[Type]' means "proper
+(nil-terminated) list whose elements all belong to `Type'";
+For example, `[atom()|integer()]' means the same thing as
+`[atom()+integer()]', i.e., a proper list of atoms and/or
+integers.
+
+If only a type variable is given for a parameter, as in
+"`pair(S, T) -> ...'", the same variable name may implicitly
+be used as the parameter name; there is no need to write
+"`pair(S::S, T::T) -> ...'".
+
+EDoc automatically extracts possible parameter names from the source
+code, to be used if no parameter name is given in the specification (or
+if the specification is missing altogether). If this fails, EDoc will
+generate a dummy parameter name, such as `X1'. This way, EDoc
+can often produce helpful documentation even for code that does not
+contain any annotations at all.
+
+ === Type definitions ===
+
+The following grammar (see above for auxiliary definitions) describes
+the form of the definitions that may follow a `@type' tag:
+
+<table summary="type definition grammar">
+<tbody valign="baseline">
+ <tr>
+ <td><code>Typedef</code></td>
+ <td>::=</td>
+ <td><code>TypeName "(" TypeVariables? ")" DefList?
+ <br/>| TypeName "(" TypeVariables? ")" "=" UnionType DefList?</code></td>
+ </tr>
+</tbody>
+</table>
+
+(For a truly abstract data type, no equivalence is specified.) The main
+definition may be followed by additional local definitions. Examples:
+```
+ %% @type myList(X). A special kind of lists ...'''
+```
+ %% @type filename() = string(). Atoms not allowed!'''
+```
+ %% @type thing(A) = {thong, A}
+ %% A = term().
+ %% A kind of wrapper type thingy.'''
+
+
+ === Pre-defined data types ===
+
+The following data types are predefined by EDoc, and may not be
+redefined:
+```
+ any()
+ atom()
+ binary()
+ bool()
+ char()
+ cons()
+ deep_string()
+ float()
+ function()
+ integer()
+ list()
+ nil()
+ none()
+ number()
+ pid()
+ port()
+ reference()
+ string()
+ term()
+ tuple()
+'''
+Details:
+<ul>
+ <li>`any()' means "any Erlang data type".
+ `term()' is simply an alias for `any()'.</li>
+ <li>`atom()', `binary()',
+ `float()', `function()',
+ `integer()', `pid()', `port()'
+ and `reference()' are primitive data types of
+ the Erlang programming language.</li>
+ <li>`bool()' is the subset of `atom()' consisting
+ of the atoms `true' and `false'.</li>
+ <li>`char()' is a subset of
+ `integer()' representing character codes.</li>
+ <li>`tuple()' is the set of all tuples `{...}'.</li>
+ <li>`list(T)' is just an alias for `[T]'.</li>
+ <li>`nil()' is an alias for the empty list `[]'.</li>
+ <li>`cons(H,T)' is the list constructor. This is usually not
+ used directly. It is possible to recursively define `list(T)
+ := nil()+cons(T,list(T))'.</li>
+ <li>`string()' is an alias for `[char()]'.</li>
+ <li>`deep_string()' is recursively defined as
+ `[char()+deep_string()]'.</li>
+ <li>`none()' means "no data type". E.g., a function
+ that never returns has type `(...) -> none()'</li>
+</ul>
+
+
+== Acknowledgements ==
+
+Since the first version of EDoc, several people have come up with
+suggestions (Luke Gorrie, Joe Armstrong, Erik Stenman, Sean Hinde, Ulf
+Wiger, ...), and some have even submitted code to demonstrate their
+ideas (Vlad Dumitrescu, Johan Blom, Vijay Hirani, ...). None of that
+code was actually included in the Great Rewriting that followed the
+initial public release (EDoc version 0.1), but most of the central
+points were addressed in the new system, such as better modularization
+and possibility to plug in different layout engines, and making EDoc
+understand the application directory layout.
+
+It is now getting too hard to keep track of all the people who have made
+further suggestions or submitted bug reports, but your input is always
+appreciated. Thank you.
diff --git a/lib/edoc/doc/pdf/.gitignore b/lib/edoc/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/edoc/doc/pdf/.gitignore
diff --git a/lib/edoc/doc/src/Makefile b/lib/edoc/doc/src/Makefile
new file mode 100644
index 0000000000..8d22e1c1da
--- /dev/null
+++ b/lib/edoc/doc/src/Makefile
@@ -0,0 +1,139 @@
+# ``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 via the world wide web 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.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(EDOC_VSN)
+APPLICATION=edoc
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Man page source directory (with .erl files)
+# ----------------------------------------------------
+SRC_DIR = $(ERL_TOP)/lib/edoc/src
+INC_DIR = $(ERL_TOP)/lib/edoc/include
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+XML_REF3_FILES = \
+ edoc.xml \
+ edoc_doclet.xml \
+ edoc_extract.xml \
+ edoc_layout.xml \
+ edoc_lib.xml \
+ edoc_run.xml
+
+XML_PART_FILES = part.xml part_notes.xml
+XML_CHAPTER_FILES = chapter.xml
+XML_NOTES_FILES = notes.xml
+
+BOOK_FILES = book.xml
+
+XML_FILES=\
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES) \
+ $(XML_NOTES_FILES)
+
+# ----------------------------------------------------
+INFO_FILE = ../../info
+
+HTML_FILES = \
+ $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+EXTRA_FILES = \
+ $(DEFAULT_GIF_FILES) \
+ $(DEFAULT_HTML_FILES) \
+ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_NOTES_FILES:%.xml=$(HTMLDIR)/%.html)
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+DVIPS_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+docs: pdf html man
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+man: $(MAN3_FILES)
+
+$(XML_REF3_FILES):
+ docb_gen -def vsn $(EDOC_VSN) -includes $(INC_DIR) \
+ $(SRC_DIR)/$(@:%.xml=%.erl)
+
+$(XML_CHAPTER_FILES):
+ docb_gen -chapter -def vsn $(EDOC_VSN) ../overview.edoc
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+debug opt:
+
+clean clean_docs:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
+
+release_spec:
diff --git a/lib/edoc/doc/src/book.xml b/lib/edoc/doc/src/book.xml
new file mode 100644
index 0000000000..67b7cdb2d7
--- /dev/null
+++ b/lib/edoc/doc/src/book.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>2006</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>EDoc</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>EDoc Application</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="no">
+ <xi:include href="part.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
+
diff --git a/lib/edoc/doc/src/fascicules.xml b/lib/edoc/doc/src/fascicules.xml
new file mode 100644
index 0000000000..1b9d6bc94d
--- /dev/null
+++ b/lib/edoc/doc/src/fascicules.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
+
+<fascicules>
+ <fascicule file="part" href="part_frame.html" entry="no">
+ User's Guide
+ </fascicule>
+ <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
+ Reference Manual
+ </fascicule>
+ <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
+ Release Notes
+ </fascicule>
+</fascicules>
+
diff --git a/lib/edoc/doc/src/make.dep b/lib/edoc/doc/src/make.dep
new file mode 100644
index 0000000000..b46e36314f
--- /dev/null
+++ b/lib/edoc/doc/src/make.dep
@@ -0,0 +1,21 @@
+# ----------------------------------------------------
+# >>>> Do not edit this file <<<<
+# This file was automaticly generated by
+# /home/otp/bin/docdepend
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# TeX files that the DVI file depend on
+# ----------------------------------------------------
+
+book.dvi: book.tex chapter.tex edoc.tex edoc_doclet.tex \
+ edoc_extract.tex edoc_layout.tex edoc_lib.tex \
+ edoc_run.tex part.tex ref_man.tex
+
+# ----------------------------------------------------
+# Source inlined when transforming from source to LaTeX
+# ----------------------------------------------------
+
+book.tex: ref_man.xml
+
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
new file mode 100644
index 0000000000..8fcbc8ec70
--- /dev/null
+++ b/lib/edoc/doc/src/notes.xml
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2007</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>EDoc Release Notes</title>
+ <prepared>otp_appnotes</prepared>
+ <docno>nil</docno>
+ <date>nil</date>
+ <rev>nil</rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to the EDoc
+ application.</p>
+
+<section><title>Edoc 0.7.6.5</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation is now built with open source tools
+ (xsltproc and fop) that exists on most platforms. One
+ visible change is that the frames are removed.</p>
+ <p>
+ Own Id: OTP-8201</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.6.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Miscellaneous updates.</p>
+ <p>
+ Own Id: OTP-8190</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.6.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The copyright notices have been updated.</p>
+ <p>
+ Own Id: OTP-7851</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.6.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Minor updates.</p>
+ <p>
+ Own Id: OTP-7642</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.6.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correction to work with new versions of STDLIB that no
+ longer has the <c>erl_internal:obsolete/3</c> function.</p>
+ <p>
+ Own Id: OTP-7539</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Minor changes.</p>
+ <p>
+ Own Id: OTP-7388</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.5</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Minor updates, mostly cosmetic.</p>
+ <p>
+ Own Id: OTP-7243</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+ <section>
+ <title>Edoc 0.7.3</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Minor Makefile changes.</p>
+ <p>Own Id: OTP-6689</p>
+ </item>
+ <item>
+ <p>Dialyzer warnings were eliminated.</p>
+ <p>Own Id: OTP-6737</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>EDoc 0.7.2</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Some missing files have been added:
+ <c><![CDATA[~/include/edoc_doclet.hrl]]></c>, <c><![CDATA[~/priv/edoc.dtd]]></c>,
+ <c><![CDATA[~/priv/erlang.png]]></c></p>
+ <p>Own Id: OTP-6457</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <list type="bulleted">
+ <item>Undefined macros only cause warnings, not errors.</item>
+ <item>New, built-in <c><![CDATA[@version]]></c> macro.</item>
+ <item>Documented the <c><![CDATA[@docfile]]></c> and <c><![CDATA[@headerfile]]></c>
+ generic tags.</item>
+ <item>Added recognition of <c><![CDATA["TODO:"]]></c> as a wiki
+ equivalent for <c><![CDATA[@todo]]></c> tags.</item>
+ <item>Added documentation about overview pages.</item>
+ <item><c><![CDATA['where']]></c> and <c><![CDATA[',']]></c> are allowed as
+ separators in specs.</item>
+ <item>Corrected ambiguity in spec grammar (possible
+ incompatibility issue - parentheses may need to be added
+ in some cases, in existing code).</item>
+ <item>Experimental (and undocumented) support for
+ <c><![CDATA[@param]]></c> and <c><![CDATA[@return]]></c> tags and corresponding
+ <c><![CDATA["..."]]></c> annotations on <c><![CDATA[@spec]]></c> parameters.</item>
+ </list>
+ <p>*** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>Own Id: OTP-6568</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>EDoc 0.7.1</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Fixed some broken links in the documentation.</p>
+ <p>Own Id: OTP-6419</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>EDoc 0.7.0</title>
+ <p>Miscellaneous changes.</p>
+ </section>
+</chapter>
+
diff --git a/lib/edoc/doc/src/part.xml b/lib/edoc/doc/src/part.xml
new file mode 100644
index 0000000000..a71b4eda13
--- /dev/null
+++ b/lib/edoc/doc/src/part.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2006</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>EDoc User's Guide</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p><em>EDoc</em> is the Erlang program documentation generator.
+ Inspired by the Javadoc (TM) tool for the Java (TM) programming
+ language, EDoc is adapted to the conventions of the Erlang world,
+ and has several features not found in Javadoc.</p>
+ </description>
+ <xi:include href="chapter.xml"/>
+</part>
+
diff --git a/lib/edoc/doc/src/part_notes.xml b/lib/edoc/doc/src/part_notes.xml
new file mode 100644
index 0000000000..42fc39af42
--- /dev/null
+++ b/lib/edoc/doc/src/part_notes.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2007</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>EDoc Release Notes</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p><em>EDoc</em> is the Erlang program documentation generator.
+ Inspired by the Javadoc (TM) tool for the Java (TM) programming
+ language, EDoc is adapted to the conventions of the Erlang world,
+ and has several features not found in Javadoc.</p>
+ </description>
+ <xi:include href="notes.xml"/>
+</part>
+
diff --git a/lib/edoc/doc/src/ref_man.xml b/lib/edoc/doc/src/ref_man.xml
new file mode 100644
index 0000000000..619fbaa7ca
--- /dev/null
+++ b/lib/edoc/doc/src/ref_man.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2006</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>EDoc Reference Manual</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p><em>EDoc</em> is the Erlang program documentation generator.
+ Inspired by the Javadoc (TM) tool for the Java (TM) programming
+ language, EDoc is adapted to the conventions of the Erlang world,
+ and has several features not found in Javadoc.</p>
+ </description>
+ <xi:include href="edoc.xml"/>
+ <xi:include href="edoc_doclet.xml"/>
+ <xi:include href="edoc_extract.xml"/>
+ <xi:include href="edoc_layout.xml"/>
+ <xi:include href="edoc_lib.xml"/>
+ <xi:include href="edoc_run.xml"/>
+</application>
+
diff --git a/lib/edoc/ebin/.gitignore b/lib/edoc/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/edoc/ebin/.gitignore
diff --git a/lib/edoc/include/Makefile b/lib/edoc/include/Makefile
new file mode 100644
index 0000000000..0533c27567
--- /dev/null
+++ b/lib/edoc/include/Makefile
@@ -0,0 +1,58 @@
+# ``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 via the world wide web 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.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(EDOC_VSN)
+
+# ----------------------------------------------------
+# Release Macros
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/edoc-$(VSN)
+
+# ----------------------------------------------------
+# Macros
+# ----------------------------------------------------
+
+INCLUDE_FILES = edoc_doclet.hrl
+
+# ----------------------------------------------------
+# Make Rules
+# ----------------------------------------------------
+debug opt:
+
+clean:
+
+docs:
+
+
+# ----------------------------------------------------
+# Release Targets
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+ $(INSTALL_DIR) $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(INCLUDE_FILES) $(RELSYSDIR)/include
+
+release_docs_spec:
+
diff --git a/lib/edoc/include/edoc_doclet.hrl b/lib/edoc/include/edoc_doclet.hrl
new file mode 100644
index 0000000000..a99ff1fbab
--- /dev/null
+++ b/lib/edoc/include/edoc_doclet.hrl
@@ -0,0 +1,63 @@
+%% =====================================================================
+%% Header file for EDoc doclet modules.
+%%
+%% Copyright (C) 2001-2004 Richard Carlsson
+%%
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% Author contact: [email protected]
+%% =====================================================================
+
+-define(NO_APP, []).
+
+%% Context for doclets
+
+%% @type edoc_context() = #context{dir = string(),
+%% env = edoc_lib:edoc_env(),
+%% opts = [term()]}
+
+-record(context, {dir = "",
+ env,
+ opts = []}).
+
+%% Doclet commands
+
+%% @type no_app().
+%% A value used to mark absence of an Erlang application
+%% context. Use the macro `NO_APP' defined in
+%% <a href="../include/edoc_doclet.hrl">`edoc_doclet.hrl'</a>
+%% to produce this value.
+
+%% @type doclet_gen() = #doclet_gen{sources = [string()],
+%% app = no_app() | atom(),
+%% packages = [atom()],
+%% modules = [atom()],
+%% modules = [atom()],
+%% filemap = function()}
+
+-record(doclet_gen, {sources = [],
+ app = ?NO_APP,
+ packages = [],
+ modules = [],
+ filemap
+ }).
+
+%% @type doclet_toc() = #doclet_gen{paths = [string()],
+%% indir = string()}
+
+-record(doclet_toc, {paths,
+ indir
+ }).
diff --git a/lib/edoc/info b/lib/edoc/info
new file mode 100644
index 0000000000..cb25c1e519
--- /dev/null
+++ b/lib/edoc/info
@@ -0,0 +1,3 @@
+group: tools
+short: A utility used to generate documentation out of tags in source files.
+
diff --git a/lib/edoc/priv/Makefile b/lib/edoc/priv/Makefile
new file mode 100644
index 0000000000..13225e6d1a
--- /dev/null
+++ b/lib/edoc/priv/Makefile
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2004, Ericsson Telecommunications
+# Author: Richard Carlsson, Bertil Karlsson
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+include ../../xmerl/vsn.mk
+include ../../syntax_tools/vsn.mk
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/edoc-$(EDOC_VSN)
+
+
+#
+# Common Macros
+#
+
+GEN_SCRIPT_SRC = edoc_generate.src
+GEN_SCRIPT = edoc_generate
+PRIV_FILES = stylesheet.css erlang.png edoc.dtd
+
+debug opt:
+ sed -e "s/%EDOC_VSN%/$(EDOC_VSN)/g" \
+ -e "s/%XMERL_VSN%/$(XMERL_VSN)/g" \
+ -e "s/%SYNTAX_TOOLS_VSN%/$(SYNTAX_TOOLS_VSN)/g" \
+ $(GEN_SCRIPT_SRC) > $(GEN_SCRIPT)
+
+clean:
+ rm -f $(GEN_SCRIPT)
+ rm -f core *~
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/priv
+ $(INSTALL_DATA) $(PRIV_FILES) $(RELSYSDIR)/priv
+ $(INSTALL_SCRIPT) $(GEN_SCRIPT) $(RELSYSDIR)/priv
+
+
+release_docs_spec:
+
diff --git a/lib/edoc/priv/edoc.dtd b/lib/edoc/priv/edoc.dtd
new file mode 100644
index 0000000000..6a332cf22f
--- /dev/null
+++ b/lib/edoc/priv/edoc.dtd
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- EDoc DTD Version 0.3 -->
+
+<!ELEMENT overview (title, description?, author*, copyright?, version?,
+ since?, see*, reference*, todo?, packages, modules)>
+<!ATTLIST overview
+ root CDATA #IMPLIED>
+
+<!ELEMENT title (#PCDATA)>
+
+<!ELEMENT package (description?, author*, copyright?, version?,
+ since?, deprecated?, see*, reference*, todo?,
+ modules)>
+<!ATTLIST package
+ name CDATA #REQUIRED
+ root CDATA #IMPLIED>
+
+<!ELEMENT modules (module+)>
+
+
+<!ELEMENT module (args?, description?, author*, copyright?, version?,
+ since?, deprecated?, see*, reference*, todo?,
+ behaviour*, callbacks?, typedecls?, functions)>
+<!ATTLIST module
+ name CDATA #REQUIRED
+ private (yes | no) #IMPLIED
+ hidden (yes | no) #IMPLIED
+ root CDATA #IMPLIED>
+
+<!ELEMENT description (briefDescription, fullDescription?)>
+<!ELEMENT briefDescription (#PCDATA)>
+<!ELEMENT fullDescription (#PCDATA)>
+
+<!ELEMENT author EMPTY>
+<!ATTLIST author
+ name CDATA #REQUIRED
+ email CDATA #IMPLIED
+ website CDATA #IMPLIED>
+
+<!ELEMENT copyright (#PCDATA)>
+<!ELEMENT version (#PCDATA)>
+<!ELEMENT since (#PCDATA)>
+<!ELEMENT deprecated (description)>
+
+<!ELEMENT see (#PCDATA)>
+<!ATTLIST see
+ name CDATA #REQUIRED
+ href CDATA #IMPLIED>
+
+<!ELEMENT reference (#PCDATA)>
+
+<!ELEMENT todo (#PCDATA)>
+
+<!ELEMENT behaviour (#PCDATA)>
+<!ATTLIST behaviour
+ href CDATA #IMPLIED>
+
+<!ELEMENT callbacks (callback+)>
+<!ELEMENT callback EMPTY>
+<!ATTLIST callback
+ name CDATA #REQUIRED
+ arity CDATA #REQUIRED>
+
+<!ELEMENT typedecls (typedecl+)>
+<!ELEMENT typedecl (typedef, description?)>
+<!ATTLIST typedecl
+ label CDATA #IMPLIED>
+
+<!ELEMENT functions (function+)>
+<!ELEMENT function (args, typespec?, returns?, throws?, equiv?,
+ description?, since?, deprecated?, see*, todo?)>
+<!ATTLIST function
+ name CDATA #REQUIRED
+ arity CDATA #REQUIRED
+ exported (yes | no) #IMPLIED
+ label CDATA #IMPLIED>
+
+<!ELEMENT args (arg*)>
+<!ELEMENT arg (argName, description?)>
+<!ELEMENT argName (#PCDATA)>
+
+<!ELEMENT throws (type, localdef*)>
+<!ELEMENT returns (description)>
+
+<!ELEMENT equiv (expr, see?)>
+<!ELEMENT expr (#PCDATA)>
+
+<!ELEMENT erlangName EMPTY>
+<!ATTLIST erlangName
+ app CDATA #IMPLIED
+ module CDATA #IMPLIED
+ name CDATA #REQUIRED>
+
+
+<!-- Data type specifications -->
+
+<!ELEMENT typedef (erlangName, argtypes, type?, localdef*)>
+
+<!ELEMENT typespec (erlangName, type, localdef*)>
+
+<!ELEMENT localdef ((typevar | abstype), type)>
+<!ATTLIST localdef label CDATA #IMPLIED>
+
+<!ELEMENT argtypes (type*)>
+
+<!ELEMENT type (typevar | atom | integer | float | nil | list | tuple |
+ fun | record | abstype | union)>
+<!ATTLIST type name CDATA #IMPLIED>
+
+<!ELEMENT union (typevar | atom | integer | float | nil | list | tuple |
+ fun | record | abstype)+>
+
+<!ELEMENT typevar EMPTY>
+<!ATTLIST typevar name CDATA #REQUIRED>
+
+<!ELEMENT atom EMPTY>
+<!ATTLIST atom value CDATA #REQUIRED>
+
+<!ELEMENT integer EMPTY>
+<!ATTLIST integer value CDATA #REQUIRED>
+
+<!ELEMENT float EMPTY>
+<!ATTLIST float value CDATA #REQUIRED>
+
+<!ELEMENT nil EMPTY>
+
+<!ELEMENT list (type)>
+
+<!ELEMENT tuple (type*)>
+
+<!ELEMENT fun (argtypes, type)>
+
+<!ELEMENT record (atom, field*)>
+
+<!ELEMENT field (atom, type)>
+
+<!ELEMENT abstype (erlangName, type*)>
+<!ATTLIST abstype
+ href CDATA #IMPLIED>
diff --git a/lib/edoc/priv/edoc_generate.src b/lib/edoc/priv/edoc_generate.src
new file mode 100644
index 0000000000..e87fdbc902
--- /dev/null
+++ b/lib/edoc/priv/edoc_generate.src
@@ -0,0 +1,76 @@
+#!/bin/sh
+# ``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 via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999-2000, Ericsson
+# Utvecklings AB. All Rights Reserved.''
+#
+# $Id$
+#
+#
+
+#EDOC_DIR=/clearcase/otp/internal_tools/edoc
+EDOC_DIR=/home/otp/sgml/edoc-%EDOC_VSN%
+SYNTAX_TOOLS_DIR=/home/otp/sgml/syntax_tools-%SYNTAX_TOOLS_VSN%
+XMERL_DIR=/home/otp/sgml/xmerl-%XMERL_VSN%
+
+FILE=
+APP=
+TITLE=
+VSN=
+
+while [ $# -gt 0 ]; do
+ case $1 in
+ -file)
+ FILE=$2;
+ shift;
+ shift;;
+ -app)
+ APP=$2;
+ shift;
+ shift;;
+ -title)
+ TITLE=$2;
+ shift;
+ shift;;
+ -vsn)
+ VSN=$2;
+ shift;
+ shift;;
+ esac
+done
+
+if [ -n "$FILE" ] ; then
+ EDOC_ARGS="[{layout,otpsgml_layout},{dir,\".\"},{file_suffix,\".sgml\"},{preprocess,true},{includes,[\"$XMERL_DIR/include\"]}]"
+ erl -boot start_clean -noshell \
+ -pa $EDOC_DIR/ebin \
+ -pa $SYNTAX_TOOLS_DIR/ebin \
+ -pa $XMERL_DIR/ebin \
+ -run edoc_run file $FILE $EDOC_ARGS \
+ -s erlang halt
+elif [ -n "$APP" -a -n "$TITLE" ] ; then
+ erl -boot start_clean -noshell \
+ -pa $EDOC_DIR/ebin \
+ -pa $SYNTAX_TOOLS_DIR/ebin \
+ -pa $XMERL_DIR/ebin \
+ -run edoc_run application $APP "\".\"" "[{title,$TITLE}]" \
+ -s erlang halt
+elif [ -n "$APP" -a -n "$VSN" ] ; then
+ erl -boot start_clean -noshell \
+ -pa $EDOC_DIR/ebin \
+ -pa $SYNTAX_TOOLS_DIR/ebin \
+ -pa $XMERL_DIR/ebin \
+ -run edoc_run application $APP "\".\"" "[{def,{vsn,\"$VSN\"}}]" \
+ -s erlang halt
+else
+ echo "Usage: docb_edoc [-file Filename] | [-app Appname [-title Title |-vsn Vsn]]"
+fi
diff --git a/lib/edoc/priv/erlang.png b/lib/edoc/priv/erlang.png
new file mode 100644
index 0000000000..987a618e24
--- /dev/null
+++ b/lib/edoc/priv/erlang.png
Binary files differ
diff --git a/lib/edoc/priv/stylesheet.css b/lib/edoc/priv/stylesheet.css
new file mode 100644
index 0000000000..e426a90483
--- /dev/null
+++ b/lib/edoc/priv/stylesheet.css
@@ -0,0 +1,55 @@
+/* standard EDoc style sheet */
+body {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ margin-left: .25in;
+ margin-right: .2in;
+ margin-top: 0.2in;
+ margin-bottom: 0.2in;
+ color: #000000;
+ background-color: #ffffff;
+}
+h1,h2 {
+ margin-left: -0.2in;
+}
+div.navbar {
+ background-color: #add8e6;
+ padding: 0.2em;
+}
+h2.indextitle {
+ padding: 0.4em;
+ background-color: #add8e6;
+}
+h3.function,h3.typedecl {
+ background-color: #add8e6;
+ padding-left: 1em;
+}
+div.spec {
+ margin-left: 2em;
+ background-color: #eeeeee;
+}
+a.module,a.package {
+ text-decoration:none
+}
+a.module:hover,a.package:hover {
+ background-color: #eeeeee;
+}
+ul.definitions {
+ list-style-type: none;
+}
+ul.index {
+ list-style-type: none;
+ background-color: #eeeeee;
+}
+
+/*
+ * Minor style tweaks
+ */
+ul {
+ list-style-type: square;
+}
+table {
+ border-collapse: collapse;
+}
+td {
+ padding: 3
+}
diff --git a/lib/edoc/src/Makefile b/lib/edoc/src/Makefile
new file mode 100644
index 0000000000..fd0fbac37d
--- /dev/null
+++ b/lib/edoc/src/Makefile
@@ -0,0 +1,91 @@
+#
+# Copyright (C) 2004, Ericsson Telecommunications
+# Author: Richard Carlsson, Bertil Karlsson
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(EDOC_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/edoc-$(VSN)
+
+
+#
+# Common Macros
+#
+
+EBIN = ../ebin
+XMERL = ../../xmerl
+ERL_COMPILE_FLAGS += -I../include -I$(XMERL)/include +warn_unused_vars +nowarn_shadow_vars +warn_unused_import +warn_deprecated_guard
+
+SOURCES= \
+ edoc.erl edoc_data.erl edoc_doclet.erl edoc_extract.erl \
+ edoc_layout.erl edoc_lib.erl edoc_macros.erl edoc_parser.erl \
+ edoc_refs.erl edoc_report.erl edoc_run.erl edoc_scanner.erl \
+ edoc_tags.erl edoc_types.erl edoc_wiki.erl otpsgml_layout.erl
+
+OBJECTS=$(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+HRL_FILES = edoc.hrl edoc_types.hrl ../include/edoc_doclet.hrl
+
+YRL_FILE = edoc_parser.yrl
+
+APP_FILE= edoc.app
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+
+APPUP_FILE= edoc.appup
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(OBJECTS)
+
+all: $(OBJECTS)
+
+$(OBJECTS): $(HRL_FILES) $(XMERL)/include/xmerl.hrl
+
+clean:
+ rm -f $(OBJECTS) edoc_parser.erl
+ rm -f core *~
+
+distclean: clean
+
+realclean: clean
+
+$(EBIN)/%.$(EMULATOR):%.erl
+ erlc -W $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(OBJECTS) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(SOURCES) $(HRL_FILES) $(YRL_FILE) $(RELSYSDIR)/src
+
+release_docs_spec:
+
diff --git a/lib/edoc/src/edoc.app.src b/lib/edoc/src/edoc.app.src
new file mode 100644
index 0000000000..2177533441
--- /dev/null
+++ b/lib/edoc/src/edoc.app.src
@@ -0,0 +1,24 @@
+% This is an -*- erlang -*- file.
+
+{application, edoc,
+ [{description, "EDoc"},
+ {vsn, "%VSN%"},
+ {modules, [edoc,
+ edoc_data,
+ edoc_doclet,
+ edoc_extract,
+ edoc_layout,
+ edoc_lib,
+ edoc_macros,
+ edoc_parser,
+ edoc_refs,
+ edoc_report,
+ edoc_run,
+ edoc_scanner,
+ edoc_tags,
+ edoc_types,
+ edoc_wiki,
+ otpsgml_layout]},
+ {registered,[]},
+ {applications, [compiler,kernel,stdlib,syntax_tools]},
+ {env, []}]}.
diff --git a/lib/edoc/src/edoc.appup.src b/lib/edoc/src/edoc.appup.src
new file mode 100644
index 0000000000..54a63833e6
--- /dev/null
+++ b/lib/edoc/src/edoc.appup.src
@@ -0,0 +1 @@
+{"%VSN%",[],[]}.
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
new file mode 100644
index 0000000000..ec452a5929
--- /dev/null
+++ b/lib/edoc/src/edoc.erl
@@ -0,0 +1,771 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @copyright 2001-2007 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @version {@version}
+%% @end
+%% =====================================================================
+
+%% TODO: check weirdness in name generation for @spec f(TypeName, ...) -> ...
+%% TODO: option for ignoring functions matching some pattern ('..._test_'/0)
+%% TODO: @private_type tag, opaque unless generating private docs?
+%% TODO: document the record type syntax
+%% TODO: some 'skip' option for ignoring particular modules/packages?
+%% TODO: intermediate-level packages: document even if no local sources.
+%% TODO: multiline comment support (needs modified comment representation)
+%% TODO: config-file for default settings
+%% TODO: config: locations of all local docdirs; generate local doc-index page
+%% TODO: config: URL:s of offline packages/apps
+%% TODO: config: default stylesheet
+%% TODO: config: default header/footer, etc.
+%% TODO: offline linkage
+%% TODO: including source code, explicitly and/or automatically
+
+%% @doc EDoc - the Erlang program documentation generator.
+%%
+%% This module provides the main user interface to EDoc.
+%% <ul>
+%% <li><a href="overview-summary.html">EDoc User Manual</a></li>
+%% <li><a href="overview-summary.html#Running_EDoc">Running EDoc</a></li>
+%% </ul>
+
+-module(edoc).
+
+-export([packages/1, packages/2, files/1, files/2,
+ application/1, application/2, application/3,
+ toc/1, toc/2, toc/3,
+ run/3,
+ file/1, file/2,
+ read/1, read/2,
+ layout/1, layout/2,
+ get_doc/1, get_doc/2, get_doc/3,
+ read_comments/1, read_comments/2,
+ read_source/1, read_source/2]).
+
+-import(edoc_report, [report/2, report/3, error/1, error/3]).
+
+-include("edoc.hrl").
+
+
+%% @spec (Name::filename()) -> ok
+%% @equiv file(Name, [])
+%% @deprecated See {@link file/2} for details.
+
+file(Name) ->
+ file(Name, []).
+
+%% @spec file(filename(), proplist()) -> ok
+%%
+%% @type filename() = //kernel/file:filename()
+%% @type proplist() = [term()]
+%%
+%% @deprecated This is part of the old interface to EDoc and is mainly
+%% kept for backwards compatibility. The preferred way of generating
+%% documentation is through one of the functions {@link application/2},
+%% {@link packages/2} and {@link files/2}.
+%%
+%% @doc Reads a source code file and outputs formatted documentation to
+%% a corresponding file.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {dir, filename()@}}
+%% </dt>
+%% <dd>Specifies the output directory for the created file. (By
+%% default, the output is written to the directory of the source
+%% file.)
+%% </dd>
+%% <dt>{@type {source_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the expected suffix of the input file. The default
+%% value is `".erl"'.
+%% </dd>
+%% <dt>{@type {file_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the suffix for the created file. The default value is
+%% `".html"'.
+%% </dd>
+%% </dl>
+%%
+%% See {@link get_doc/2} and {@link layout/2} for further
+%% options.
+%%
+%% For running EDoc from a Makefile or similar, see
+%% {@link edoc_run:file/1}.
+%%
+%% @see read/2
+
+%% NEW-OPTIONS: source_suffix, file_suffix, dir
+%% INHERIT-OPTIONS: read/2
+
+file(Name, Options) ->
+ Text = read(Name, Options),
+ SrcSuffix = proplists:get_value(source_suffix, Options,
+ ?DEFAULT_SOURCE_SUFFIX),
+ BaseName = filename:basename(Name, SrcSuffix),
+ Suffix = proplists:get_value(file_suffix, Options,
+ ?DEFAULT_FILE_SUFFIX),
+ Dir = proplists:get_value(dir, Options, filename:dirname(Name)),
+ edoc_lib:write_file(Text, Dir, BaseName ++ Suffix).
+
+
+%% TODO: better documentation of files/1/2, packages/1/2, application/1/2/3
+
+%% @spec (Files::[filename() | {package(), [filename()]}]) -> ok
+%% @equiv packages(Packages, [])
+
+files(Files) ->
+ files(Files, []).
+
+%% @spec (Files::[filename() | {package(), [filename()]}],
+%% Options::proplist()) -> ok
+%% @doc Runs EDoc on a given set of source files. See {@link run/3} for
+%% details, including options.
+%% @equiv run([], Files, Options)
+
+files(Files, Options) ->
+ run([], Files, Options).
+
+%% @spec (Packages::[package()]) -> ok
+%% @equiv packages(Packages, [])
+
+packages(Packages) ->
+ packages(Packages, []).
+
+%% @spec (Packages::[package()], Options::proplist()) -> ok
+%% @type package() = atom() | string()
+%%
+%% @doc Runs EDoc on a set of packages. The `source_path' option is used
+%% to locate the files; see {@link run/3} for details, including
+%% options. This function automatically appends the current directory to
+%% the source path.
+%%
+%% @equiv run(Packages, [], Options)
+
+packages(Packages, Options) ->
+ run(Packages, [], Options ++ [{source_path, [?CURRENT_DIR]}]).
+
+%% @spec (Application::atom()) -> ok
+%% @equiv application(Application, [])
+
+application(App) ->
+ application(App, []).
+
+%% @spec (Application::atom(), Options::proplist()) -> ok
+%% @doc Run EDoc on an application in its default app-directory. See
+%% {@link application/3} for details.
+%% @see application/1
+
+application(App, Options) when is_atom(App) ->
+ case code:lib_dir(App) of
+ Dir when is_list(Dir) ->
+ application(App, Dir, Options);
+ _ ->
+ report("cannot find application directory for '~s'.",
+ [App]),
+ exit(error)
+ end.
+
+%% @spec (Application::atom(), Dir::filename(), Options::proplist())
+%% -> ok
+%% @doc Run EDoc on an application located in the specified directory.
+%% Tries to automatically set up good defaults. Unless the user
+%% specifies otherwise:
+%% <ul>
+%% <li>The `doc' subdirectory will be used as the target directory, if
+%% it exists; otherwise the application directory is used.
+%% </li>
+%% <li>The source code is assumed to be located in the `src'
+%% subdirectory, if it exists, or otherwise in the application
+%% directory itself.
+%% </li>
+%% <li>The {@link run/3. `subpackages'} option is turned on. All found
+%% source files will be processed.
+%% </li>
+%% <li>The `include' subdirectory is automatically added to the
+%% include path. (Only important if {@link read_source/2.
+%% preprocessing} is turned on.)
+%% </li>
+%% </ul>
+%%
+%% See {@link run/3} for details, including options.
+%%
+%% @see application/2
+
+application(App, Dir, Options) when is_atom(App) ->
+ Src = edoc_lib:try_subdir(Dir, ?SOURCE_DIR),
+ Overview = filename:join(edoc_lib:try_subdir(Dir, ?EDOC_DIR),
+ ?OVERVIEW_FILE),
+ Opts = Options ++ [{source_path, [Src]},
+ subpackages,
+ {title, io_lib:fwrite("The ~s application", [App])},
+ {overview, Overview},
+ {dir, filename:join(Dir, ?EDOC_DIR)},
+ {includes, [filename:join(Dir, "include")]}],
+ Opts1 = set_app_default(App, Dir, Opts),
+ %% Recursively document all subpackages of '' - i.e., everything.
+ run([''], [], [{application, App} | Opts1]).
+
+%% Try to set up a default application base URI in a smart way if the
+%% user has not specified it explicitly.
+
+set_app_default(App, Dir0, Opts) ->
+ case proplists:get_value(app_default, Opts) of
+ undefined ->
+ AppName = atom_to_list(App),
+ Dir = edoc_lib:simplify_path(filename:absname(Dir0)),
+ AppDir = case filename:basename(Dir) of
+ AppName ->
+ filename:dirname(Dir);
+ _ ->
+ ?APP_DEFAULT
+ end,
+ [{app_default, AppDir} | Opts];
+ _ ->
+ Opts
+ end.
+
+%% If no source files are found for a (specified) package, no package
+%% documentation will be generated either (even if there is a
+%% package-documentation file). This is the way it should be. For
+%% specified files, use empty package (unless otherwise specified). The
+%% assumed package is always used for creating the output. If the actual
+%% module or package of the source differs from the assumption gathered
+%% from the path and file name, a warning should be issued (since links
+%% are likely to be incorrect).
+
+opt_defaults() ->
+ [packages].
+
+opt_negations() ->
+ [{no_preprocess, preprocess},
+ {no_subpackages, subpackages},
+ {no_packages, packages}].
+
+%% @spec run(Packages::[package()],
+%% Files::[filename() | {package(), [filename()]}],
+%% Options::proplist()) -> ok
+%% @doc Runs EDoc on a given set of source files and/or packages. Note
+%% that the doclet plugin module has its own particular options; see the
+%% `doclet' option below.
+%%
+%% Also see {@link layout/2} for layout-related options, and
+%% {@link get_doc/2} for options related to reading source
+%% files.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {app_default, string()@}}
+%% </dt>
+%% <dd>Specifies the default base URI for unknown applications.
+%% </dd>
+%% <dt>{@type {application, App::atom()@}}
+%% </dt>
+%% <dd>Specifies that the generated documentation describes the
+%% application `App'. This mainly affects generated references.
+%% </dd>
+%% <dt>{@type {dir, filename()@}}
+%% </dt>
+%% <dd>Specifies the target directory for the generated documentation.
+%% </dd>
+%% <dt>{@type {doc_path, [string()]@}}
+%% </dt>
+%% <dd>Specifies a list of URI:s pointing to directories that contain
+%% EDoc-generated documentation. URI without a `scheme://' part are
+%% taken as relative to `file://'. (Note that such paths must use
+%% `/' as separator, regardless of the host operating system.)
+%% </dd>
+%% <dt>{@type {doclet, Module::atom()@}}
+%% </dt>
+%% <dd>Specifies a callback module to be used for creating the
+%% documentation. The module must export a function `run(Cmd, Ctxt)'.
+%% The default doclet module is {@link edoc_doclet}; see {@link
+%% edoc_doclet:run/2} for doclet-specific options.
+%% </dd>
+%% <dt>{@type {exclude_packages, [package()]@}}
+%% </dt>
+%% <dd>Lists packages to be excluded from the documentation. Typically
+%% used in conjunction with the `subpackages' option.
+%% </dd>
+%% <dt>{@type {file_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the suffix used for output files. The default value is
+%% `".html"'. Note that this also affects generated references.
+%% </dd>
+%% <dt>{@type {new, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', any existing `edoc-info' file in the
+%% target directory will be ignored and overwritten. The default
+%% value is `false'.
+%% </dd>
+%% <dt>{@type {packages, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', it it assumed that packages (module
+%% namespaces) are being used, and that the source code directory
+%% structure reflects this. The default value is `true'. (Usually,
+%% this does the right thing even if all the modules belong to the
+%% top-level "empty" package.) `no_packages' is an alias for
+%% `{packages, false}'. See the `subpackages' option below for
+%% further details.
+%%
+%% If the source code is organized in a hierarchy of
+%% subdirectories although it does not use packages, use
+%% `no_packages' together with the recursive-search `subpackages'
+%% option (on by default) to automatically generate documentation
+%% for all the modules.
+%% </dd>
+%% <dt>{@type {source_path, [filename()]@}}
+%% </dt>
+%% <dd>Specifies a list of file system paths used to locate the source
+%% code for packages.
+%% </dd>
+%% <dt>{@type {source_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the expected suffix of input files. The default
+%% value is `".erl"'.
+%% </dd>
+%% <dt>{@type {subpackages, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', all subpackages of specified packages
+%% will also be included in the documentation. The default value is
+%% `false'. `no_subpackages' is an alias for `{subpackages,
+%% false}'. See also the `exclude_packages' option.
+%%
+%% Subpackage source files are found by recursively searching
+%% for source code files in subdirectories of the known source code
+%% root directories. (Also see the `source_path' option.) Directory
+%% names must begin with a lowercase letter and contain only
+%% alphanumeric characters and underscore, or they will be ignored.
+%% (For example, a subdirectory named `test-files' will not be
+%% searched.)
+%% </dd>
+%% </dl>
+%%
+%% @see files/2
+%% @see packages/2
+%% @see application/2
+
+%% NEW-OPTIONS: source_path, application
+%% INHERIT-OPTIONS: init_context/1
+%% INHERIT-OPTIONS: expand_sources/2
+%% INHERIT-OPTIONS: target_dir_info/5
+%% INHERIT-OPTIONS: edoc_lib:find_sources/3
+%% INHERIT-OPTIONS: edoc_lib:run_doclet/2
+%% INHERIT-OPTIONS: edoc_lib:get_doc_env/4
+
+run(Packages, Files, Opts0) ->
+ Opts = expand_opts(Opts0),
+ Ctxt = init_context(Opts),
+ Dir = Ctxt#context.dir,
+ Path = proplists:append_values(source_path, Opts),
+ Ss = sources(Path, Packages, Opts),
+ {Ss1, Ms} = expand_sources(expand_files(Files) ++ Ss, Opts),
+ Ps = [P || {_, P, _, _} <- Ss1],
+ App = proplists:get_value(application, Opts, ?NO_APP),
+ {App1, Ps1, Ms1} = target_dir_info(Dir, App, Ps, Ms, Opts),
+ %% The "empty package" is never included in the list of packages.
+ Ps2 = edoc_lib:unique(lists:sort(Ps1)) -- [''],
+ Ms2 = edoc_lib:unique(lists:sort(Ms1)),
+ Fs = package_files(Path, Ps2),
+ Env = edoc_lib:get_doc_env(App1, Ps2, Ms2, Opts),
+ Ctxt1 = Ctxt#context{env = Env},
+ Cmd = #doclet_gen{sources = Ss1,
+ app = App1,
+ packages = Ps2,
+ modules = Ms2,
+ filemap = Fs
+ },
+ F = fun (M) ->
+ M:run(Cmd, Ctxt1)
+ end,
+ edoc_lib:run_doclet(F, Opts).
+
+expand_opts(Opts0) ->
+ proplists:substitute_negations(opt_negations(),
+ Opts0 ++ opt_defaults()).
+
+%% NEW-OPTIONS: dir
+%% DEFER-OPTIONS: run/3
+
+init_context(Opts) ->
+ #context{dir = proplists:get_value(dir, Opts, ?CURRENT_DIR),
+ opts = Opts
+ }.
+
+%% INHERIT-OPTIONS: edoc_lib:find_sources/3
+
+sources(Path, Packages, Opts) ->
+ lists:foldl(fun (P, Xs) ->
+ edoc_lib:find_sources(Path, P, Opts) ++ Xs
+ end,
+ [], Packages).
+
+package_files(Path, Packages) ->
+ Name = ?PACKAGE_FILE, % this is hard-coded for now
+ D = lists:foldl(fun (P, D) ->
+ F = edoc_lib:find_file(Path, P, Name),
+ dict:store(P, F, D)
+ end,
+ dict:new(), Packages),
+ fun (P) ->
+ case dict:find(P, D) of
+ {ok, F} -> F;
+ error -> ""
+ end
+ end.
+
+%% Expand user-specified sets of files.
+
+expand_files([{P, Fs1} | Fs]) ->
+ [{P, filename:basename(F), filename:dirname(F)} || F <- Fs1]
+ ++ expand_files(Fs);
+expand_files([F | Fs]) ->
+ [{'', filename:basename(F), filename:dirname(F)} |
+ expand_files(Fs)];
+expand_files([]) ->
+ [].
+
+%% Create the (assumed) full module names. Keep only the first source
+%% for each module, but preserve the order of the list.
+
+%% NEW-OPTIONS: source_suffix, packages
+%% DEFER-OPTIONS: run/3
+
+expand_sources(Ss, Opts) ->
+ Suffix = proplists:get_value(source_suffix, Opts,
+ ?DEFAULT_SOURCE_SUFFIX),
+ Ss1 = case proplists:get_bool(packages, Opts) of
+ true -> Ss;
+ false -> [{'',F,D} || {_P,F,D} <- Ss]
+ end,
+ expand_sources(Ss1, Suffix, sets:new(), [], []).
+
+expand_sources([{P, F, D} | Fs], Suffix, S, As, Ms) ->
+ M = list_to_atom(packages:concat(P, filename:rootname(F, Suffix))),
+ case sets:is_element(M, S) of
+ true ->
+ expand_sources(Fs, Suffix, S, As, Ms);
+ false ->
+ S1 = sets:add_element(M, S),
+ expand_sources(Fs, Suffix, S1, [{M, P, F, D} | As],
+ [M | Ms])
+ end;
+expand_sources([], _Suffix, _S, As, Ms) ->
+ {lists:reverse(As), lists:reverse(Ms)}.
+
+%% NEW-OPTIONS: new
+
+target_dir_info(Dir, App, Ps, Ms, Opts) ->
+ case proplists:get_bool(new, Opts) of
+ true ->
+ {App, Ps, Ms};
+ false ->
+ {App1, Ps1, Ms1} = edoc_lib:read_info_file(Dir),
+ {if App == ?NO_APP -> App1;
+ true -> App
+ end,
+ Ps ++ Ps1,
+ Ms ++ Ms1}
+ end.
+
+
+%% @hidden Not official yet
+
+toc(Dir) ->
+ toc(Dir, []).
+
+%% @equiv toc(Dir, Paths, [])
+%% @hidden Not official yet
+
+%% NEW-OPTIONS: doc_path
+
+toc(Dir, Opts) ->
+ Paths = proplists:append_values(doc_path, Opts)
+ ++ edoc_lib:find_doc_dirs(),
+ toc(Dir, Paths, Opts).
+
+%% @doc Create a meta-level table of contents.
+%% @hidden Not official yet
+
+%% INHERIT-OPTIONS: init_context/1
+%% INHERIT-OPTIONS: edoc_lib:run_doclet/2
+%% INHERIT-OPTIONS: edoc_lib:get_doc_env/4
+
+toc(Dir, Paths, Opts0) ->
+ Opts = expand_opts(Opts0 ++ [{dir, Dir}]),
+ Ctxt = init_context(Opts),
+ Env = edoc_lib:get_doc_env('', [], [], Opts),
+ Ctxt1 = Ctxt#context{env = Env},
+ F = fun (M) ->
+ M:run(#doclet_toc{paths=Paths}, Ctxt1)
+ end,
+ edoc_lib:run_doclet(F, Opts).
+
+
+%% @spec read(File::filename()) -> string()
+%% @equiv read(File, [])
+
+read(File) ->
+ read(File, []).
+
+%% @spec read(File::filename(), Options::proplist()) -> string()
+%%
+%% @doc Reads and processes a source file and returns the resulting
+%% EDoc-text as a string. See {@link get_doc/2} and {@link layout/2} for
+%% options.
+%%
+%% @see file/2
+
+%% INHERIT-OPTIONS: get_doc/2, layout/2
+
+read(File, Opts) ->
+ {_ModuleName, Doc} = get_doc(File, Opts),
+ layout(Doc, Opts).
+
+
+%% @spec layout(Doc::edoc_module()) -> string()
+%% @equiv layout(Doc, [])
+
+layout(Doc) ->
+ layout(Doc, []).
+
+%% @spec layout(Doc::edoc_module(), Options::proplist()) -> string()
+%%
+%% @doc Transforms EDoc module documentation data to text. The default
+%% layout creates an HTML document.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {layout, Module::atom()@}}
+%% </dt>
+%% <dd>Specifies a callback module to be used for formatting. The
+%% module must export a function `module(Doc, Options)'. The
+%% default callback module is {@link edoc_layout}; see {@link
+%% edoc_layout:module/2} for layout-specific options.
+%% </dd>
+%% </dl>
+%%
+%% @see layout/1
+%% @see run/3
+%% @see read/2
+%% @see file/2
+
+%% INHERIT-OPTIONS: edoc_lib:run_layout/2
+
+layout(Doc, Opts) ->
+ F = fun (M) ->
+ M:module(Doc, Opts)
+ end,
+ edoc_lib:run_layout(F, Opts).
+
+
+%% @spec (File) -> [comment()]
+%% @equiv read_comments(File, [])
+
+read_comments(File) ->
+ read_comments(File, []).
+
+%% @spec read_comments(File::filename(), Options::proplist()) ->
+%% [comment()]
+%% where
+%% comment() = {Line, Column, Indentation, Text},
+%% Line = integer(),
+%% Column = integer(),
+%% Indentation = integer(),
+%% Text = [string()]
+%%
+%% @doc Extracts comments from an Erlang source code file. See the
+%% module {@link //syntax_tools/erl_comment_scan} for details on the
+%% representation of comments. Currently, no options are avaliable.
+
+read_comments(File, _Opts) ->
+ erl_comment_scan:file(File).
+
+
+%% @spec (File) -> [syntaxTree()]
+%% @equiv read_source(File, [])
+
+read_source(Name) ->
+ read_source(Name, []).
+
+%% @spec read_source(File::filename(), Options::proplist()) ->
+%% [syntaxTree()]
+%%
+%% @type syntaxTree() = //syntax_tools/erl_syntax:syntaxTree()
+%%
+%% @doc Reads an Erlang source file and returns the list of "source code
+%% form" syntax trees.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {preprocess, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', the source file will be read via the
+%% Erlang preprocessor (`epp'). The default value is `false'.
+%% `no_preprocess' is an alias for `{preprocess, false}'.
+%%
+%% Normally, preprocessing is not necessary for EDoc to work, but
+%% if a file contains too exotic definitions or uses of macros, it
+%% will not be possible to read it without preprocessing. <em>Note:
+%% comments in included files will not be available to EDoc, even
+%% with this option enabled.</em>
+%% </dd>
+%% <dt>{@type {includes, Path::[string()]@}}
+%% </dt>
+%% <dd>Specifies a list of directory names to be searched for include
+%% files, if the `preprocess' option is turned on. Also used with
+%% the `@headerfile' tag. The default value is the empty list. The
+%% directory of the source file is always automatically appended to
+%% the search path.
+%% </dd>
+%% <dt>{@type {macros, [{atom(), term()@}]@}}
+%% </dt>
+%% <dd>Specifies a list of pre-defined Erlang preprocessor (`epp')
+%% macro definitions, used if the `preprocess' option is turned on.
+%% The default value is the empty list.</dd>
+%% </dl>
+%%
+%% @see get_doc/2
+%% @see //syntax_tools/erl_syntax
+
+%% NEW-OPTIONS: [no_]preprocess (preprocess -> includes, macros)
+
+read_source(Name, Opts0) ->
+ Opts = expand_opts(Opts0),
+ case read_source_1(Name, Opts) of
+ {ok, Forms} ->
+ check_forms(Forms, Name),
+ Forms;
+ {error, R} ->
+ error({"error reading file '~s'.",
+ [edoc_lib:filename(Name)]}),
+ exit({error, R})
+ end.
+
+read_source_1(Name, Opts) ->
+ case proplists:get_bool(preprocess, Opts) of
+ true ->
+ read_source_2(Name, Opts);
+ false ->
+ epp_dodger:quick_parse_file(Name, Opts ++ [{no_fail, false}])
+ end.
+
+read_source_2(Name, Opts) ->
+ Includes = proplists:append_values(includes, Opts)
+ ++ [filename:dirname(Name)],
+ Macros = proplists:append_values(macros, Opts),
+ epp:parse_file(Name, Includes, Macros).
+
+check_forms(Fs, Name) ->
+ Fun = fun (F) ->
+ case erl_syntax:type(F) of
+ error_marker ->
+ case erl_syntax:error_marker_info(F) of
+ {L, M, D} ->
+ error(L, Name, {format_error, M, D});
+
+ Other ->
+ report(Name, "unknown error in "
+ "source code: ~w.", [Other])
+ end,
+ exit(error);
+ _ ->
+ ok
+ end
+ end,
+ lists:foreach(Fun, Fs).
+
+
+%% @spec get_doc(File::filename()) -> {ModuleName, edoc_module()}
+%% @equiv get_doc(File, [])
+
+get_doc(File) ->
+ get_doc(File, []).
+
+%% @spec get_doc(File::filename(), Options::proplist()) ->
+%% {ModuleName, edoc_module()}
+%% ModuleName = atom()
+%%
+%% @type edoc_module(). The EDoc documentation data for a module,
+%% expressed as an XML document in {@link //xmerl. XMerL} format. See
+%% the file <a href="../priv/edoc.dtd">`edoc.dtd'</a> for details.
+%%
+%% @doc Reads a source code file and extracts EDoc documentation data.
+%% Note that without an environment parameter (see {@link get_doc/3}),
+%% hypertext links may not be correct.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {def, Macros@}}
+%% </dt>
+%% <dd><ul>
+%% <li>`Macros' = {@type Macro | [Macro]}</li>
+%% <li>`Macro' = {@type {Name::atom(), Text::string()@}}</li>
+%% </ul>
+%% Specifies a set of EDoc macro definitions. See
+%% <a href="overview-summary.html#Macro_expansion">Inline macro expansion</a>
+%% for details.
+%% </dd>
+%% <dt>{@type {hidden, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', documentation of hidden functions will
+%% also be included. The default value is `false'.
+%% </dd>
+%% <dt>{@type {private, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', documentation of private functions will
+%% also be included. The default value is `false'.
+%% </dd>
+%% <dt>{@type {todo, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', To-Do notes written using `@todo' or
+%% `@TODO' tags will be included in the documentation. The default
+%% value is `false'.
+%% </dd>
+%% </dl>
+%%
+%% See {@link read_source/2}, {@link read_comments/2} and {@link
+%% edoc_lib:get_doc_env/4} for further options.
+%%
+%% @see get_doc/3
+%% @see run/3
+%% @see edoc_extract:source/5
+%% @see read/2
+%% @see layout/2
+
+%% INHERIT-OPTIONS: get_doc/3
+%% INHERIT-OPTIONS: edoc_lib:get_doc_env/4
+
+get_doc(File, Opts) ->
+ Env = edoc_lib:get_doc_env(Opts),
+ get_doc(File, Env, Opts).
+
+%% @spec get_doc(File::filename(), Env::edoc_lib:edoc_env(),
+%% Options::proplist()) -> {ModuleName, edoc_module()}
+%% ModuleName = atom()
+%%
+%% @doc Like {@link get_doc/2}, but for a given environment
+%% parameter. `Env' is an environment created by {@link
+%% edoc_lib:get_doc_env/4}.
+
+%% INHERIT-OPTIONS: read_source/2, read_comments/2, edoc_extract:source/5
+%% DEFER-OPTIONS: get_doc/2
+
+get_doc(File, Env, Opts) ->
+ edoc_extract:source(File, Env, Opts).
diff --git a/lib/edoc/src/edoc.hrl b/lib/edoc/src/edoc.hrl
new file mode 100644
index 0000000000..71cc1a52b9
--- /dev/null
+++ b/lib/edoc/src/edoc.hrl
@@ -0,0 +1,100 @@
+%% =====================================================================
+%% Header file for EDoc
+%%
+%% Copyright (C) 2001-2004 Richard Carlsson
+%%
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% Author contact: [email protected]
+%% =====================================================================
+
+%% Note: Documentation in this file is included by edoc_extract.erl
+
+-define(APPLICATION, edoc).
+-define(INFO_FILE, "edoc-info").
+-define(PACKAGE_FILE, "package.edoc").
+-define(OVERVIEW_FILE, "overview.edoc").
+-define(PACKAGE_SUMMARY, "package-summary").
+-define(DEFAULT_SOURCE_SUFFIX, ".erl").
+-define(DEFAULT_FILE_SUFFIX, ".html").
+-define(DEFAULT_DOCLET, edoc_doclet).
+-define(DEFAULT_LAYOUT, edoc_layout).
+-define(APP_DEFAULT, "http://www.erlang.org/edoc/doc").
+-define(CURRENT_DIR, ".").
+-define(SOURCE_DIR, "src").
+-define(EBIN_DIR, "ebin").
+-define(EDOC_DIR, "doc").
+
+-include("edoc_doclet.hrl").
+
+%% Module information
+
+%% @type module() = #module{name = [] | atom(),
+%% parameters = none | [atom()],
+%% functions = ordset(function_name()),
+%% exports = ordset(function_name()),
+%% attributes = ordset({atom(), term()}),
+%% records = [{atom(), [{atom(), term()}]}]}
+%% ordset(T) = sets:ordset(T)
+%% function_name(T) = {atom(), integer()}
+
+-record(module, {name = [],
+ parameters = none,
+ functions = [],
+ exports = [],
+ attributes = [],
+ records = []
+ }).
+
+%% Environment for generating documentation data
+
+-record(env, {module = [],
+ package = [],
+ root = "",
+ file_suffix,
+ package_summary,
+ apps,
+ modules,
+ packages,
+ app_default,
+ macros = [],
+ includes = []
+ }).
+
+%% Simplified comment data
+
+%% @type comment() = #comment{line = integer(),
+%% text = string()}
+
+-record(comment, {line = 0, text}).
+
+%% Module Entries (one per function, plus module header and footer)
+
+%% @type entry() = #entry{name = atom(),
+%% args = [string()],
+%% line = integer(),
+%% export = bool(),
+%% data = term()}
+
+-record(entry, {name, args = [], line = 0, export, data}).
+
+%% Generic tag information
+
+%% @type tag() = #tag{name = atom(),
+%% line = integer(),
+%% data = term()}
+
+-record(tag, {name, line = 0, data}).
diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl
new file mode 100644
index 0000000000..124f8eb9a1
--- /dev/null
+++ b/lib/edoc/src/edoc_data.erl
@@ -0,0 +1,545 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright 2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc Building the EDoc external data structure. See the file
+%% <a href="../priv/edoc.dtd">`edoc.dtd'</a> for details.
+
+-module(edoc_data).
+
+-export([module/4, package/4, overview/4, type/2]).
+
+-include("edoc.hrl").
+
+%% TODO: report multiple definitions of the same type in the same module.
+%% TODO: check that variables in @equiv are found in the signature
+%% TODO: copy types from target (if missing) when using @equiv
+
+%% <!ELEMENT module (args?, description?, author*, copyright?,
+%% version?, since?, deprecated?, see*, reference*,
+%% todo?, behaviour*, callbacks?, typedecls?,
+%% functions)>
+%% <!ATTLIST module
+%% name CDATA #REQUIRED
+%% private NMTOKEN(yes | no) #IMPLIED
+%% hidden NMTOKEN(yes | no) #IMPLIED
+%% root CDATA #IMPLIED>
+%% <!ELEMENT args (arg*)>
+%% <!ELEMENT arg (argName, description?)>
+%% <!ELEMENT argName (#PCDATA)>
+%% <!ELEMENT description (briefDescription, fullDescription?)>
+%% <!ELEMENT briefDescription (#PCDATA)>
+%% <!ELEMENT fullDescription (#PCDATA)>
+%% <!ELEMENT author EMPTY>
+%% <!ATTLIST author
+%% name CDATA #REQUIRED
+%% email CDATA #IMPLIED
+%% website CDATA #IMPLIED>
+%% <!ELEMENT version (#PCDATA)>
+%% <!ELEMENT since (#PCDATA)>
+%% <!ELEMENT copyright (#PCDATA)>
+%% <!ELEMENT deprecated (description)>
+%% <!ELEMENT see (#PCDATA)>
+%% <!ATTLIST see
+%% name CDATA #REQUIRED
+%% href CDATA #IMPLIED>
+%% <!ELEMENT reference (#PCDATA)>
+%% <!ELEMENT todo (#PCDATA)>
+%% <!ELEMENT behaviour (#PCDATA)>
+%% <!ATTLIST behaviour
+%% href CDATA #IMPLIED>
+%% <!ELEMENT callbacks (callback+)>
+%% <!ELEMENT typedecls (typedecl+)>
+%% <!ELEMENT typedecl (typedef, description?)>
+%% <!ELEMENT functions (function+)>
+
+%% NEW-OPTIONS: private, hidden, todo
+%% DEFER-OPTIONS: edoc_extract:source/4
+
+module(Module, Entries, Env, Opts) ->
+ Name = atom_to_list(Module#module.name),
+ HeaderEntry = get_entry(module, Entries),
+ HeaderTags = HeaderEntry#entry.data,
+ AllTags = get_all_tags(Entries),
+ Functions = function_filter(Entries, Opts),
+ Out = {module, ([{name, Name},
+ {root, Env#env.root}]
+ ++ case is_private(HeaderTags) of
+ true -> [{private, "yes"}];
+ false -> []
+ end
+ ++ case is_hidden(HeaderTags) of
+ true -> [{hidden, "yes"}];
+ false -> []
+ end),
+ (module_args(Module#module.parameters)
+ ++ behaviours(Module#module.attributes, Env)
+ ++ get_doc(HeaderTags)
+ ++ authors(HeaderTags)
+ ++ get_version(HeaderTags)
+ ++ get_since(HeaderTags)
+ ++ get_copyright(HeaderTags)
+ ++ get_deprecated(HeaderTags)
+ ++ sees(HeaderTags, Env)
+ ++ references(HeaderTags)
+ ++ todos(HeaderTags, Opts)
+ ++ [{typedecls, types(AllTags, Env)},
+ {functions, functions(Functions, Env, Opts)}
+ | callbacks(Functions, Module, Env, Opts)])
+ },
+ xmerl_lib:expand_element(Out).
+
+get_all_tags(Es) ->
+ lists:flatmap(fun (#entry{data = Ts}) -> Ts end, Es).
+
+is_private(Ts) ->
+ get_tags(private, Ts) =/= [].
+
+description([]) ->
+ [];
+description(Desc) ->
+ ShortDesc = edoc_lib:get_first_sentence(Desc),
+ [{description,
+ [{briefDescription, ShortDesc},
+ {fullDescription, Desc}]}].
+
+module_args(none) ->
+ [];
+module_args(Vs) ->
+ [{args, [{arg, [{argName, [atom_to_list(V)]}]} || V <- Vs]}].
+
+types(Tags, Env) ->
+ [{typedecl, [{label, edoc_types:to_label(Def)}],
+ [edoc_types:to_xml(Def, Env)] ++ description(Doc)}
+ || #tag{name = type, data = {Def, Doc}} <- Tags].
+
+functions(Es, Env, Opts) ->
+ [function(N, As, Export, Ts, Env, Opts)
+ || #entry{name = {_,_}=N, args = As, export = Export, data = Ts}
+ <- Es].
+
+function_filter(Es, Opts) ->
+ Private = proplists:get_bool(private, Opts),
+ Hidden = proplists:get_bool(hidden, Opts),
+ [E || E <- Es, function_filter(E, Private, Hidden)].
+
+%% Note that only entries whose names have the form {_,_} are functions.
+function_filter(#entry{name = {_,_}, export = Export, data = Ts},
+ Private, Hidden) ->
+ ((Export andalso not is_private(Ts)) orelse Private)
+ andalso ((not is_hidden(Ts)) orelse Hidden);
+function_filter(_, _, _) ->
+ false.
+
+is_hidden(Ts) ->
+ get_tags(hidden, Ts) =/= [].
+
+callbacks(Es, Module, Env, Opts) ->
+ case lists:any(fun (#entry{name = {behaviour_info, 1}}) -> true;
+ (_) -> false
+ end,
+ Es) of
+ true ->
+ try (Module#module.name):behaviour_info(callbacks) of
+ Fs ->
+ Fs1 = [{F,A} || {F,A} <- Fs, is_atom(F), is_integer(A)],
+ if Fs1 =:= [] ->
+ [];
+ true ->
+ [{callbacks,
+ [callback(F, Env, Opts) || F <- Fs1]}]
+ end
+ catch
+ _:_ -> []
+ end;
+ false -> []
+ end.
+
+%% <!ELEMENT callback EMPTY>
+%% <!ATTLIST callback
+%% name CDATA #REQUIRED
+%% arity CDATA #REQUIRED>
+
+callback({N, A}, _Env, _Opts) ->
+ {callback, [{name, atom_to_list(N)},
+ {arity, integer_to_list(A)}],
+ []}.
+
+%% <!ELEMENT function (args, typespec?, returns?, throws?, equiv?,
+%% description?, since?, deprecated?, see*, todo?)>
+%% <!ATTLIST function
+%% name CDATA #REQUIRED
+%% arity CDATA #REQUIRED
+%% exported NMTOKEN(yes | no) #REQUIRED
+%% label CDATA #IMPLIED>
+%% <!ELEMENT args (arg*)>
+%% <!ELEMENT arg (argName, description?)>
+%% <!ELEMENT argName (#PCDATA)>
+%% <!ELEMENT returns (description)>
+%% <!ELEMENT throws (type, localdef*)>
+%% <!ELEMENT equiv (expr, see?)>
+%% <!ELEMENT expr (#PCDATA)>
+
+function({N, A}, As, Export, Ts, Env, Opts) ->
+ {Args, Ret, Spec} = signature(Ts, As, Env),
+ {function, [{name, atom_to_list(N)},
+ {arity, integer_to_list(A)},
+ {exported, case Export of
+ true -> "yes";
+ false -> "no"
+ end},
+ {label, edoc_refs:to_label(edoc_refs:function(N, A))}],
+ [{args, [{arg, [{argName, [atom_to_list(A)]}] ++ description(D)}
+ || {A, D} <- Args]}]
+ ++ Spec
+ ++ case Ret of
+ [] -> [];
+ _ -> [{returns, description(Ret)}]
+ end
+ ++ get_throws(Ts, Env)
+ ++ get_equiv(Ts, Env)
+ ++ get_doc(Ts)
+ ++ get_since(Ts)
+ ++ get_deprecated(Ts, N, A, Env)
+ ++ sees(Ts, Env)
+ ++ todos(Ts, Opts)
+ }.
+
+get_throws(Ts, Env) ->
+ case get_tags(throws, Ts) of
+ [Throws] ->
+ Type = Throws#tag.data,
+ [edoc_types:to_xml(Type, Env)];
+ [] ->
+ []
+ end.
+
+get_equiv(Ts, Env) ->
+ case get_tags(equiv, Ts) of
+ [Equiv] ->
+ Expr = Equiv#tag.data,
+ See = case get_expr_ref(Equiv#tag.data) of
+ none -> [];
+ Ref ->
+ [see(Ref, [edoc_refs:to_string(Ref)], Env)]
+ end,
+ [{equiv, [{expr, [erl_prettypr:format(Expr)]} | See]}];
+ [] ->
+ []
+ end.
+
+get_doc(Ts) ->
+ case get_tags(doc, Ts) of
+ [T] ->
+ description(T#tag.data);
+ [] ->
+ []
+ end.
+
+get_copyright(Ts) ->
+ get_pcdata_tag(copyright, Ts).
+
+get_version(Ts) ->
+ get_pcdata_tag(version, Ts).
+
+get_since(Ts) ->
+ get_pcdata_tag(since, Ts).
+
+get_pcdata_tag(Tag, Ts) ->
+ case get_tags(Tag, Ts) of
+ [T] ->
+ [{Tag, [T#tag.data]}];
+ [] ->
+ []
+ end.
+
+%% Deprecation declarations for xref:
+%%
+%% -deprecated(Info).
+%% Info = Spec | [Spec]
+%% Spec = module | {F,A} | {F,A,Details}}
+%% Details = next_version | next_major_release | eventually
+%% (EXTENSION: | string() | {M1,F1,A1}}
+%% TODO: use info from '-deprecated(...)' (xref-)declarations.
+
+get_deprecated(Ts) ->
+ case get_tags(deprecated, Ts) of
+ [T] ->
+ [{deprecated, description(T#tag.data)}];
+ [] ->
+ []
+ end.
+
+get_deprecated(Ts, F, A, Env) ->
+ case get_deprecated(Ts) of
+ [] ->
+ M = Env#env.module,
+ case otp_internal:obsolete(M, F, A) of
+ {Tag, Text} when Tag =:= deprecated; Tag =:= removed ->
+ deprecated([Text]);
+ {Tag, Repl, _Rel} when Tag =:= deprecated; Tag =:= removed ->
+ deprecated(Repl, Env);
+ _ ->
+ []
+ end;
+ Es ->
+ Es
+ end.
+
+deprecated(Repl, Env) ->
+ {Text, Ref} = replacement_function(Env#env.module, Repl),
+ Desc = ["Use ", {a, href(Ref, Env), [{code, [Text]}]}, " instead."],
+ deprecated(Desc).
+
+deprecated(Desc) ->
+ [{deprecated, description(Desc)}].
+
+replacement_function(M0, {M,F,A}) when is_list(A) ->
+ %% refer to the largest listed arity - the most general version
+ replacement_function(M0, {M,F,lists:last(lists:sort(A))});
+replacement_function(M, {M,F,A}) ->
+ {io_lib:fwrite("~w/~w", [F, A]), edoc_refs:function(F, A)};
+replacement_function(_, {M,F,A}) ->
+ {io_lib:fwrite("~w:~w/~w", [M, F, A]), edoc_refs:function(M, F, A)}.
+
+get_expr_ref(Expr) ->
+ case catch {ok, erl_syntax_lib:analyze_application(Expr)} of
+ {ok, {F, A}} when is_atom(F), is_integer(A) ->
+ edoc_refs:function(F, A);
+ {ok, {M, {F, A}}} when is_atom(M), is_atom(F), is_integer(A) ->
+ edoc_refs:function(M, F, A);
+ _ ->
+ none
+ end.
+
+authors(Ts) ->
+ [author(Info) || #tag{data = Info} <- get_tags(author, Ts)].
+
+%% <!ATTLIST author
+%% name CDATA #REQUIRED
+%% email CDATA #IMPLIED
+%% website CDATA #IMPLIED>
+
+author({Name, Mail, URI}) ->
+ %% At least one of Name and Mail must be nonempty in the tag.
+ {author, ([{name, if Name =:= "" -> Mail;
+ true -> Name
+ end}]
+ ++ if Mail =:= "" ->
+ case lists:member($@, Name) of
+ true -> [{email, Name}];
+ false -> []
+ end;
+ true -> [{email, Mail}]
+ end
+ ++ if URI =:= "" -> [];
+ true -> [{website, URI}]
+ end), []}.
+
+behaviours(As, Env) ->
+ [{behaviour, href(edoc_refs:module(B), Env), [atom_to_list(B)]}
+ || {behaviour, B} <- As, is_atom(B)].
+
+sees(Tags, Env) ->
+ Ts = get_tags(see, Tags),
+ Rs = lists:keysort(1, [Data || #tag{data = Data} <- Ts]),
+ [see(Ref, XML, Env) || {Ref, XML} <- Rs].
+
+see(Ref, [], Env) ->
+ see(Ref, [edoc_refs:to_string(Ref)], Env);
+see(Ref, XML, Env) ->
+ {see, [{name, edoc_refs:to_string(Ref)}] ++ href(Ref, Env), XML}.
+
+href(Ref, Env) ->
+ [{href, edoc_refs:get_uri(Ref, Env)}]
+ ++ case edoc_refs:is_top(Ref, Env) of
+ true ->
+ [{target, "_top"}];
+ false ->
+ []
+ end.
+
+references(Tags) ->
+ [{reference, XML} || #tag{data = XML} <- get_tags(reference, Tags)].
+
+todos(Tags, Opts) ->
+ case proplists:get_bool(todo, Opts) of
+ true ->
+ [{todo, XML} || #tag{data = XML} <- get_tags('todo', Tags)];
+ false ->
+ []
+ end.
+
+signature(Ts, As, Env) ->
+ case get_tags(spec, Ts) of
+ [T] ->
+ Spec = T#tag.data,
+ R = merge_returns(Spec, Ts),
+ As0 = edoc_types:arg_names(Spec),
+ Ds0 = edoc_types:arg_descs(Spec),
+ %% choose names in spec before names in code
+ P = dict:from_list(params(Ts)),
+ As1 = merge_args(As0, As, Ds0, P),
+ %% check_params(As1, P),
+ Spec1 = edoc_types:set_arg_names(Spec, [A || {A,_} <- As1]),
+ {As1, R, [edoc_types:to_xml(Spec1, Env)]};
+ [] ->
+ S = sets:new(),
+ {[{A, ""} || A <- fix_argnames(As, S, 1)], [], []}
+ end.
+
+params(Ts) ->
+ [T#tag.data || T <- get_tags(param, Ts)].
+
+%% check_params(As, P) ->
+%% case dict:keys(P) -- [N || {N,_} <- As] of
+%% [] -> ok;
+%% Ps -> error %% TODO: report @param declarations with no match
+%% end.
+
+merge_returns(Spec, Ts) ->
+ case get_tags(returns, Ts) of
+ [] ->
+ case edoc_types:range_desc(Spec) of
+ "" -> [];
+ Txt -> [Txt]
+ end;
+ [T] -> T#tag.data
+ end.
+
+%% Names are chosen from the first list (the specification) if possible.
+%% Descriptions specified with @param (in P dict) override descriptions
+%% from the spec (in Ds).
+
+merge_args(As, As1, Ds, P) ->
+ merge_args(As, As1, Ds, [], P, sets:new(), 1).
+
+merge_args(['_' | As], ['_' | As1], [D | Ds], Rs, P, S, N) ->
+ merge_args(As, As1, Ds, Rs, P, S, N, make_name(N, S), D);
+merge_args(['_' | As], [A | As1], [D | Ds], Rs, P, S, N) ->
+ merge_args(As, As1, Ds, Rs, P, S, N, A, D);
+merge_args([A | As], [_ | As1], [D | Ds], Rs, P, S, N) ->
+ merge_args(As, As1, Ds, Rs, P, S, N, A, D);
+merge_args([], [], [], Rs, _P, _S, _N) ->
+ lists:reverse(Rs).
+
+merge_args(As, As1, Ds, Rs, P, S, N, A, D0) ->
+ D = case dict:find(A, P) of
+ {ok, D1} -> D1;
+ error when D0 =:= [] -> []; % no description
+ error -> [D0] % a simple-xml text element
+ end,
+ merge_args(As, As1, Ds, [{A, D} | Rs], P,
+ sets:add_element(A, S), N + 1).
+
+fix_argnames(['_' | As], S, N) ->
+ A = make_name(N, S),
+ [A | fix_argnames(As, sets:add_element(A, S), N + 1)];
+fix_argnames([A | As], S, N) ->
+ [A | fix_argnames(As, sets:add_element(A, S), N + 1)];
+fix_argnames([], _S, _N) ->
+ [].
+
+make_name(N, S) ->
+ make_name(N, S, "X").
+
+make_name(N, S, Base) ->
+ A = list_to_atom(Base ++ integer_to_list(N)),
+ case sets:is_element(A, S) of
+ true ->
+ make_name(N, S, Base ++ "x");
+ false ->
+ A
+ end.
+
+get_entry(Name, [#entry{name = Name} = E | _Es]) -> E;
+get_entry(Name, [_ | Es]) -> get_entry(Name, Es).
+
+get_tags(Tag, [#tag{name = Tag} = T | Ts]) -> [T | get_tags(Tag, Ts)];
+get_tags(Tag, [_ | Ts]) -> get_tags(Tag, Ts);
+get_tags(_, []) -> [].
+
+%% ---------------------------------------------------------------------
+
+type(T, Env) ->
+ xmerl_lib:expand_element({type, [edoc_types:to_xml(T, Env)]}).
+
+%% <!ELEMENT package (description?, author*, copyright?, version?,
+%% since?, deprecated?, see*, reference*, todo?,
+%% modules)>
+%% <!ATTLIST package
+%% name CDATA #REQUIRED
+%% root CDATA #IMPLIED>
+%% <!ELEMENT modules (module+)>
+
+package(Package, Tags, Env, Opts) ->
+ Env1 = Env#env{package = Package,
+ root = edoc_refs:relative_package_path('', Package)},
+ xmerl_lib:expand_element(package_1(Package, Tags, Env1, Opts)).
+
+package_1(Package, Tags, Env, Opts) ->
+ {package, [{root, Env#env.root}],
+ ([{packageName, [atom_to_list(Package)]}]
+ ++ get_doc(Tags)
+ ++ authors(Tags)
+ ++ get_copyright(Tags)
+ ++ get_version(Tags)
+ ++ get_since(Tags)
+ ++ get_deprecated(Tags)
+ ++ sees(Tags, Env)
+ ++ references(Tags)
+ ++ todos(Tags, Opts))
+ }.
+
+%% <!ELEMENT overview (title, description?, author*, copyright?, version?,
+%% since?, see*, reference*, todo?, packages, modules)>
+%% <!ATTLIST overview
+%% root CDATA #IMPLIED>
+%% <!ELEMENT title (#PCDATA)>
+
+overview(Title, Tags, Env, Opts) ->
+ Env1 = Env#env{package = '',
+ root = ""},
+ xmerl_lib:expand_element(overview_1(Title, Tags, Env1, Opts)).
+
+overview_1(Title, Tags, Env, Opts) ->
+ {overview, [{root, Env#env.root}],
+ ([{title, [get_title(Tags, Title)]}]
+ ++ get_doc(Tags)
+ ++ authors(Tags)
+ ++ get_copyright(Tags)
+ ++ get_version(Tags)
+ ++ get_since(Tags)
+ ++ sees(Tags, Env)
+ ++ references(Tags)
+ ++ todos(Tags, Opts))
+ }.
+
+get_title(Ts, Default) ->
+ case get_tags(title, Ts) of
+ [T] ->
+ T#tag.data;
+ [] ->
+ Default
+ end.
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
new file mode 100644
index 0000000000..f1d876d593
--- /dev/null
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -0,0 +1,521 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @copyright 2003-2006 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc Standard doclet module for EDoc.
+
+%% Note that this is written so that it is *not* depending on edoc.hrl!
+
+%% TODO: copy "doc-files" subdirectories, recursively.
+%% TODO: generate summary page of TODO-notes
+%% TODO: generate summary page of deprecated things
+%% TODO: generate decent indexes over modules, methods, records, etc.
+
+-module(edoc_doclet).
+
+-export([run/2]).
+
+-import(edoc_report, [report/2, warning/2]).
+
+%% @headerfile "edoc_doclet.hrl"
+-include("../include/edoc_doclet.hrl").
+
+-define(EDOC_APP, edoc).
+-define(DEFAULT_FILE_SUFFIX, ".html").
+-define(INDEX_FILE, "index.html").
+-define(OVERVIEW_FILE, "overview.edoc").
+-define(PACKAGE_SUMMARY, "package-summary.html").
+-define(OVERVIEW_SUMMARY, "overview-summary.html").
+-define(PACKAGES_FRAME, "packages-frame.html").
+-define(MODULES_FRAME, "modules-frame.html").
+-define(STYLESHEET, "stylesheet.css").
+-define(IMAGE, "erlang.png").
+-define(NL, "\n").
+
+-include("xmerl.hrl").
+
+%% Sources is the list of inputs in the order they were found. Packages
+%% and Modules are sorted lists of atoms without duplicates. (They
+%% usually include the data from the edoc-info file in the target
+%% directory, if it exists.) Note that the "empty package" is never
+%% included in Packages!
+
+%% @spec (Command::doclet_gen() | doclet_toc(), edoc_context()) -> ok
+%% @doc Main doclet entry point. See the file <a
+%% href="../include/edoc_doclet.hrl">`edoc_doclet.hrl'</a> for the data
+%% structures used for passing parameters.
+%%
+%% Also see {@link edoc:layout/2} for layout-related options, and
+%% {@link edoc:get_doc/2} for options related to reading source
+%% files.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {file_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the suffix used for output files. The default value is
+%% `".html"'.
+%% </dd>
+%% <dt>{@type {hidden, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', documentation of hidden modules and
+%% functions will also be included. The default value is `false'.
+%% </dd>
+%% <dt>{@type {overview, edoc:filename()@}}
+%% </dt>
+%% <dd>Specifies the name of the overview-file. By default, this doclet
+%% looks for a file `"overview.edoc"' in the target directory.
+%% </dd>
+%% <dt>{@type {private, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', documentation of private modules and
+%% functions will also be included. The default value is `false'.
+%% </dd>
+%% <dt>{@type {stylesheet, string()@}}
+%% </dt>
+%% <dd>Specifies the URI used for referencing the stylesheet. The
+%% default value is `"stylesheet.css"'. If an empty string is
+%% specified, no stylesheet reference will be generated.
+%% </dd>
+%% <dt>{@type {stylesheet_file, edoc:filename()@}}
+%% </dt>
+%% <dd>Specifies the name of the stylesheet file. By default, this
+%% doclet uses the file `"stylesheet.css"' in the `priv'
+%% subdirectory of the EDoc installation directory. The named file
+%% will be copied to the target directory.
+%% </dd>
+%% <dt>{@type {title, string()@}}
+%% </dt>
+%% <dd>Specifies the title of the overview-page.
+%% </dd>
+%% </dl>
+
+%% INHERIT-OPTIONS: title/2
+%% INHERIT-OPTIONS: sources/5
+%% INHERIT-OPTIONS: overview/4
+%% INHERIT-OPTIONS: copy_stylesheet/2
+%% INHERIT-OPTIONS: stylesheet/1
+
+run(#doclet_gen{}=Cmd, Ctxt) ->
+ gen(Cmd#doclet_gen.sources,
+ Cmd#doclet_gen.app,
+ Cmd#doclet_gen.packages,
+ Cmd#doclet_gen.modules,
+ Cmd#doclet_gen.filemap,
+ Ctxt);
+run(#doclet_toc{}=Cmd, Ctxt) ->
+ toc(Cmd#doclet_toc.paths, Ctxt).
+
+gen(Sources, App, Packages, Modules, FileMap, Ctxt) ->
+ Dir = Ctxt#context.dir,
+ Env = Ctxt#context.env,
+ Options = Ctxt#context.opts,
+ Title = title(App, Options),
+ CSS = stylesheet(Options),
+ {Modules1, Error} = sources(Sources, Dir, Modules, Env, Options),
+ modules_frame(Dir, Modules1, Title, CSS),
+ packages(Packages, Dir, FileMap, Env, Options),
+ packages_frame(Dir, Packages, Title, CSS),
+ overview(Dir, Title, Env, Options),
+ index_file(Dir, length(Packages) > 1, Title),
+ edoc_lib:write_info_file(App, Packages, Modules1, Dir),
+ copy_stylesheet(Dir, Options),
+ copy_image(Dir),
+ %% handle postponed error during processing of source files
+ case Error of
+ true -> exit(error);
+ false -> ok
+ end.
+
+
+%% NEW-OPTIONS: title
+%% DEFER-OPTIONS: run/2
+
+title(App, Options) ->
+ proplists:get_value(title, Options,
+ if App == ?NO_APP ->
+ "Overview";
+ true ->
+ io_lib:fwrite("Application: ~s", [App])
+ end).
+
+
+%% Processing the individual source files.
+
+%% NEW-OPTIONS: file_suffix, private, hidden
+%% INHERIT-OPTIONS: edoc:layout/2
+%% INHERIT-OPTIONS: edoc:get_doc/3
+%% DEFER-OPTIONS: run/2
+
+sources(Sources, Dir, Modules, Env, Options) ->
+ Suffix = proplists:get_value(file_suffix, Options,
+ ?DEFAULT_FILE_SUFFIX),
+ Private = proplists:get_bool(private, Options),
+ Hidden = proplists:get_bool(hidden, Options),
+ {Ms, E} = lists:foldl(fun (Src, {Set, Error}) ->
+ source(Src, Dir, Suffix, Env, Set,
+ Private, Hidden, Error, Options)
+ end,
+ {sets:new(), false}, Sources),
+ {[M || M <- Modules, sets:is_element(M, Ms)], E}.
+
+
+%% Generating documentation for a source file, adding its name to the
+%% set if it was successful. Errors are just flagged at this stage,
+%% allowing all source files to be processed even if some of them fail.
+
+source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden,
+ Error, Options) ->
+ File = filename:join(Path, Name),
+ case catch {ok, edoc:get_doc(File, Env, Options)} of
+ {ok, {Module, Doc}} ->
+ check_name(Module, M, P, File),
+ case ((not is_private(Doc)) orelse Private)
+ andalso ((not is_hidden(Doc)) orelse Hidden) of
+ true ->
+ Text = edoc:layout(Doc, Options),
+ Name1 = packages:last(M) ++ Suffix,
+ edoc_lib:write_file(Text, Dir, Name1, P),
+ {sets:add_element(Module, Set), Error};
+ false ->
+ {Set, Error}
+ end;
+ R ->
+ report("skipping source file '~s': ~W.", [File, R, 15]),
+ {Set, true}
+ end.
+
+check_name(M, M0, P0, File) ->
+ P = list_to_atom(packages:strip_last(M)),
+ N = packages:last(M),
+ N0 = packages:last(M0),
+ case N of
+ [$? | _] ->
+ %% A module name of the form '?...' is assumed to be caused
+ %% by the epp_dodger parser when the module declaration has
+ %% the form '-module(?MACRO).'; skip the filename check.
+ ok;
+ _ ->
+ if N =/= N0 ->
+ warning("file '~s' actually contains module '~s'.",
+ [File, M]);
+ true ->
+ ok
+ end
+ end,
+ if P =/= P0 ->
+ warning("file '~s' belongs to package '~s', not '~s'.",
+ [File, P, P0]);
+ true ->
+ ok
+ end.
+
+
+%% Generating the summary files for packages.
+
+%% INHERIT-OPTIONS: read_file/4
+%% INHERIT-OPTIONS: edoc_lib:run_layout/2
+
+packages(Packages, Dir, FileMap, Env, Options) ->
+ lists:foreach(fun (P) ->
+ package(P, Dir, FileMap, Env, Options)
+ end,
+ Packages).
+
+package(P, Dir, FileMap, Env, Opts) ->
+ Tags = case FileMap(P) of
+ "" ->
+ [];
+ File ->
+ read_file(File, package, Env, Opts)
+ end,
+ Data = edoc_data:package(P, Tags, Env, Opts),
+ F = fun (M) ->
+ M:package(Data, Opts)
+ end,
+ Text = edoc_lib:run_layout(F, Opts),
+ edoc_lib:write_file(Text, Dir, ?PACKAGE_SUMMARY, P).
+
+
+%% Creating an index file, with some frames optional.
+%% TODO: get rid of frames, or change doctype to Frameset
+
+index_file(Dir, Packages, Title) ->
+ Frame1 = {frame, [{src,?PACKAGES_FRAME},
+ {name,"packagesFrame"},{title,""}],
+ []},
+ Frame2 = {frame, [{src,?MODULES_FRAME},
+ {name,"modulesFrame"},{title,""}],
+ []},
+ Frame3 = {frame, [{src,?OVERVIEW_SUMMARY},
+ {name,"overviewFrame"},{title,""}],
+ []},
+ Frameset = {frameset, [{cols,"20%,80%"}],
+ case Packages of
+ true ->
+ [?NL,
+ {frameset, [{rows,"30%,70%"}],
+ [?NL, Frame1, ?NL, Frame2, ?NL]}
+ ];
+ false ->
+ [?NL, Frame2, ?NL]
+ end
+ ++ [?NL, Frame3, ?NL,
+ {noframes,
+ [?NL,
+ {h2, ["This page uses frames"]},
+ ?NL,
+ {p, ["Your browser does not accept frames.",
+ ?NL, br,
+ "You should go to the ",
+ {a, [{href, ?OVERVIEW_SUMMARY}],
+ ["non-frame version"]},
+ " instead.", ?NL]},
+ ?NL]},
+ ?NL]},
+ XML = xhtml_1(Title, [], Frameset),
+ Text = xmerl:export_simple([XML], xmerl_html, []),
+ edoc_lib:write_file(Text, Dir, ?INDEX_FILE).
+
+packages_frame(Dir, Ps, Title, CSS) ->
+ Body = [?NL,
+ {h2, [{class, "indextitle"}], ["Packages"]},
+ ?NL,
+ {table, [{width, "100%"}, {border, 0},
+ {summary, "list of packages"}],
+ lists:concat(
+ [[?NL,
+ {tr, [{td, [], [{a, [{href, package_ref(P)},
+ {target,"overviewFrame"},
+ {class, "package"}],
+ [atom_to_list(P)]}]}]}]
+ || P <- Ps])},
+ ?NL],
+ XML = xhtml(Title, CSS, Body),
+ Text = xmerl:export_simple([XML], xmerl_html, []),
+ edoc_lib:write_file(Text, Dir, ?PACKAGES_FRAME).
+
+modules_frame(Dir, Ms, Title, CSS) ->
+ Body = [?NL,
+ {h2, [{class, "indextitle"}], ["Modules"]},
+ ?NL,
+ {table, [{width, "100%"}, {border, 0},
+ {summary, "list of modules"}],
+ lists:concat(
+ [[?NL,
+ {tr, [{td, [],
+ [{a, [{href, module_ref(M)},
+ {target, "overviewFrame"},
+ {class, "module"}],
+ [atom_to_list(M)]}]}]}]
+ || M <- Ms])},
+ ?NL],
+ XML = xhtml(Title, CSS, Body),
+ Text = xmerl:export_simple([XML], xmerl_html, []),
+ edoc_lib:write_file(Text, Dir, ?MODULES_FRAME).
+
+module_ref(M) ->
+ edoc_refs:relative_package_path(M, '') ++ ?DEFAULT_FILE_SUFFIX.
+
+package_ref(P) ->
+ edoc_lib:join_uri(edoc_refs:relative_package_path(P, ''),
+ ?PACKAGE_SUMMARY).
+
+xhtml(Title, CSS, Content) ->
+ xhtml_1(Title, CSS, {body, [{bgcolor, "white"}], Content}).
+
+xhtml_1(Title, CSS, Body) ->
+ {html, [?NL,
+ {head, [?NL, {title, [Title]}, ?NL] ++ CSS},
+ ?NL,
+ Body,
+ ?NL]
+ }.
+
+%% NEW-OPTIONS: overview
+%% INHERIT-OPTIONS: read_file/4
+%% INHERIT-OPTIONS: edoc_lib:run_layout/2
+%% INHERIT-OPTIONS: edoc_extract:file/4
+%% DEFER-OPTIONS: run/2
+
+overview(Dir, Title, Env, Opts) ->
+ File = proplists:get_value(overview, Opts,
+ filename:join(Dir, ?OVERVIEW_FILE)),
+ Tags = read_file(File, overview, Env, Opts),
+ Data = edoc_data:overview(Title, Tags, Env, Opts),
+ F = fun (M) ->
+ M:overview(Data, Opts)
+ end,
+ Text = edoc_lib:run_layout(F, Opts),
+ edoc_lib:write_file(Text, Dir, ?OVERVIEW_SUMMARY).
+
+
+copy_image(Dir) ->
+ case code:priv_dir(?EDOC_APP) of
+ PrivDir when is_list(PrivDir) ->
+ From = filename:join(PrivDir, ?IMAGE),
+ edoc_lib:copy_file(From, filename:join(Dir, ?IMAGE));
+ _ ->
+ report("cannot find default image file.", []),
+ exit(error)
+ end.
+
+%% NEW-OPTIONS: stylesheet_file
+%% DEFER-OPTIONS: run/2
+
+copy_stylesheet(Dir, Options) ->
+ case proplists:get_value(stylesheet, Options) of
+ undefined ->
+ From = case proplists:get_value(stylesheet_file, Options) of
+ File when is_list(File) ->
+ File;
+ _ ->
+ case code:priv_dir(?EDOC_APP) of
+ PrivDir when is_list(PrivDir) ->
+ filename:join(PrivDir, ?STYLESHEET);
+ _ ->
+ report("cannot find default "
+ "stylesheet file.", []),
+ exit(error)
+ end
+ end,
+ edoc_lib:copy_file(From, filename:join(Dir, ?STYLESHEET));
+ _ ->
+ ok
+ end.
+
+%% NEW-OPTIONS: stylesheet
+%% DEFER-OPTIONS: run/2
+
+stylesheet(Options) ->
+ case proplists:get_value(stylesheet, Options) of
+ "" ->
+ [];
+ S ->
+ Ref = case S of
+ undefined ->
+ ?STYLESHEET;
+ "" ->
+ ""; % no stylesheet
+ S when is_list(S) ->
+ S;
+ _ ->
+ report("bad value for option 'stylesheet'.",
+ []),
+ exit(error)
+ end,
+ [{link, [{rel, "stylesheet"},
+ {type, "text/css"},
+ {href, Ref},
+ {title, "EDoc"}], []},
+ ?NL]
+ end.
+
+is_private(E) ->
+ case get_attrval(private, E) of
+ "yes" -> true;
+ _ -> false
+ end.
+
+is_hidden(E) ->
+ case get_attrval(hidden, E) of
+ "yes" -> true;
+ _ -> false
+ end.
+
+get_attrval(Name, #xmlElement{attributes = As}) ->
+ case get_attr(Name, As) of
+ [#xmlAttribute{value = V}] ->
+ V;
+ [] -> ""
+ end.
+
+get_attr(Name, [#xmlAttribute{name = Name} = A | As]) ->
+ [A | get_attr(Name, As)];
+get_attr(Name, [_ | As]) ->
+ get_attr(Name, As);
+get_attr(_, []) ->
+ [].
+
+%% Read external source file. Fails quietly, returning empty tag list.
+
+%% INHERIT-OPTIONS: edoc_extract:file/4
+
+read_file(File, Context, Env, Opts) ->
+ case edoc_extract:file(File, Context, Env, Opts) of
+ {ok, Tags} ->
+ Tags;
+ {error, _} ->
+ []
+ end.
+
+
+%% TODO: FIXME: meta-level index generation
+
+%% Creates a Table of Content from a list of Paths (ie paths to applications)
+%% and an overview file.
+
+-define(EDOC_DIR, "doc").
+-define(INDEX_DIR, "doc/index").
+-define(CURRENT_DIR, ".").
+
+toc(Paths, Ctxt) ->
+ Opts = Ctxt#context.opts,
+ Dir = Ctxt#context.dir,
+ Env = Ctxt#context.env,
+ app_index_file(Paths, Dir, Env, Opts).
+
+%% TODO: FIXME: it's unclear how much of this is working at all
+
+%% NEW-OPTIONS: title
+%% INHERIT-OPTIONS: overview/4
+
+app_index_file(Paths, Dir, Env, Options) ->
+ Title = proplists:get_value(title, Options,"Overview"),
+% Priv = proplists:get_bool(private, Options),
+ CSS = stylesheet(Options),
+ Apps1 = [{filename:dirname(A),filename:basename(A)} || A <- Paths],
+ index_file(Dir, false, Title),
+ application_frame(Dir, Apps1, Title, CSS),
+ modules_frame(Dir, [], Title, CSS),
+ overview(Dir, Title, Env, Options),
+% edoc_lib:write_info_file(Prod, [], Modules1, Dir),
+ copy_stylesheet(Dir, Options).
+
+application_frame(Dir, Apps, Title, CSS) ->
+ Body = [?NL,
+ {h2, ["Applications"]},
+ ?NL,
+ {table, [{width, "100%"}, {border, 0}],
+ lists:concat(
+ [[{tr, [{td, [], [{a, [{href,app_ref(Path,App)},
+ {target,"_top"}],
+ [App]}]}]}]
+ || {Path,App} <- Apps])},
+ ?NL],
+ XML = xhtml(Title, CSS, Body),
+ Text = xmerl:export_simple([XML], xmerl_html, []),
+ edoc_lib:write_file(Text, Dir, ?MODULES_FRAME).
+
+app_ref(Path,M) ->
+ filename:join([Path,M,?EDOC_DIR,?INDEX_FILE]).
diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl
new file mode 100644
index 0000000000..ea2755f7aa
--- /dev/null
+++ b/lib/edoc/src/edoc_extract.erl
@@ -0,0 +1,584 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @copyright 2001-2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc EDoc documentation extraction.
+
+-module(edoc_extract).
+
+-export([source/3, source/4, source/5, header/3, header/4, header/5,
+ file/4, text/4]).
+
+-import(edoc_report, [report/3, warning/3]).
+
+%% %% @headerfile "edoc.hrl" (disabled until it can be made private)
+-include("edoc.hrl").
+
+%% @type filename() = file:filename()
+
+%% @spec source(File::filename(), Env::edoc_env(), Options::proplist())
+%% -> {ModuleName, edoc_module()}
+%% ModuleName = atom()
+%% proplist() = [term()]
+%%
+%% @doc Like {@link source/5}, but reads the syntax tree and the
+%% comments from the specified file.
+%%
+%% @see edoc:read_comments/2
+%% @see edoc:read_source/2
+%% @see source/4
+
+source(File, Env, Opts) ->
+ Forms = edoc:read_source(File, Opts),
+ Comments = edoc:read_comments(File, Opts),
+ source(Forms, Comments, File, Env, Opts).
+
+%% @spec source(Forms, Comments::[comment()], File::filename(),
+%% Env::edoc_env(), Options::proplist()) ->
+%% {ModuleName, edoc_module()}
+%%
+%% Forms = syntaxTree() | [syntaxTree()]
+%% comment() = {Line, Column, Indentation, Text}
+%% Line = integer()
+%% Column = integer()
+%% Indentation = integer()
+%% Text = [string()]
+%% ModuleName = atom()
+%%
+%% @doc Like {@link source/4}, but first inserts the given comments in
+%% the syntax trees. The syntax trees must contain valid position
+%% information. (Cf. {@link edoc:read_comments/2}.)
+%%
+%% @see edoc:read_comments/2
+%% @see edoc:read_source/2
+%% @see source/3
+%% @see source/4
+%% @see //syntax_tools/erl_recomment
+
+source(Forms, Comments, File, Env, Opts) when is_list(Forms) ->
+ Forms1 = erl_syntax:form_list(Forms),
+ source(Forms1, Comments, File, Env, Opts);
+source(Forms, Comments, File, Env, Opts) ->
+ Tree = erl_recomment:quick_recomment_forms(Forms, Comments),
+ source(Tree, File, Env, Opts).
+
+%% @spec source(Forms, File::filename(), Env::edoc_env(),
+%% Options::proplist()) ->
+%% {ModuleName, edoc_module()}
+%%
+%% Forms = syntaxTree() | [syntaxTree()]
+%% ModuleName = atom()
+%% edoc_module() = edoc:edoc_module()
+%% @type edoc_env() = edoc_lib:edoc_env()
+%%
+%% @doc Extracts EDoc documentation from commented source code syntax
+%% trees. The given `Forms' must be a single syntax tree of
+%% type `form_list', or a list of syntax trees representing
+%% "program forms" (cf. {@link edoc:read_source/2}.
+%% `Env' is an environment created by {@link
+%% edoc_lib:get_doc_env/4}. The `File' argument is used for
+%% error reporting and output file name generation only.
+%%
+%% See {@link edoc:get_doc/2} for descriptions of the `def',
+%% `hidden', `private', and `todo' options.
+%%
+%% @see edoc:read_comments/2
+%% @see edoc:read_source/2
+%% @see source/5
+%% @see //syntax_tools/erl_recomment
+
+%% Note that the actual module name found in the source file will be
+%% used for generating the documentation, creating relative links, etc.
+
+%% INHERIT-OPTIONS: add_macro_defs/3
+%% INHERIT-OPTIONS: edoc_data:module/4
+
+source(Forms, File, Env, Opts) when is_list(Forms) ->
+ source(erl_syntax:form_list(Forms), File, Env, Opts);
+source(Tree, File0, Env, Opts) ->
+ Forms = preprocess_forms(Tree),
+ File = edoc_lib:filename(File0),
+ Module = get_module_info(Tree, File),
+ {Header, Footer, Entries} = collect(Forms, Module),
+ Name = Module#module.name,
+ Package = list_to_atom(packages:strip_last(Name)),
+ Env1 = Env#env{module = Name,
+ package = Package,
+ root = edoc_refs:relative_package_path('', Package)},
+ Env2 = add_macro_defs(module_macros(Env1), Opts, Env1),
+ Entries1 = get_tags([Header, Footer | Entries], Env2, File),
+ Data = edoc_data:module(Module, Entries1, Env2, Opts),
+ {Name, Data}.
+
+
+%% @spec header(File::filename(), Env::edoc_env(), Options::proplist())
+%% -> {ok, Tags} | {error, Reason}
+%% Tags = [term()]
+%% Reason = term()
+%%
+%% @doc Similar to {@link header/5}, but reads the syntax tree and the
+%% comments from the specified file.
+%%
+%% @see edoc:read_comments/2
+%% @see edoc:read_source/2
+%% @see header/4
+
+header(File, Env, Opts) ->
+ Forms = edoc:read_source(File),
+ Comments = edoc:read_comments(File),
+ header(Forms, Comments, File, Env, Opts).
+
+%% @spec header(Forms, Comments::[comment()], File::filename(),
+%% Env::edoc_env(), Options::proplist()) ->
+%% {ok, Tags} | {error, Reason}
+%% Forms = syntaxTree() | [syntaxTree()]
+%% Tags = [term()]
+%% Reason = term()
+%%
+%% @doc Similar to {@link header/4}, but first inserts the given
+%% comments in the syntax trees. The syntax trees must contain valid
+%% position information. (Cf. {@link edoc:read_comments/2}.)
+%%
+%% @see header/3
+%% @see header/4
+%% @see //syntax_tools/erl_recomment
+
+header(Forms, Comments, File, Env, Opts) when is_list(Forms) ->
+ Forms1 = erl_syntax:form_list(Forms),
+ header(Forms1, Comments, File, Env, Opts);
+header(Forms, Comments, File, Env, Opts) ->
+ Tree = erl_recomment:quick_recomment_forms(Forms, Comments),
+ header(Tree, File, Env, Opts).
+
+%% @spec header(Forms, File::filename(), Env::edoc_env(),
+%% Options::proplist()) ->
+%% {ok, Tags} | {error, Reason}
+%% Forms = syntaxTree() | [syntaxTree()]
+%% Tags = [term()]
+%% Reason = term()
+%%
+%% @doc Extracts EDoc documentation from commented header file syntax
+%% trees. Similar to {@link source/5}, but ignores any documentation
+%% that occurs before a module declaration or a function definition.
+%% (Warning messages are printed if content may be ignored.) `Env' is
+%% assumed to already be set up with a suitable module context.
+%%
+%% @see header/5
+%% @see //syntax_tools/erl_recomment
+
+header(Forms, File, Env, Opts) when is_list(Forms) ->
+ header(erl_syntax:form_list(Forms), File, Env, Opts);
+header(Tree, File0, Env, _Opts) ->
+ Forms = preprocess_forms(Tree),
+ File = edoc_lib:filename(File0),
+ Module = #module{name = Env#env.module}, % a dummy module record
+ %% We take only "footer" tags, i.e., any kind of definition will
+ %% kill all the information above it up to that point. Then we call
+ %% this the 'header' to make error reports make better sense.
+ {Header, Footer, Entries} = collect(Forms, Module),
+ if Header#entry.data /= [] ->
+ warning(File, "documentation before module declaration is ignored by @headerfile", []);
+ true -> ok
+ end,
+ if Entries /= [] ->
+ warning(File, "documentation before function definitions is ignored by @headerfile", []);
+ true -> ok
+ end,
+ [Entry] = get_tags([Footer#entry{name = header}], Env, File),
+ Entry#entry.data.
+
+%% NEW-OPTIONS: def
+%% DEFER-OPTIONS: source/4
+
+add_macro_defs(Defs0, Opts, Env) ->
+ Defs = proplists:append_values(def, Opts),
+ edoc_macros:check_defs(Defs),
+ Env#env{macros = Defs ++ Defs0 ++ Env#env.macros}.
+
+
+%% @spec file(File::filename(), Context, Env::edoc_env(),
+%% Options::proplist()) -> {ok, Tags} | {error, Reason}
+%% Context = overview | package
+%% Tags = [term()]
+%% Reason = term()
+%%
+%% @doc Reads a text file and returns the list of tags in the file. Any
+%% lines of text before the first tag are ignored. `Env' is an
+%% environment created by {@link edoc_lib:get_doc_env/4}. Upon error,
+%% `Reason' is an atom returned from the call to {@link
+%% //kernel/file:read_file/1}.
+%%
+%% See {@link text/4} for options.
+
+%% INHERIT-OPTIONS: text/4
+
+file(File, Context, Env, Opts) ->
+ case file:read_file(File) of
+ {ok, Bin} ->
+ {ok, text(binary_to_list(Bin), Context, Env, Opts, File)};
+ {error, _R} = Error ->
+ Error
+ end.
+
+
+%% @spec (Text::string(), Context, Env::edoc_env(),
+%% Options::proplist()) -> Tags
+%% Context = overview | package
+%% Tags = [term()]
+%%
+%% @doc Returns the list of tags in the text. Any lines of text before
+%% the first tag are ignored. `Env' is an environment created by {@link
+%% edoc_lib:get_doc_env/4}.
+%%
+%% See {@link source/4} for a description of the `def' option.
+
+%% INHERIT-OPTIONS: add_macro_defs/3
+%% DEFER-OPTIONS: source/4
+
+text(Text, Context, Env, Opts) ->
+ text(Text, Context, Env, Opts, "").
+
+text(Text, Context, Env, Opts, Where) ->
+ Env1 = add_macro_defs(file_macros(Context, Env), Opts, Env),
+ Cs = edoc_lib:lines(Text),
+ Ts0 = edoc_tags:scan_lines(Cs, 1),
+ Tags = sets:from_list(edoc_tags:tag_names()),
+ Ts1 = edoc_tags:filter_tags(Ts0, Tags, Where),
+ Single = sets:from_list(edoc_tags:tags(single)),
+ Allow = sets:from_list(edoc_tags:tags(Context)),
+ case edoc_tags:check_tags(Ts1, Allow, Single, Where) of
+ true ->
+ exit(error);
+ false ->
+ Ts2 = edoc_macros:expand_tags(Ts1, Env1, Where),
+ How = dict:from_list(edoc_tags:tag_parsers()),
+ edoc_tags:parse_tags(Ts2, How, Env1, Where)
+ end.
+
+
+%% @spec (Forms::[syntaxTree()], File::filename()) -> moduleInfo()
+%% @doc Initialises a module-info record with data about the module
+%% represented by the list of forms. Exports are guaranteed to exist in
+%% the set of defined names.
+
+get_module_info(Forms, File) ->
+ L = case catch {ok, erl_syntax_lib:analyze_forms(Forms)} of
+ {ok, L1} ->
+ L1;
+ syntax_error ->
+ report(File, "syntax error in input.", []),
+ exit(error);
+ {'EXIT', R} ->
+ exit(R);
+ R ->
+ throw(R)
+ end,
+ {Name, Vars} = case lists:keyfind(module, 1, L) of
+ {module, N} when is_atom(N) ->
+ {N, none};
+ {module, {N, _Vs} = NVs} when is_atom(N) ->
+ NVs;
+ _ ->
+ report(File, "module name missing.", []),
+ exit(error)
+ end,
+ Functions = ordsets:from_list(get_list_keyval(functions, L)),
+ Exports = ordsets:from_list(get_list_keyval(exports, L)),
+ Attributes = ordsets:from_list(get_list_keyval(attributes, L)),
+ Records = get_list_keyval(records, L),
+ #module{name = Name,
+ parameters = Vars,
+ functions = Functions,
+ exports = ordsets:intersection(Exports, Functions),
+ attributes = Attributes,
+ records = Records}.
+
+get_list_keyval(Key, L) ->
+ case lists:keyfind(Key, 1, L) of
+ {Key, As} ->
+ ordsets:from_list(As);
+ _ ->
+ []
+ end.
+
+%% @spec (Forms::[syntaxTree()]) -> [syntaxTree()]
+%% @doc Preprocessing: copies any precomments on forms to standalone
+%% comments, and removes "invisible" forms from the list.
+
+preprocess_forms(Tree) ->
+ preprocess_forms_1(erl_syntax:form_list_elements(
+ erl_syntax:flatten_form_list(Tree))).
+
+preprocess_forms_1([F | Fs]) ->
+ case erl_syntax:get_precomments(F) of
+ [] ->
+ preprocess_forms_2(F, Fs);
+ Cs ->
+ Cs ++ preprocess_forms_2(F, Fs)
+ end;
+preprocess_forms_1([]) ->
+ [].
+
+preprocess_forms_2(F, Fs) ->
+ case erl_syntax_lib:analyze_form(F) of
+ comment ->
+ [F | preprocess_forms_1(Fs)];
+ {function, _} ->
+ [F | preprocess_forms_1(Fs)];
+ {rule, _} ->
+ [F | preprocess_forms_1(Fs)];
+ {attribute, {module, _}} ->
+ [F | preprocess_forms_1(Fs)];
+ text ->
+ [F | preprocess_forms_1(Fs)];
+ _ ->
+ preprocess_forms_1(Fs)
+ end.
+
+%% This collects the data for the header and the functions of the
+%% module. Note that the list of forms is assumed to have been
+%% preprocessed first, so that all "invisible" forms are removed, and
+%% the only interesting comments are those that are standalone comments
+%% in the list.
+
+collect(Fs, Mod) ->
+ collect(Fs, [], [], undefined, Mod).
+
+collect([F | Fs], Cs, As, Header, Mod) ->
+ case erl_syntax_lib:analyze_form(F) of
+ comment ->
+ collect(Fs, [F | Cs], As, Header, Mod);
+ {function, Name} ->
+ L = erl_syntax:get_pos(F),
+ Export = ordsets:is_element(Name, Mod#module.exports),
+ Args = parameters(erl_syntax:function_clauses(F)),
+ collect(Fs, [], [#entry{name = Name, args = Args, line = L,
+ export = Export,
+ data = comment_text(Cs)} | As],
+ Header, Mod);
+ {rule, Name} ->
+ L = erl_syntax:get_pos(F),
+ Export = ordsets:is_element(Name, Mod#module.exports),
+ Args = parameters(erl_syntax:rule_clauses(F)),
+ collect(Fs, [], [#entry{name = Name, args = Args, line = L,
+ export = Export,
+ data = comment_text(Cs)} | As],
+ Header, Mod);
+ {attribute, {module, _}} when Header =:= undefined ->
+ L = erl_syntax:get_pos(F),
+ collect(Fs, [], As, #entry{name = module, line = L,
+ data = comment_text(Cs)},
+ Mod);
+ _ ->
+ %% Drop current seen comments.
+ collect(Fs, [], As, Header, Mod)
+ end;
+collect([], Cs, As, Header, _Mod) ->
+ Footer = #entry{name = footer, data = comment_text(Cs)},
+ As1 = lists:reverse(As),
+ if Header =:= undefined ->
+ {#entry{name = module, data = []}, Footer, As1};
+ true ->
+ {Header, Footer, As1}
+ end.
+
+%% Returns a list of simplified comment information (position and text)
+%% for a list of abstract comments. The order of elements is reversed.
+
+comment_text(Cs) ->
+ comment_text(Cs, []).
+
+comment_text([C | Cs], Ss) ->
+ L = erl_syntax:get_pos(C),
+ comment_text(Cs, [#comment{line = L,
+ text = [remove_percent_chars(S)
+ || S <- erl_syntax:comment_text(C)]}
+ | Ss]);
+comment_text([], Ss) ->
+ Ss.
+
+%% @spec (string()) -> string()
+%%
+%% @doc Replaces leading `%' characters by spaces. For example, `"%%%
+%% foo" -> "\s\s\s foo"', but `"% % foo" -> "\s % foo"', since the
+%% second `%' is preceded by whitespace.
+
+remove_percent_chars([$% | Cs]) -> [$\s | remove_percent_chars(Cs)];
+remove_percent_chars(Cs) -> Cs.
+
+%% Extracting possible parameter names from Erlang clause patterns. The
+%% atom '_' is used when no name can be found. (Better names are made up
+%% later, when we also may have typespecs available; see edoc_data.)
+
+parameters(Clauses) ->
+ select_names([find_names(Ps) || Ps <- patterns(Clauses)]).
+
+patterns(Cs) ->
+ edoc_lib:transpose([erl_syntax:clause_patterns(C) || C <- Cs]).
+
+find_names(Ps) ->
+ find_names(Ps, []).
+
+find_names([P | Ps], Ns) ->
+ case erl_syntax:type(P) of
+ variable ->
+ find_names(Ps, [tidy_name(erl_syntax:variable_name(P)) | Ns]);
+ match_expr ->
+ %% Right-hand side gets priority over left-hand side!
+ %% Note that the list is reversed afterwards.
+ P1 = erl_syntax:match_expr_pattern(P),
+ P2 = erl_syntax:match_expr_body(P),
+ find_names([P1, P2 | Ps], Ns);
+ list ->
+ P1 = erl_syntax:list_tail(P),
+ find_names([P1 | Ps], Ns);
+ record_expr ->
+ A = erl_syntax:record_expr_type(P),
+ N = list_to_atom(capitalize(erl_syntax:atom_name(A))),
+ find_names(Ps, [N | Ns]);
+ infix_expr ->
+ %% this can only be a '++' operation
+ P1 = erl_syntax:infix_expr_right(P),
+ find_names([P1 | Ps], Ns);
+ _ ->
+ find_names(Ps, Ns)
+ end;
+find_names([], Ns) ->
+ lists:reverse(Ns).
+
+select_names(Ls) ->
+ select_names(Ls, [], sets:new()).
+
+select_names([Ns | Ls], As, S) ->
+ A = select_name(Ns, S),
+ select_names(Ls, [A | As], sets:add_element(A, S));
+select_names([], As, _) ->
+ lists:reverse(As).
+
+select_name([A | Ns], S) ->
+ case sets:is_element(A, S) of
+ true ->
+ select_name(Ns, S);
+ false ->
+ A
+ end;
+select_name([], _S) ->
+ '_'.
+
+%% Strip leading underscore characters from parameter names. If the
+%% result does not begin with an uppercase character, we add a single
+%% leading underscore. If the result would be empty, the atom '_' is
+%% returned.
+
+tidy_name(A) ->
+ case atom_to_list(A) of
+ [$_ | Cs] ->
+ list_to_atom(tidy_name_1(Cs));
+ _ ->
+ A
+ end.
+
+tidy_name_1([$_ | Cs]) -> tidy_name_1(Cs);
+tidy_name_1([C | _]=Cs) when C >= $A, C =< $Z -> Cs;
+tidy_name_1([C | _]=Cs) when C >= $\300, C =< $\336, C =/= $\327-> Cs;
+tidy_name_1(Cs) -> [$_ | Cs].
+
+%% Change initial character from lowercase to uppercase.
+
+capitalize([C | Cs]) when C >= $a, C =< $z -> [C - 32 | Cs];
+capitalize(Cs) -> Cs.
+
+%% Collects the tags belonging to each entry, checks them, expands
+%% macros and parses the content.
+
+%% %This is commented out until it can be made private
+%% %@type tags() = #tags{names = set(atom()),
+%% % single = set(atom()),
+%% % module = set(atom()),
+%% % footer = set(atom()),
+%% % function = set(atom())}
+%% % set(T) = sets:set(T)
+
+-record(tags, {names,single,module,function,footer}).
+
+get_tags(Es, Env, File) ->
+ %% Cache this stuff for quick lookups.
+ Tags = #tags{names = sets:from_list(edoc_tags:tag_names()),
+ single = sets:from_list(edoc_tags:tags(single)),
+ module = sets:from_list(edoc_tags:tags(module)),
+ footer = sets:from_list(edoc_tags:tags(footer)),
+ function = sets:from_list(edoc_tags:tags(function))},
+ How = dict:from_list(edoc_tags:tag_parsers()),
+ get_tags(Es, Tags, Env, How, File).
+
+get_tags([#entry{name = Name, data = Cs} = E | Es], Tags, Env,
+ How, File) ->
+ Where = {File, Name},
+ Ts0 = scan_tags(Cs),
+ Ts1 = check_tags(Ts0, Tags, Where),
+ Ts2 = edoc_macros:expand_tags(Ts1, Env, Where),
+ Ts = edoc_tags:parse_tags(Ts2, How, Env, Where),
+ [E#entry{data = Ts} | get_tags(Es, Tags, Env, How, File)];
+get_tags([], _, _, _, _) ->
+ [].
+
+%% Scanning a list of separate comments for tags.
+
+scan_tags([#comment{line = L, text = Ss} | Es]) ->
+ edoc_tags:scan_lines(Ss, L) ++ scan_tags(Es);
+scan_tags([]) ->
+ [].
+
+%% Check the set of found tags (depending on context).
+%% Completely unknown tags are filtered out with a warning.
+
+check_tags(Ts0, Tags, Where) ->
+ Ts = edoc_tags:filter_tags(Ts0, Tags#tags.names, Where),
+ case check_tags_1(Ts, Tags, Where) of
+ false -> Ts;
+ true -> exit(error)
+ end.
+
+check_tags_1(Ts, Tags, {_, module} = Where) ->
+ Allow = Tags#tags.module,
+ Single = Tags#tags.single,
+ edoc_tags:check_tags(Ts, Allow, Single, Where);
+check_tags_1(Ts, Tags, {_, footer} = Where) ->
+ Allow = Tags#tags.footer,
+ Single = Tags#tags.single,
+ edoc_tags:check_tags(Ts, Allow, Single, Where);
+check_tags_1(Ts, Tags, Where) ->
+ Allow = Tags#tags.function,
+ Single = Tags#tags.single,
+ edoc_tags:check_tags(Ts, Allow, Single, Where).
+
+%% Macros for modules
+
+module_macros(Env) ->
+ [{module, atom_to_list(Env#env.module)}]
+ ++ edoc_macros:std_macros(Env).
+
+%% Macros for reading auxiliary edoc-files
+
+file_macros(_Context, Env) ->
+ edoc_macros:std_macros(Env).
diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl
new file mode 100644
index 0000000000..900f0b3040
--- /dev/null
+++ b/lib/edoc/src/edoc_layout.erl
@@ -0,0 +1,875 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2001-2006 Richard Carlsson
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc The standard HTML layout module for EDoc. See the {@link edoc}
+%% module for details on usage.
+
+%% Note that this is written so that it is *not* depending on edoc.hrl!
+
+-module(edoc_layout).
+
+-export([module/2, package/2, overview/2, type/1]).
+
+-import(edoc_report, [report/2]).
+
+-include("xmerl.hrl").
+
+-define(HTML_EXPORT, xmerl_html).
+-define(DEFAULT_XML_EXPORT, ?HTML_EXPORT).
+-define(OVERVIEW_SUMMARY, "overview-summary.html").
+-define(STYLESHEET, "stylesheet.css").
+-define(NL, "\n").
+-define(DESCRIPTION_TITLE, "Description").
+-define(DESCRIPTION_LABEL, "description").
+-define(DATA_TYPES_TITLE, "Data Types").
+-define(DATA_TYPES_LABEL, "types").
+-define(FUNCTION_INDEX_TITLE, "Function Index").
+-define(FUNCTION_INDEX_LABEL, "index").
+-define(FUNCTIONS_TITLE, "Function Details").
+-define(FUNCTIONS_LABEL, "functions").
+
+
+%% @doc The layout function.
+%%
+%% Options to the standard layout:
+%% <dl>
+%% <dt>{@type {index_columns, integer()@}}
+%% </dt>
+%% <dd>Specifies the number of column pairs used for the function
+%% index tables. The default value is 1.
+%% </dd>
+%% <dt>{@type {stylesheet, string()@}}
+%% </dt>
+%% <dd>Specifies the URI used for referencing the stylesheet. The
+%% default value is `"stylesheet.css"'. If an empty string is
+%% specified, no stylesheet reference will be generated.
+%% </dd>
+%% <dt>{@type {sort_functions, bool()@}}
+%% </dt>
+%% <dd>If `true', the detailed function descriptions are listed by
+%% name, otherwise they are listed in the order of occurrence in
+%% the source file. The default value is `true'.
+%% </dd>
+%% <dt>{@type {xml_export, Module::atom()@}}
+%% </dt>
+%% <dd>Specifies an {@link //xmerl. `xmerl'} callback module to be
+%% used for exporting the documentation. See {@link
+%% //xmerl/xmerl:export_simple/3} for details.
+%% </dd>
+%% </dl>
+%%
+%% @see edoc:layout/2
+
+%% NEW-OPTIONS: xml_export, index_columns, stylesheet
+
+module(Element, Options) ->
+ XML = layout_module(Element, init_opts(Element, Options)),
+ Export = proplists:get_value(xml_export, Options,
+ ?DEFAULT_XML_EXPORT),
+ xmerl:export_simple(XML, Export, []).
+
+% Put layout options in a data structure for easier access.
+
+%% %Commented out until it can be made private
+%% %@type opts() = #opts{root = string(),
+%% % stylesheet = string(),
+%% % index_columns = integer()}
+
+-record(opts, {root, stylesheet, index_columns, sort_functions}).
+
+init_opts(Element, Options) ->
+ R = #opts{root = get_attrval(root, Element),
+ index_columns = proplists:get_value(index_columns,
+ Options, 1),
+ sort_functions = proplists:get_value(sort_functions,
+ Options, true)
+ },
+ case proplists:get_value(stylesheet, Options) of
+ undefined ->
+ S = edoc_lib:join_uri(R#opts.root, ?STYLESHEET),
+ R#opts{stylesheet = S};
+ "" ->
+ R; % don't use any stylesheet
+ S when is_list(S) ->
+ R#opts{stylesheet = S};
+ _ ->
+ report("bad value for option `stylesheet'.", []),
+ exit(error)
+ end.
+
+
+%% =====================================================================
+%% XML-BASED LAYOUT ENGINE
+%% =====================================================================
+
+%% We assume that we have expanded XML data.
+
+%% <!ELEMENT module (behaviour*, description?, author*, copyright?,
+%% version?, since?, deprecated?, see*, reference*,
+%% todo?, typedecls?, functions)>
+%% <!ATTLIST module
+%% name CDATA #REQUIRED
+%% private NMTOKEN(yes | no) #IMPLIED
+%% root CDATA #IMPLIED>
+%% <!ELEMENT behaviour (#PCDATA)>
+%% <!ATTLIST behaviour
+%% href CDATA #IMPLIED>
+%% <!ELEMENT description (briefDescription, fullDescription?)>
+%% <!ELEMENT briefDescription (#PCDATA)>
+%% <!ELEMENT fullDescription (#PCDATA)>
+%% <!ELEMENT author EMPTY>
+%% <!ATTLIST author
+%% name CDATA #REQUIRED
+%% email CDATA #IMPLIED
+%% website CDATA #IMPLIED>
+%% <!ELEMENT version (#PCDATA)>
+%% <!ELEMENT since (#PCDATA)>
+%% <!ELEMENT copyright (#PCDATA)>
+%% <!ELEMENT deprecated (description)>
+%% <!ELEMENT see (#PCDATA)>
+%% <!ATTLIST see
+%% name CDATA #REQUIRED
+%% href CDATA #IMPLIED>
+%% <!ELEMENT reference (#PCDATA)>
+%% <!ELEMENT todo (#PCDATA)>
+%% <!ELEMENT typedecls (typedecl+)>
+%% <!ELEMENT functions (function+)>
+
+%% TODO: improve layout of parameterized modules
+
+layout_module(#xmlElement{name = module, content = Es}=E, Opts) ->
+ Args = module_params(get_content(args, Es)),
+ Name = get_attrval(name, E),
+ Title = case get_elem(args, Es) of
+ [] -> ["Module ", Name];
+ _ -> ["Abstract module ", Name, " [", {Args}, "]"]
+ end,
+ Desc = get_content(description, Es),
+ ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Functions = [{function_name(E), E} || E <- get_content(functions, Es)],
+ Types = [{type_name(E), E} || E <- get_content(typedecls, Es)],
+ SortedFs = lists:sort(Functions),
+ Body = (navigation("top")
+ ++ [?NL, hr, ?NL, ?NL, {h1, Title}, ?NL]
+ ++ doc_index(FullDesc, Functions, Types)
+ ++ ShortDesc
+ ++ [?NL]
+ ++ copyright(Es)
+ ++ deprecated(Es, "module")
+ ++ [?NL]
+ ++ version(Es)
+ ++ since(Es)
+ ++ behaviours(Es, Name)
+ ++ authors(Es)
+ ++ references(Es)
+ ++ sees(Es)
+ ++ todos(Es)
+ ++ if FullDesc == [] -> [];
+ true -> [?NL,
+ {h2, [{a, [{name, "description"}],
+ ["Description"]}]}
+ | FullDesc]
+ end
+ ++ types(lists:sort(Types))
+ ++ function_index(SortedFs, Opts#opts.index_columns)
+ ++ if Opts#opts.sort_functions -> functions(SortedFs);
+ true -> functions(Functions)
+ end
+ ++ [hr, ?NL]
+ ++ navigation("bottom")
+ ++ timestamp()),
+ xhtml(Title, stylesheet(Opts), Body).
+
+module_params(Es) ->
+ As = [{get_text(argName, Es1),
+ get_content(fullDescription, get_content(description, Es1))}
+ || #xmlElement{content = Es1} <- Es],
+ case As of
+ [] -> [];
+ [First | Rest] ->
+ [element(1, First) | [ {[", ",A]} || {A, _D} <- Rest]]
+ end.
+
+timestamp() ->
+ [?NL, {p, [{i, [io_lib:fwrite("Generated by EDoc, ~s, ~s.",
+ [edoc_lib:datestr(date()),
+ edoc_lib:timestr(time())])
+ ]}]},
+ ?NL].
+
+stylesheet(Opts) ->
+ case Opts#opts.stylesheet of
+ undefined ->
+ [];
+ CSS ->
+ [{link, [{rel, "stylesheet"},
+ {type, "text/css"},
+ {href, CSS},
+ {title, "EDoc"}], []},
+ ?NL]
+ end.
+
+navigation(Where) ->
+ [?NL,
+ {'div', [{class, "navbar"}],
+ [{a, [{name, "#navbar_" ++ Where}], []},
+ {table, [{width, "100%"}, {border,0},
+ {cellspacing, 0}, {cellpadding, 2},
+ {summary, "navigation bar"}],
+ [{tr,
+ [{td, [{a, [{href, ?OVERVIEW_SUMMARY}, {target,"overviewFrame"}],
+ ["Overview"]}]},
+ {td, [{a, [{href, "http://www.erlang.org/"}],
+ [{img, [{src, "erlang.png"}, {align, "right"},
+ {border, 0}, {alt, "erlang logo"}],
+ []}]}
+ ]}
+ ]}
+ ]}
+ ]}
+ ].
+
+doc_index(FullDesc, Functions, Types) ->
+ case doc_index_rows(FullDesc, Functions, Types) of
+ [] -> [];
+ Rs ->
+ [{ul, [{class, "index"}],
+ [{li, [{a, [{href, local_label(R)}], [T]}]}
+ || {T, R} <- Rs]}]
+ end.
+
+doc_index_rows(FullDesc, Functions, Types) ->
+ (if FullDesc == [] -> [];
+ true -> [{?DESCRIPTION_TITLE, ?DESCRIPTION_LABEL}]
+ end
+ ++ if Types == [] -> [];
+ true -> [{?DATA_TYPES_TITLE, ?DATA_TYPES_LABEL}]
+ end
+ ++ if Functions == [] -> [];
+ true -> [{?FUNCTION_INDEX_TITLE, ?FUNCTION_INDEX_LABEL},
+ {?FUNCTIONS_TITLE, ?FUNCTIONS_LABEL}]
+ end).
+
+function_index(Fs, Cols) ->
+ case function_index_rows(Fs, Cols, []) of
+ [] -> [];
+ Rows ->
+ [?NL,
+ {h2, [{a, [{name, ?FUNCTION_INDEX_LABEL}],
+ [?FUNCTION_INDEX_TITLE]}]},
+ ?NL,
+ {table, [{width, "100%"}, {border, 1},
+ {cellspacing,0}, {cellpadding,2},
+ {summary, "function index"}],
+ Rows},
+ ?NL]
+ end.
+
+function_index_rows(Fs, Cols, Title) ->
+ Rows = (length(Fs) + (Cols - 1)) div Cols,
+ (if Title == [] -> [];
+ true -> [{tr, [{th, [{colspan, Cols * 2}, {align, left}],
+ [Title]}]},
+ ?NL]
+ end
+ ++ lists:flatmap(fun index_row/1,
+ edoc_lib:transpose(edoc_lib:segment(Fs, Rows)))).
+
+index_row(Fs) ->
+ [{tr, lists:flatmap(fun index_col/1, Fs)}, ?NL].
+
+index_col({Name, F=#xmlElement{content = Es}}) ->
+ [{td, [{valign, "top"}],
+ label_href(function_header(Name, F, "*"), F)},
+ {td, index_desc(Es)}].
+
+index_desc(Es) ->
+ Desc = get_content(description, Es),
+ (case get_content(deprecated, Es) of
+ [] -> [];
+ _ -> ["(", {em, ["Deprecated"]}, ".) "]
+ end
+ ++ case get_content(briefDescription, Desc) of
+ [] ->
+ equiv(Es); % no description at all if no equiv
+ ShortDesc ->
+ ShortDesc
+ end).
+
+label_href(Content, F) ->
+ case get_attrval(label, F) of
+ "" -> Content;
+ Ref -> [{a, [{href, local_label(Ref)}], Content}]
+ end.
+
+%% <!ELEMENT function (args, typespec?, returns?, throws?, equiv?,
+%% description?, since?, deprecated?, see*, todo?)>
+%% <!ATTLIST function
+%% name CDATA #REQUIRED
+%% arity CDATA #REQUIRED
+%% exported NMTOKEN(yes | no) #REQUIRED
+%% label CDATA #IMPLIED>
+%% <!ELEMENT args (arg*)>
+%% <!ELEMENT equiv (expr, see?)>
+%% <!ELEMENT expr (#PCDATA)>
+
+functions(Fs) ->
+ Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs),
+ if Es == [] -> [];
+ true ->
+ [?NL,
+ {h2, [{a, [{name, ?FUNCTIONS_LABEL}], [?FUNCTIONS_TITLE]}]},
+ ?NL | Es]
+ end.
+
+function(Name, E=#xmlElement{content = Es}) ->
+ ([?NL,
+ {h3, [{class, "function"}],
+ label_anchor(function_header(Name, E, " *"), E)},
+ ?NL]
+ ++ [{'div', [{class, "spec"}],
+ [?NL,
+ {p,
+ case typespec(get_content(typespec, Es)) of
+ [] ->
+ signature(get_content(args, Es),
+ get_attrval(name, E));
+ Spec -> Spec
+ end},
+ ?NL]
+ ++ case params(get_content(args, Es)) of
+ [] -> [];
+ Ps -> [{p, Ps}, ?NL]
+ end
+ ++ case returns(get_content(returns, Es)) of
+ [] -> [];
+ Rs -> [{p, Rs}, ?NL]
+ end}]
+ ++ throws(Es)
+ ++ equiv_p(Es)
+ ++ deprecated(Es, "function")
+ ++ fulldesc(Es)
+ ++ since(Es)
+ ++ sees(Es)
+ ++ todos(Es)).
+
+function_name(E) ->
+ atom(get_attrval(name, E)) ++ "/" ++ get_attrval(arity, E).
+
+function_header(Name, E, Private) ->
+ case is_exported(E) of
+ true -> [Name];
+ false -> [Name, Private]
+ end.
+
+is_exported(E) ->
+ case get_attrval(exported, E) of
+ "yes" -> true;
+ _ -> false
+ end.
+
+label_anchor(Content, E) ->
+ case get_attrval(label, E) of
+ "" -> Content;
+ Ref -> [{a, [{name, Ref}], Content}]
+ end.
+
+%% <!ELEMENT args (arg*)>
+%% <!ELEMENT arg (argName, description?)>
+%% <!ELEMENT argName (#PCDATA)>
+
+%% This is currently only done for functions without type spec.
+
+signature(Es, Name) ->
+ [{tt, [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> any()"]}].
+
+arg(#xmlElement{content = Es}) ->
+ [get_text(argName, Es)].
+
+%% parameter and return value descriptions (if any)
+
+params(Es) ->
+ As = [{get_text(argName, Es1),
+ get_content(fullDescription, get_content(description, Es1))}
+ || #xmlElement{content = Es1} <- Es],
+ As1 = [A || A <- As, element(2, A) /= []],
+ if As1 == [] ->
+ [];
+ true ->
+ [ { [{tt, [A]}, ": "] ++ D ++ [br, ?NL] }
+ || {A, D} <- As1]
+ end.
+
+returns(Es) ->
+ case get_content(fullDescription, get_content(description, Es)) of
+ [] ->
+ [];
+ D ->
+ ["returns: "] ++ D
+ end.
+
+%% <!ELEMENT throws (type, localdef*)>
+
+throws(Es) ->
+ case get_content(throws, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, (["throws ", {tt, t_utype(get_elem(type, Es1))}]
+ ++ local_defs(get_elem(localdef, Es1)))},
+ ?NL]
+ end.
+
+%% <!ELEMENT typespec (erlangName, type, localdef*)>
+
+typespec([]) -> [];
+typespec(Es) ->
+ [{tt, ([t_name(get_elem(erlangName, Es))]
+ ++ t_utype(get_elem(type, Es)))}]
+ ++ local_defs(get_elem(localdef, Es)).
+
+%% <!ELEMENT typedecl (typedef, description?)>
+%% <!ELEMENT typedef (erlangName, argtypes, type?, localdef*)>
+
+types([]) -> [];
+types(Ts) ->
+ Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E) end, Ts),
+ [?NL,
+ {h2, [{a, [{name, ?DATA_TYPES_LABEL}],
+ [?DATA_TYPES_TITLE]}]},
+ ?NL | Es].
+
+typedecl(Name, E=#xmlElement{content = Es}) ->
+ ([?NL, {h3, [{class, "typedecl"}], label_anchor([Name, "()"], E)}, ?NL]
+ ++ [{p, typedef(get_content(typedef, Es))}, ?NL]
+ ++ fulldesc(Es)).
+
+type_name(#xmlElement{content = Es}) ->
+ t_name(get_elem(erlangName, get_content(typedef, Es))).
+
+typedef(Es) ->
+ Name = ([t_name(get_elem(erlangName, Es)), "("]
+ ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
+ (case get_elem(type, Es) of
+ [] -> [{b, ["abstract datatype"]}, ": ", {tt, Name}];
+ Type ->
+ [{tt, Name ++ [" = "] ++ t_utype(Type)}]
+ end
+ ++ local_defs(get_elem(localdef, Es))).
+
+local_defs([]) -> [];
+local_defs(Es) ->
+ [?NL,
+ {ul, [{class, "definitions"}],
+ lists:concat([[{li, [{tt, localdef(E)}]}, ?NL] || E <- Es])}].
+
+localdef(E = #xmlElement{content = Es}) ->
+ (case get_elem(typevar, Es) of
+ [] ->
+ label_anchor(t_abstype(get_content(abstype, Es)), E);
+ [V] ->
+ t_var(V)
+ end
+ ++ [" = "] ++ t_utype(get_elem(type, Es))).
+
+fulldesc(Es) ->
+ case get_content(fullDescription, get_content(description, Es)) of
+ [] -> [?NL];
+ Desc -> [{p, Desc}, ?NL]
+ end.
+
+sees(Es) ->
+ case get_elem(see, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["See also:"]}, " "] ++ seq(fun see/1, Es1, ["."])},
+ ?NL]
+ end.
+
+see(E=#xmlElement{content = Es}) ->
+ see(E, Es).
+
+see(E, Es) ->
+ case href(E) of
+ [] -> Es;
+ Ref ->
+ [{a, Ref, Es}]
+ end.
+
+href(E) ->
+ case get_attrval(href, E) of
+ "" -> [];
+ URI ->
+ T = case get_attrval(target, E) of
+ "" -> [];
+ S -> [{target, S}]
+ end,
+ [{href, URI} | T]
+ end.
+
+equiv_p(Es) ->
+ equiv(Es, true).
+
+equiv(Es) ->
+ equiv(Es, false).
+
+equiv(Es, P) ->
+ case get_content(equiv, Es) of
+ [] -> [];
+ Es1 ->
+ case get_content(expr, Es1) of
+ [] -> [];
+ [Expr] ->
+ Expr1 = [{tt, [Expr]}],
+ Expr2 = case get_elem(see, Es1) of
+ [] ->
+ Expr1;
+ [E=#xmlElement{}] ->
+ see(E, Expr1)
+ end,
+ Txt = ["Equivalent to "] ++ Expr2 ++ ["."],
+ (case P of
+ true -> [{p, Txt}];
+ false -> Txt
+ end
+ ++ [?NL])
+ end
+ end.
+
+copyright(Es) ->
+ case get_content(copyright, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, ["Copyright \251 " | Es1]}, ?NL]
+ end.
+
+version(Es) ->
+ case get_content(version, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["Version:"]}, " " | Es1]}, ?NL]
+ end.
+
+since(Es) ->
+ case get_content(since, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["Introduced in:"]}, " " | Es1]}, ?NL]
+ end.
+
+deprecated(Es, S) ->
+ Es1 = get_content(description, get_content(deprecated, Es)),
+ case get_content(fullDescription, Es1) of
+ [] -> [];
+ Es2 ->
+ [{p, [{b, ["This " ++ S ++ " is deprecated:"]}, " " | Es2]},
+ ?NL]
+ end.
+
+behaviours(Es, Name) ->
+ (case get_elem(behaviour, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, ([{b, ["Behaviours:"]}, " "]
+ ++ seq(fun behaviour/1, Es1, ["."]))},
+ ?NL]
+ end
+ ++
+ case get_content(callbacks, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, ([{b, ["This module defines the ", {tt, [Name]},
+ " behaviour."]},
+ br, " Required callback functions: "]
+ ++ seq(fun callback/1, Es1, ["."]))},
+ ?NL]
+ end).
+
+behaviour(E=#xmlElement{content = Es}) ->
+ see(E, [{tt, Es}]).
+
+callback(E=#xmlElement{}) ->
+ Name = get_attrval(name, E),
+ Arity = get_attrval(arity, E),
+ [{tt, [Name, "/", Arity]}].
+
+authors(Es) ->
+ case get_elem(author, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["Authors:"]}, " "] ++ seq(fun author/1, Es1, ["."])},
+ ?NL]
+ end.
+
+atom(String) ->
+ io_lib:write_atom(list_to_atom(String)).
+
+%% <!ATTLIST author
+%% name CDATA #REQUIRED
+%% email CDATA #IMPLIED
+%% website CDATA #IMPLIED>
+
+author(E=#xmlElement{}) ->
+ Name = get_attrval(name, E),
+ Mail = get_attrval(email, E),
+ URI = get_attrval(website, E),
+ (if Name == Mail ->
+ [{a, [{href, "mailto:" ++ Mail}],[{tt, [Mail]}]}];
+ true ->
+ if Mail == "" -> [Name];
+ true -> [Name, " (", {a, [{href, "mailto:" ++ Mail}],
+ [{tt, [Mail]}]}, ")"]
+ end
+ end
+ ++ if URI == "" ->
+ [];
+ true ->
+ [" [", {em, ["web site:"]}, " ",
+ {tt, [{a, [{href, URI}, {target, "_top"}], [URI]}]},
+ "]"]
+ end).
+
+references(Es) ->
+ case get_elem(reference, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["References"]},
+ {ul, [{li, C} || #xmlElement{content = C} <- Es1]}]},
+ ?NL]
+ end.
+
+todos(Es) ->
+ case get_elem(todo, Es) of
+ [] -> [];
+ Es1 ->
+ Todos = [{li, [{font, [{color,red}], C}]}
+ || #xmlElement{content = C} <- Es1],
+ [{p, [{b, [{font, [{color,red}], ["To do"]}]},
+ {ul, Todos}]},
+ ?NL]
+ end.
+
+t_name([E]) ->
+ N = get_attrval(name, E),
+ case get_attrval(module, E) of
+ "" -> atom(N);
+ M ->
+ S = atom(M) ++ ":" ++ atom(N),
+ case get_attrval(app, E) of
+ "" -> S;
+ A -> "//" ++ atom(A) ++ "/" ++ S
+ end
+ end.
+
+t_utype([E]) ->
+ t_utype_elem(E).
+
+t_utype_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> t_type(Es);
+ Name ->
+ T = t_type(Es),
+ case T of
+ [Name] -> T; % avoid generating "Foo::Foo"
+ T -> [Name] ++ ["::"] ++ T
+ end
+ end.
+
+t_type([E=#xmlElement{name = typevar}]) ->
+ t_var(E);
+t_type([E=#xmlElement{name = atom}]) ->
+ t_atom(E);
+t_type([E=#xmlElement{name = integer}]) ->
+ t_integer(E);
+t_type([E=#xmlElement{name = float}]) ->
+ t_float(E);
+t_type([#xmlElement{name = nil}]) ->
+ t_nil();
+t_type([#xmlElement{name = list, content = Es}]) ->
+ t_list(Es);
+t_type([#xmlElement{name = tuple, content = Es}]) ->
+ t_tuple(Es);
+t_type([#xmlElement{name = 'fun', content = Es}]) ->
+ t_fun(Es);
+t_type([#xmlElement{name = record, content = Es}]) ->
+ t_record(Es);
+t_type([E = #xmlElement{name = abstype, content = Es}]) ->
+ T = t_abstype(Es),
+ see(E, T);
+t_type([#xmlElement{name = union, content = Es}]) ->
+ t_union(Es).
+
+t_var(E) ->
+ [get_attrval(name, E)].
+
+t_atom(E) ->
+ [get_attrval(value, E)].
+
+t_integer(E) ->
+ [get_attrval(value, E)].
+
+t_float(E) ->
+ [get_attrval(value, E)].
+
+t_nil() ->
+ ["[]"].
+
+t_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"].
+
+t_tuple(Es) ->
+ ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+
+t_fun(Es) ->
+ ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
+ [") -> "] ++ t_utype(get_elem(type, Es))).
+
+t_record(Es) ->
+ ["#"] ++ t_type(get_elem(atom, Es)) ++ ["{"]
+ ++ seq(fun t_field/1, get_elem(field, Es), ["}"]).
+
+t_field(#xmlElement{content = Es}) ->
+ t_type(get_elem(atom, Es)) ++ [" = "] ++ t_utype(get_elem(type, Es)).
+
+t_abstype(Es) ->
+ ([t_name(get_elem(erlangName, Es)), "("]
+ ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"])).
+
+t_union(Es) ->
+ seq(fun t_utype_elem/1, Es, " | ", []).
+
+seq(F, Es) ->
+ seq(F, Es, []).
+
+seq(F, Es, Tail) ->
+ seq(F, Es, ", ", Tail).
+
+seq(F, [E], _Sep, Tail) ->
+ F(E) ++ Tail;
+seq(F, [E | Es], Sep, Tail) ->
+ F(E) ++ [Sep] ++ seq(F, Es, Sep, Tail);
+seq(_F, [], _Sep, Tail) ->
+ Tail.
+
+get_elem(Name, [#xmlElement{name = Name} = E | Es]) ->
+ [E | get_elem(Name, Es)];
+get_elem(Name, [_ | Es]) ->
+ get_elem(Name, Es);
+get_elem(_, []) ->
+ [].
+
+get_attr(Name, [#xmlAttribute{name = Name} = A | As]) ->
+ [A | get_attr(Name, As)];
+get_attr(Name, [_ | As]) ->
+ get_attr(Name, As);
+get_attr(_, []) ->
+ [].
+
+get_attrval(Name, #xmlElement{attributes = As}) ->
+ case get_attr(Name, As) of
+ [#xmlAttribute{value = V}] ->
+ V;
+ [] -> ""
+ end.
+
+get_content(Name, Es) ->
+ case get_elem(Name, Es) of
+ [#xmlElement{content = Es1}] ->
+ Es1;
+ [] -> []
+ end.
+
+get_text(Name, Es) ->
+ case get_content(Name, Es) of
+ [#xmlText{value = Text}] ->
+ Text;
+ [] -> ""
+ end.
+
+local_label(R) ->
+ "#" ++ R.
+
+xhtml(Title, CSS, Body) ->
+ [{html, [?NL,
+ {head, [?NL,
+ {title, Title},
+ ?NL] ++ CSS},
+ ?NL,
+ {body, [{bgcolor, "white"}], Body},
+ ?NL]
+ },
+ ?NL].
+
+%% ---------------------------------------------------------------------
+
+type(E) ->
+ type(E, []).
+
+type(E, Ds) ->
+ xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds),
+ ?HTML_EXPORT).
+
+package(E=#xmlElement{name = package, content = Es}, Options) ->
+ Opts = init_opts(E, Options),
+ Name = get_text(packageName, Es),
+ Title = ["Package ", Name],
+ Desc = get_content(description, Es),
+% ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Body = ([?NL, {h1, [Title]}, ?NL]
+% ++ ShortDesc
+ ++ copyright(Es)
+ ++ deprecated(Es, "package")
+ ++ version(Es)
+ ++ since(Es)
+ ++ authors(Es)
+ ++ references(Es)
+ ++ sees(Es)
+ ++ todos(Es)
+ ++ FullDesc),
+ XML = xhtml(Title, stylesheet(Opts), Body),
+ xmerl:export_simple(XML, ?HTML_EXPORT, []).
+
+overview(E=#xmlElement{name = overview, content = Es}, Options) ->
+ Opts = init_opts(E, Options),
+ Title = [get_text(title, Es)],
+ Desc = get_content(description, Es),
+% ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Body = (navigation("top")
+ ++ [?NL, {h1, [Title]}, ?NL]
+% ++ ShortDesc
+ ++ copyright(Es)
+ ++ version(Es)
+ ++ since(Es)
+ ++ authors(Es)
+ ++ references(Es)
+ ++ sees(Es)
+ ++ todos(Es)
+ ++ FullDesc
+ ++ [?NL, hr]
+ ++ navigation("bottom")
+ ++ timestamp()),
+ XML = xhtml(Title, stylesheet(Opts), Body),
+ xmerl:export_simple(XML, ?HTML_EXPORT, []).
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
new file mode 100644
index 0000000000..47e61f7932
--- /dev/null
+++ b/lib/edoc/src/edoc_lib.erl
@@ -0,0 +1,998 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright 2001-2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc Utility functions for EDoc.
+
+-module(edoc_lib).
+
+-export([count/2, lines/1, split_at/2, split_at_stop/1,
+ split_at_space/1, filename/1, transpose/1, segment/2,
+ get_first_sentence/1, is_space/1, strip_space/1, parse_expr/2,
+ parse_contact/2, escape_uri/1, join_uri/2, is_relative_uri/1,
+ is_name/1, to_label/1, find_doc_dirs/0, find_sources/2,
+ find_sources/3, find_file/3, try_subdir/2, unique/1,
+ write_file/3, write_file/4, write_info_file/4,
+ read_info_file/1, get_doc_env/1, get_doc_env/4, copy_file/2,
+ uri_get/1, run_doclet/2, run_layout/2,
+ simplify_path/1, timestr/1, datestr/1]).
+
+-import(edoc_report, [report/2, warning/2]).
+
+-include("edoc.hrl").
+-include("xmerl.hrl").
+
+-define(FILE_BASE, "/").
+
+
+%% ---------------------------------------------------------------------
+%% List and string utilities
+
+timestr({H,M,Sec}) ->
+ lists:flatten(io_lib:fwrite("~2.2.0w:~2.2.0w:~2.2.0w",[H,M,Sec])).
+
+datestr({Y,M,D}) ->
+ Ms = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"],
+ lists:flatten(io_lib:fwrite("~s ~w ~w",[lists:nth(M, Ms),D,Y])).
+
+count(X, Xs) ->
+ count(X, Xs, 0).
+
+count(X, [X | Xs], N) ->
+ count(X, Xs, N + 1);
+count(X, [_ | Xs], N) ->
+ count(X, Xs, N);
+count(_X, [], N) ->
+ N.
+
+lines(Cs) ->
+ lines(Cs, [], []).
+
+lines([$\n | Cs], As, Ls) ->
+ lines(Cs, [], [lists:reverse(As) | Ls]);
+lines([C | Cs], As, Ls) ->
+ lines(Cs, [C | As], Ls);
+lines([], As, Ls) ->
+ lists:reverse([lists:reverse(As) | Ls]).
+
+split_at(Cs, K) ->
+ split_at(Cs, K, []).
+
+split_at([K | Cs], K, As) ->
+ {lists:reverse(As), Cs};
+split_at([C | Cs], K, As) ->
+ split_at(Cs, K, [C | As]);
+split_at([], _K, As) ->
+ {lists:reverse(As), []}.
+
+split_at_stop(Cs) ->
+ split_at_stop(Cs, []).
+
+split_at_stop([$., $\s | Cs], As) ->
+ {lists:reverse(As), Cs};
+split_at_stop([$., $\t | Cs], As) ->
+ {lists:reverse(As), Cs};
+split_at_stop([$., $\n | Cs], As) ->
+ {lists:reverse(As), Cs};
+split_at_stop([$.], As) ->
+ {lists:reverse(As), []};
+split_at_stop([C | Cs], As) ->
+ split_at_stop(Cs, [C | As]);
+split_at_stop([], As) ->
+ {lists:reverse(As), []}.
+
+split_at_space(Cs) ->
+ split_at_space(Cs, []).
+
+split_at_space([$\s | Cs], As) ->
+ {lists:reverse(As), Cs};
+split_at_space([$\t | Cs], As) ->
+ {lists:reverse(As), Cs};
+split_at_space([$\n | Cs], As) ->
+ {lists:reverse(As), Cs};
+split_at_space([C | Cs], As) ->
+ split_at_space(Cs, [C | As]);
+split_at_space([], As) ->
+ {lists:reverse(As), []}.
+
+is_space([$\s | Cs]) -> is_space(Cs);
+is_space([$\t | Cs]) -> is_space(Cs);
+is_space([$\n | Cs]) -> is_space(Cs);
+is_space([_C | _Cs]) -> false;
+is_space([]) -> true.
+
+strip_space([$\s | Cs]) -> strip_space(Cs);
+strip_space([$\t | Cs]) -> strip_space(Cs);
+strip_space([$\n | Cs]) -> strip_space(Cs);
+strip_space(Cs) -> Cs.
+
+segment(Es, N) ->
+ segment(Es, [], [], 0, N).
+
+segment([E | Es], As, Cs, N, M) when N < M ->
+ segment(Es, [E | As], Cs, N + 1, M);
+segment([_ | _] = Es, As, Cs, _N, M) ->
+ segment(Es, [], [lists:reverse(As) | Cs], 0, M);
+segment([], [], Cs, _N, _M) ->
+ lists:reverse(Cs);
+segment([], As, Cs, _N, _M) ->
+ lists:reverse([lists:reverse(As) | Cs]).
+
+transpose([]) -> [];
+transpose([[] | Xss]) -> transpose(Xss);
+transpose([[X | Xs] | Xss]) ->
+ [[X | [H || [H | _T] <- Xss]]
+ | transpose([Xs | [T || [_H | T] <- Xss]])].
+
+%% Note that the parser will not produce two adjacent text segments;
+%% thus, if a text segment ends with a period character, it marks the
+%% end of the summary sentence only if it is also the last segment in
+%% the list, or is followed by a 'p' or 'br' ("whitespace") element.
+
+get_first_sentence([#xmlElement{name = p, content = Es} | _]) ->
+ %% Descend into initial paragraph.
+ get_first_sentence_1(Es);
+get_first_sentence(Es) ->
+ get_first_sentence_1(Es).
+
+get_first_sentence_1([E = #xmlText{value = Txt} | Es]) ->
+ Last = case Es of
+ [#xmlElement{name = p} | _] -> true;
+ [#xmlElement{name = br} | _] -> true;
+ [] -> true;
+ _ -> false
+ end,
+ case end_of_sentence(Txt, Last) of
+ {value, Txt1} ->
+ [E#xmlText{value = Txt1}];
+ none ->
+ [E | get_first_sentence_1(Es)]
+ end;
+get_first_sentence_1([E | Es]) ->
+ % Skip non-text segments - don't descend further
+ [E | get_first_sentence_1(Es)];
+get_first_sentence_1([]) ->
+ [].
+
+end_of_sentence(Cs, Last) ->
+ end_of_sentence(Cs, Last, []).
+
+%% We detect '.' and '!' as end-of-sentence markers.
+
+end_of_sentence([C=$., $\s | _], _, As) ->
+ end_of_sentence_1(C, true, As);
+end_of_sentence([C=$., $\t | _], _, As) ->
+ end_of_sentence_1(C, true, As);
+end_of_sentence([C=$., $\n | _], _, As) ->
+ end_of_sentence_1(C, true, As);
+end_of_sentence([C=$.], Last, As) ->
+ end_of_sentence_1(C, Last, As);
+end_of_sentence([C=$!, $\s | _], _, As) ->
+ end_of_sentence_1(C, true, As);
+end_of_sentence([C=$!, $\t | _], _, As) ->
+ end_of_sentence_1(C, true, As);
+end_of_sentence([C=$!, $\n | _], _, As) ->
+ end_of_sentence_1(C, true, As);
+end_of_sentence([C=$!], Last, As) ->
+ end_of_sentence_1(C, Last, As);
+end_of_sentence([C | Cs], Last, As) ->
+ end_of_sentence(Cs, Last, [C | As]);
+end_of_sentence([], Last, As) ->
+ end_of_sentence_1($., Last, strip_space(As)). % add a '.'
+
+end_of_sentence_1(C, true, As) ->
+ {value, lists:reverse([C | As])};
+end_of_sentence_1(_, false, _) ->
+ none.
+
+%% For handling ISO 8859-1 (Latin-1) we use the following information:
+%%
+%% 000 - 037 NUL - US control
+%% 040 - 057 SPC - / punctuation
+%% 060 - 071 0 - 9 digit
+%% 072 - 100 : - @ punctuation
+%% 101 - 132 A - Z uppercase
+%% 133 - 140 [ - ` punctuation
+%% 141 - 172 a - z lowercase
+%% 173 - 176 { - ~ punctuation
+%% 177 DEL control
+%% 200 - 237 control
+%% 240 - 277 NBSP - � punctuation
+%% 300 - 326 � - � uppercase
+%% 327 � punctuation
+%% 330 - 336 � - � uppercase
+%% 337 - 366 � - � lowercase
+%% 367 � punctuation
+%% 370 - 377 � - � lowercase
+
+%% Names must begin with a lowercase letter and contain only
+%% alphanumerics and underscores.
+
+is_name([C | Cs]) when C >= $a, C =< $z ->
+ is_name_1(Cs);
+is_name([C | Cs]) when C >= $\337, C =< $\377, C =/= $\367 ->
+ is_name_1(Cs);
+is_name(_) -> false.
+
+is_name_1([C | Cs]) when C >= $a, C =< $z ->
+ is_name_1(Cs);
+is_name_1([C | Cs]) when C >= $A, C =< $Z ->
+ is_name_1(Cs);
+is_name_1([C | Cs]) when C >= $0, C =< $9 ->
+ is_name_1(Cs);
+is_name_1([C | Cs]) when C >= $\300, C =< $\377, C =/= $\327, C =/= $\367 ->
+ is_name_1(Cs);
+is_name_1([$_ | Cs]) ->
+ is_name_1(Cs);
+is_name_1([]) -> true;
+is_name_1(_) -> false.
+
+to_atom(A) when is_atom(A) -> A;
+to_atom(S) when is_list(S) -> list_to_atom(S).
+
+unique([X | Xs]) -> [X | unique(Xs, X)];
+unique([]) -> [].
+
+unique([X | Xs], X) -> unique(Xs, X);
+unique([X | Xs], _) -> [X | unique(Xs, X)];
+unique([], _) -> [].
+
+
+%% ---------------------------------------------------------------------
+%% Parsing utilities
+
+%% @doc EDoc Erlang expression parsing. For parsing things like the
+%% content of <a href="overview-summary.html#ftag-equiv">`@equiv'</a>
+%% tags, and strings denoting file names, e.g. in @headerfile. Also used
+%% by {@link edoc_run}.
+
+parse_expr(S, L) ->
+ case erl_scan:string(S ++ ".", L) of
+ {ok, Ts, _} ->
+ case erl_parse:parse_exprs(Ts) of
+ {ok, [Expr]} ->
+ Expr;
+ {error, {999999, erl_parse, _}} ->
+ throw_error(eof, L);
+ {error, E} ->
+ throw_error(E, L)
+ end;
+ {error, E, _} ->
+ throw_error(E, L)
+ end.
+
+
+%% @doc EDoc "contact information" parsing. This is the type of the
+%% content in e.g.
+%% <a href="overview-summary.html#mtag-author">`@author'</a> tags.
+
+%% @type info() = #info{name = string(),
+%% mail = string(),
+%% uri = string()}
+
+-record(info, {name = "", email = "", uri = ""}).
+
+parse_contact(S, L) ->
+ I = scan_name(S, L, #info{}, []),
+ {I#info.name, I#info.email, I#info.uri}.
+
+%% The name is taken as the first non-whitespace-only string before,
+%% between, or following the e-mail/URI sections. Subsequent text that
+%% is not e/mail or URI is ignored.
+
+scan_name([$< | Cs], L, I, As) ->
+ case I#info.email of
+ "" ->
+ {Cs1, I1} = scan_email(Cs, L, set_name(I, As), []),
+ scan_name(Cs1, L, I1, []);
+ _ ->
+ throw_error("multiple '<...>' sections.", L)
+ end;
+scan_name([$[ | Cs], L, I, As) ->
+ case I#info.uri of
+ "" ->
+ {Cs1, I1} = scan_uri(Cs, L, set_name(I, As), []),
+ scan_name(Cs1, L, I1, []);
+ _ ->
+ throw_error("multiple '[...]' sections.", L)
+ end;
+scan_name([$\n | Cs], L, I, As) ->
+ scan_name(Cs, L + 1, I, [$\n | As]);
+scan_name([C | Cs], L, I, As) ->
+ scan_name(Cs, L, I, [C | As]);
+scan_name([], _L, I, As) ->
+ set_name(I, As).
+
+scan_uri([$] | Cs], _L, I, As) ->
+ {Cs, I#info{uri = strip_and_reverse(As)}};
+scan_uri([$\n | Cs], L, I, As) ->
+ scan_uri(Cs, L + 1, I, [$\n | As]);
+scan_uri([C | Cs], L, I, As) ->
+ scan_uri(Cs, L, I, [C | As]);
+scan_uri([], L, _I, _As) ->
+ throw_error({missing, $]}, L).
+
+scan_email([$> | Cs], _L, I, As) ->
+ {Cs, I#info{email = strip_and_reverse(As)}};
+scan_email([$\n | Cs], L, I, As) ->
+ scan_email(Cs, L + 1, I, [$\n | As]);
+scan_email([C | Cs], L, I, As) ->
+ scan_email(Cs, L, I, [C | As]);
+scan_email([], L, _I, _As) ->
+ throw_error({missing, $>}, L).
+
+set_name(I, As) ->
+ case I#info.name of
+ "" -> I#info{name = strip_and_reverse(As)};
+ _ -> I
+ end.
+
+strip_and_reverse(As) ->
+ edoc_lib:strip_space(lists:reverse(edoc_lib:strip_space(As))).
+
+
+%% ---------------------------------------------------------------------
+%% URI and Internet
+
+%% This is a conservative URI escaping, which escapes anything that may
+%% not appear in an NMTOKEN ([a-zA-Z0-9]|'.'|'-'|'_'), including ':'.
+%% Characters are first encoded in UTF-8.
+%%
+%% Note that this should *not* be applied to complete URI, but only to
+%% segments that may need escaping, when forming a complete URI.
+%%
+%% TODO: general utf-8 encoding for all of Unicode (0-16#10ffff)
+
+escape_uri([C | Cs]) when C >= $a, C =< $z ->
+ [C | escape_uri(Cs)];
+escape_uri([C | Cs]) when C >= $A, C =< $Z ->
+ [C | escape_uri(Cs)];
+escape_uri([C | Cs]) when C >= $0, C =< $9 ->
+ [C | escape_uri(Cs)];
+escape_uri([C = $. | Cs]) ->
+ [C | escape_uri(Cs)];
+escape_uri([C = $- | Cs]) ->
+ [C | escape_uri(Cs)];
+escape_uri([C = $_ | Cs]) ->
+ [C | escape_uri(Cs)];
+escape_uri([C | Cs]) when C > 16#7f ->
+ %% This assumes that characters are at most 16 bits wide.
+ escape_byte(((C band 16#c0) bsr 6) + 16#c0)
+ ++ escape_byte(C band 16#3f + 16#80)
+ ++ escape_uri(Cs);
+escape_uri([C | Cs]) ->
+ escape_byte(C) ++ escape_uri(Cs);
+escape_uri([]) ->
+ [].
+
+escape_byte(C) ->
+ "%" ++ hex_octet(C).
+
+% utf8([C | Cs]) when C > 16#7f ->
+% [((C band 16#c0) bsr 6) + 16#c0, C band 16#3f ++ 16#80 | utf8(Cs)];
+% utf8([C | Cs]) ->
+% [C | utf8(Cs)];
+% utf8([]) ->
+% [].
+
+hex_octet(N) when N =< 9 ->
+ [$0 + N];
+hex_octet(N) when N > 15 ->
+ hex_octet(N bsr 4) ++ hex_octet(N band 15);
+hex_octet(N) ->
+ [N - 10 + $a].
+
+%% Please note that URI are *not* file names. Don't use the stdlib
+%% 'filename' module for operations on (any parts of) URI.
+
+join_uri(Base, "") ->
+ Base;
+join_uri("", Path) ->
+ Path;
+join_uri(Base, Path) ->
+ Base ++ "/" ++ Path.
+
+%% Check for relative URI; "network paths" ("//...") not included!
+
+is_relative_uri([$: | _]) ->
+ false;
+is_relative_uri([$/, $/ | _]) ->
+ false;
+is_relative_uri([$/ | _]) ->
+ true;
+is_relative_uri([$? | _]) ->
+ true;
+is_relative_uri([$# | _]) ->
+ true;
+is_relative_uri([_ | Cs]) ->
+ is_relative_uri(Cs);
+is_relative_uri([]) ->
+ true.
+
+uri_get("file:///" ++ Path) ->
+ uri_get_file(Path);
+uri_get("file://localhost/" ++ Path) ->
+ uri_get_file(Path);
+uri_get("file://" ++ Path) ->
+ Msg = io_lib:format("cannot handle 'file:' scheme with "
+ "nonlocal network-path: 'file://~s'.",
+ [Path]),
+ {error, Msg};
+uri_get("file:/" ++ Path) ->
+ uri_get_file(Path);
+uri_get("file:" ++ Path) ->
+ Msg = io_lib:format("ignoring malformed URI: 'file:~s'.", [Path]),
+ {error, Msg};
+uri_get("http:" ++ Path) ->
+ uri_get_http("http:" ++ Path);
+uri_get("ftp:" ++ Path) ->
+ uri_get_ftp("ftp:" ++ Path);
+uri_get("//" ++ Path) ->
+ Msg = io_lib:format("cannot access network-path: '//~s'.", [Path]),
+ {error, Msg};
+uri_get(URI) ->
+ case is_relative_uri(URI) of
+ true ->
+ uri_get_file(URI);
+ false ->
+ Msg = io_lib:format("cannot handle URI: '~s'.", [URI]),
+ {error, Msg}
+ end.
+
+uri_get_file(File0) ->
+ File = filename:join(?FILE_BASE, File0),
+ case read_file(File) of
+ {ok, Text} ->
+ {ok, Text};
+ {error, R} ->
+ {error, file:format_error(R)}
+ end.
+
+uri_get_http(URI) ->
+ %% Try using option full_result=false
+ case catch {ok, http:request(get, {URI,[]}, [],
+ [{full_result, false}])} of
+ {'EXIT', _} ->
+ uri_get_http_r10(URI);
+ Result ->
+ uri_get_http_1(Result, URI)
+ end.
+
+uri_get_http_r10(URI) ->
+ %% Try most general form of request
+ Result = (catch {ok, http:request(get, {URI,[]}, [], [])}),
+ uri_get_http_1(Result, URI).
+
+uri_get_http_1(Result, URI) ->
+ case Result of
+ {ok, {ok, {200, Text}}} when is_list(Text) ->
+ %% new short result format
+ {ok, Text};
+ {ok, {ok, {Status, Text}}} when is_integer(Status), is_list(Text) ->
+ %% new short result format when status /= 200
+ Phrase = httpd_util:reason_phrase(Status),
+ {error, http_errmsg(Phrase, URI)};
+ {ok, {ok, {{_Vsn, 200, _Phrase}, _Hdrs, Text}}} when is_list(Text) ->
+ %% new long result format
+ {ok, Text};
+ {ok, {ok, {{_Vsn, _Status, Phrase}, _Hdrs, Text}}} when is_list(Text) ->
+ %% new long result format when status /= 200
+ {error, http_errmsg(Phrase, URI)};
+ {ok, {200,_Hdrs,Text}} when is_list(Text) ->
+ %% old result format
+ {ok, Text};
+ {ok, {Status,_Hdrs,Text}} when is_list(Text) ->
+ %% old result format when status /= 200
+ Phrase = httpd_util:reason_phrase(Status),
+ {error, http_errmsg(Phrase, URI)};
+ {ok, {error, R}} ->
+ Reason = inet:format_error(R),
+ {error, http_errmsg(Reason, URI)};
+ {ok, R} ->
+ Reason = io_lib:format("bad return value ~P", [R, 5]),
+ {error, http_errmsg(Reason, URI)};
+ {'EXIT', R} ->
+ Reason = io_lib:format("crashed with reason ~w", [R]),
+ {error, http_errmsg(Reason, URI)};
+ R ->
+ Reason = io_lib:format("uncaught throw: ~w", [R]),
+ {error, http_errmsg(Reason, URI)}
+ end.
+
+http_errmsg(Reason, URI) ->
+ io_lib:format("http error: ~s: '~s'", [Reason, URI]).
+
+%% TODO: implement ftp access method
+
+uri_get_ftp(URI) ->
+ Msg = io_lib:format("cannot access ftp scheme yet: '~s'.", [URI]),
+ {error, Msg}.
+
+to_label([$\s | Cs]) ->
+ to_label(Cs);
+to_label([$\t | Cs]) ->
+ to_label(Cs);
+to_label([$\n | Cs]) ->
+ to_label(Cs);
+to_label([]) ->
+ [];
+to_label(Cs) ->
+ to_label_1(Cs).
+
+to_label_1([$\s | Cs]) ->
+ to_label_2([$\s | Cs]);
+to_label_1([$\t | Cs]) ->
+ to_label_2([$\s | Cs]);
+to_label_1([$\n | Cs]) ->
+ to_label_2([$\s | Cs]);
+to_label_1([C | Cs]) ->
+ [C | to_label_1(Cs)];
+to_label_1([]) ->
+ [].
+
+to_label_2(Cs) ->
+ case to_label(Cs) of
+ [] -> [];
+ Cs1 -> [$_ | Cs1]
+ end.
+
+
+%% ---------------------------------------------------------------------
+%% Files
+
+filename([C | T]) when is_integer(C), C > 0 ->
+ [C | filename(T)];
+filename([H|T]) ->
+ filename(H) ++ filename(T);
+filename([]) ->
+ [];
+filename(N) when is_atom(N) ->
+ atom_to_list(N);
+filename(N) ->
+ report("bad filename: `~P'.", [N, 25]),
+ exit(error).
+
+copy_file(From, To) ->
+ case file:copy(From, To) of
+ {ok, _} -> ok;
+ {error, R} ->
+ R1 = file:format_error(R),
+ report("error copying '~s' to '~s': ~s.", [From, To, R1]),
+ exit(error)
+ end.
+
+list_dir(Dir, Error) ->
+ case file:list_dir(Dir) of
+ {ok, Fs} ->
+ Fs;
+ {error, R} ->
+ F = case Error of
+ %% true ->
+ %% fun (S, As) -> report(S, As), exit(error) end;
+ false ->
+ fun (S, As) -> warning(S, As), [] end
+ end,
+ R1 = file:format_error(R),
+ F("could not read directory '~s': ~s.", [filename(Dir), R1])
+ end.
+
+simplify_path(P) ->
+ case filename:basename(P) of
+ "." ->
+ simplify_path(filename:dirname(P));
+ ".." ->
+ simplify_path(filename:dirname(filename:dirname(P)));
+ _ ->
+ P
+ end.
+
+%% The directories From and To are assumed to exist.
+
+%% copy_dir(From, To) ->
+%% Es = list_dir(From, true), % error if listing fails
+%% lists:foreach(fun (E) -> copy_dir(From, To, E) end, Es).
+
+%% copy_dir(From, To, Entry) ->
+%% From1 = filename:join(From, Entry),
+%% To1 = filename:join(To, Entry),
+%% case filelib:is_dir(From1) of
+%% true ->
+%% make_dir(To1),
+%% copy_dir(From1, To1);
+%% false ->
+%% copy_file(From1, To1)
+%% end.
+
+%% make_dir(Dir) ->
+%% case file:make_dir(Dir) of
+%% ok -> ok;
+%% {error, R} ->
+%% R1 = file:format_error(R),
+%% report("cannot create directory '~s': ~s.", [Dir, R1]),
+%% exit(error)
+%% end.
+
+try_subdir(Dir, Subdir) ->
+ D = filename:join(Dir, Subdir),
+ case filelib:is_dir(D) of
+ true -> D;
+ false -> Dir
+ end.
+
+%% @spec (Text::deep_string(), Dir::edoc:filename(),
+%% Name::edoc:filename()) -> ok
+%%
+%% @doc Write the given `Text' to the file named by `Name' in directory
+%% `Dir'. If the target directory does not exist, it will be created.
+
+write_file(Text, Dir, Name) ->
+ write_file(Text, Dir, Name, '').
+
+
+%% @spec (Text::deep_string(), Dir::edoc:filename(),
+%% Name::edoc:filename(), Package::atom()|string()) -> ok
+%% @doc Like {@link write_file/3}, but adds path components to the target
+%% directory corresponding to the specified package.
+
+write_file(Text, Dir, Name, Package) ->
+ Dir1 = filename:join([Dir | packages:split(Package)]),
+ File = filename:join(Dir1, Name),
+ ok = filelib:ensure_dir(File),
+ case file:open(File, [write]) of
+ {ok, FD} ->
+ io:put_chars(FD, Text),
+ ok = file:close(FD);
+ {error, R} ->
+ R1 = file:format_error(R),
+ report("could not write file '~s': ~s.", [File, R1]),
+ exit(error)
+ end.
+
+write_info_file(App, Packages, Modules, Dir) ->
+ Ts = [{packages, Packages},
+ {modules, Modules}],
+ Ts1 = if App =:= ?NO_APP -> Ts;
+ true -> [{application, App} | Ts]
+ end,
+ S = [io_lib:fwrite("~p.\n", [T]) || T <- Ts1],
+ write_file(S, Dir, ?INFO_FILE).
+
+%% @spec (Name::edoc:filename()) -> {ok, string()} | {error, Reason}
+%%
+%% @doc Reads text from the file named by `Name'.
+
+read_file(File) ->
+ case file:read_file(File) of
+ {ok, Bin} -> {ok, binary_to_list(Bin)};
+ {error, Reason} -> {error, Reason}
+ end.
+
+
+%% ---------------------------------------------------------------------
+%% Info files
+
+info_file_data(Ts) ->
+ App = proplists:get_value(application, Ts, ?NO_APP),
+ Ps = proplists:append_values(packages, Ts),
+ Ms = proplists:append_values(modules, Ts),
+ {App, Ps, Ms}.
+
+%% Local file access - don't complain if file does not exist.
+
+read_info_file(Dir) ->
+ File = filename:join(Dir, ?INFO_FILE),
+ case filelib:is_file(File) of
+ true ->
+ case read_file(File) of
+ {ok, Text} ->
+ parse_info_file(Text, File);
+ {error, R} ->
+ R1 = file:format_error(R),
+ warning("could not read '~s': ~s.", [File, R1]),
+ {?NO_APP, [], []}
+ end;
+ false ->
+ {?NO_APP, [], []}
+ end.
+
+%% URI access
+
+uri_get_info_file(Base) ->
+ URI = join_uri(Base, ?INFO_FILE),
+ case uri_get(URI) of
+ {ok, Text} ->
+ parse_info_file(Text, URI);
+ {error, Msg} ->
+ warning("could not read '~s': ~s.", [URI, Msg]),
+ {?NO_APP, [], []}
+ end.
+
+parse_info_file(Text, Name) ->
+ case parse_terms(Text) of
+ {ok, Vs} ->
+ info_file_data(Vs);
+ {error, eof} ->
+ warning("unexpected end of file in '~s'.", [Name]),
+ {?NO_APP, [], []};
+ {error, {_Line,Module,R}} ->
+ warning("~s: ~s.", [Module:format_error(R), Name]),
+ {?NO_APP, [], []}
+ end.
+
+parse_terms(Text) ->
+ case erl_scan:string(Text) of
+ {ok, Ts, _Line} ->
+ parse_terms_1(Ts, [], []);
+ {error, R, _Line} ->
+ {error, R}
+ end.
+
+parse_terms_1([T={dot, _L} | Ts], As, Vs) ->
+ case erl_parse:parse_term(lists:reverse([T | As])) of
+ {ok, V} ->
+ parse_terms_1(Ts, [], [V | Vs]);
+ {error, R} ->
+ {error, R}
+ end;
+parse_terms_1([T | Ts], As, Vs) ->
+ parse_terms_1(Ts, [T | As], Vs);
+parse_terms_1([], [], Vs) ->
+ {ok, lists:reverse(Vs)};
+parse_terms_1([], _As, _Vs) ->
+ {error, eof}.
+
+
+%% ---------------------------------------------------------------------
+%% Source files and packages
+
+find_sources(Path, Opts) ->
+ find_sources(Path, "", Opts).
+
+%% @doc See {@link edoc:run/3} for a description of the options
+%% `subpackages', `source_suffix' and `exclude_packages'.
+
+%% NEW-OPTIONS: subpackages, source_suffix, exclude_packages
+%% DEFER-OPTIONS: edoc:run/3
+
+find_sources(Path, Pkg, Opts) ->
+ Rec = proplists:get_bool(subpackages, Opts),
+ Ext = proplists:get_value(source_suffix, Opts, ?DEFAULT_SOURCE_SUFFIX),
+ find_sources(Path, Pkg, Rec, Ext, Opts).
+
+find_sources(Path, Pkg, Rec, Ext, Opts) ->
+ Skip = proplists:get_value(exclude_packages, Opts, []),
+ lists:flatten(find_sources_1(Path, to_atom(Pkg), Rec, Ext, Skip)).
+
+find_sources_1([P | Ps], Pkg, Rec, Ext, Skip) ->
+ Dir = filename:join(P, filename:join(packages:split(Pkg))),
+ Fs1 = find_sources_1(Ps, Pkg, Rec, Ext, Skip),
+ case filelib:is_dir(Dir) of
+ true ->
+ [find_sources_2(Dir, Pkg, Rec, Ext, Skip) | Fs1];
+ false ->
+ Fs1
+ end;
+find_sources_1([], _Pkg, _Rec, _Ext, _Skip) ->
+ [].
+
+find_sources_2(Dir, Pkg, Rec, Ext, Skip) ->
+ case lists:member(Pkg, Skip) of
+ false ->
+ Es = list_dir(Dir, false), % just warn if listing fails
+ Es1 = [{Pkg, E, Dir} || E <- Es, is_source_file(E, Ext)],
+ case Rec of
+ true ->
+ [find_sources_3(Es, Dir, Pkg, Rec, Ext, Skip) | Es1];
+ false ->
+ Es1
+ end;
+ true ->
+ []
+ end.
+
+find_sources_3(Es, Dir, Pkg, Rec, Ext, Skip) ->
+ [find_sources_2(filename:join(Dir, E),
+ to_atom(packages:concat(Pkg, E)), Rec, Ext, Skip)
+ || E <- Es, is_package_dir(E, Dir)].
+
+is_source_file(Name, Ext) ->
+ (filename:extension(Name) == Ext)
+ andalso is_name(filename:rootname(Name, Ext)).
+
+is_package_dir(Name, Dir) ->
+ is_name(filename:rootname(filename:basename(Name)))
+ andalso filelib:is_dir(filename:join(Dir, Name)).
+
+find_file([P | Ps], Pkg, Name) ->
+ Dir = filename:join(P, filename:join(packages:split(Pkg))),
+ File = filename:join(Dir, Name),
+ case filelib:is_file(File) of
+ true ->
+ File;
+ false ->
+ find_file(Ps, Pkg, Name)
+ end;
+find_file([], _Pkg, _Name) ->
+ "".
+
+find_doc_dirs() ->
+ find_doc_dirs(code:get_path()).
+
+find_doc_dirs([P0 | Ps]) ->
+ P = filename:absname(P0),
+ P1 = case filename:basename(P) of
+ ?EBIN_DIR ->
+ filename:dirname(P);
+ _ ->
+ P
+ end,
+ Dir = try_subdir(P1, ?EDOC_DIR),
+ File = filename:join(Dir, ?INFO_FILE),
+ case filelib:is_file(File) of
+ true ->
+ [Dir | find_doc_dirs(Ps)];
+ false ->
+ find_doc_dirs(Ps)
+ end;
+find_doc_dirs([]) ->
+ [].
+
+%% All names with "internal linkage" are mapped to the empty string, so
+%% that relative references will be created. For apps, the empty string
+%% implies that we use the default app-path.
+
+%% NEW-OPTIONS: doc_path
+%% DEFER-OPTIONS: get_doc_env/4
+
+get_doc_links(App, Packages, Modules, Opts) ->
+ Path = proplists:append_values(doc_path, Opts) ++ find_doc_dirs(),
+ Ds = [{P, uri_get_info_file(P)} || P <- Path],
+ Ds1 = [{"", {App, Packages, Modules}} | Ds],
+ D = dict:new(),
+ make_links(Ds1, D, D, D).
+
+make_links([{Dir, {App, Ps, Ms}} | Ds], A, P, M) ->
+ A1 = if App == ?NO_APP -> A;
+ true -> add_new(App, Dir, A)
+ end,
+ F = fun (K, D) -> add_new(K, Dir, D) end,
+ P1 = lists:foldl(F, P, Ps),
+ M1 = lists:foldl(F, M, Ms),
+ make_links(Ds, A1, P1, M1);
+make_links([], A, P, M) ->
+ F = fun (D) ->
+ fun (K) ->
+ case dict:find(K, D) of
+ {ok, V} -> V;
+ error -> ""
+ end
+ end
+ end,
+ {F(A), F(P), F(M)}.
+
+add_new(K, V, D) ->
+ case dict:is_key(K, D) of
+ true ->
+ D;
+ false ->
+ dict:store(K, V, D)
+ end.
+
+%% @spec (Options::proplist()) -> edoc_env()
+%% @equiv get_doc_env([], [], [], Opts)
+
+get_doc_env(Opts) ->
+ get_doc_env([], [], [], Opts).
+
+%% @spec (App, Packages, Modules, Options::proplist()) -> edoc_env()
+%% App = [] | atom()
+%% Packages = [atom()]
+%% Modules = [atom()]
+%% proplist() = [term()]
+%%
+%% @type edoc_env(). Environment information needed by EDoc for
+%% generating references. The data representation is not documented.
+%%
+%% @doc Creates an environment data structure used by parts of EDoc for
+%% generating references, etc. See {@link edoc:run/3} for a description
+%% of the options `file_suffix', `app_default' and `doc_path'.
+%%
+%% @see edoc_extract:source/4
+%% @see edoc:get_doc/3
+
+%% NEW-OPTIONS: file_suffix, app_default
+%% INHERIT-OPTIONS: get_doc_links/4
+%% DEFER-OPTIONS: edoc:run/3
+
+get_doc_env(App, Packages, Modules, Opts) ->
+ Suffix = proplists:get_value(file_suffix, Opts,
+ ?DEFAULT_FILE_SUFFIX),
+ AppDefault = proplists:get_value(app_default, Opts, ?APP_DEFAULT),
+ Includes = proplists:append_values(includes, Opts),
+
+ {A, P, M} = get_doc_links(App, Packages, Modules, Opts),
+ #env{file_suffix = Suffix,
+ package_summary = ?PACKAGE_SUMMARY ++ Suffix,
+ apps = A,
+ packages = P,
+ modules = M,
+ app_default = AppDefault,
+ includes = Includes
+ }.
+
+%% ---------------------------------------------------------------------
+%% Plug-in modules
+
+%% @doc See {@link edoc:run/3} for a description of the `doclet' option.
+
+%% NEW-OPTIONS: doclet
+%% DEFER-OPTIONS: edoc:run/3
+
+run_doclet(Fun, Opts) ->
+ run_plugin(doclet, ?DEFAULT_DOCLET, Fun, Opts).
+
+%% @doc See {@link edoc:layout/2} for a description of the `layout'
+%% option.
+
+%% NEW-OPTIONS: layout
+%% DEFER-OPTIONS: edoc:layout/2
+
+run_layout(Fun, Opts) ->
+ run_plugin(layout, ?DEFAULT_LAYOUT, Fun, Opts).
+
+run_plugin(Name, Default, Fun, Opts) ->
+ run_plugin(Name, Name, Default, Fun, Opts).
+
+run_plugin(Name, Key, Default, Fun, Opts) when is_atom(Name) ->
+ Module = get_plugin(Key, Default, Opts),
+ case catch {ok, Fun(Module)} of
+ {ok, Value} ->
+ Value;
+ R ->
+ report("error in ~s '~w': ~W.", [Name, Module, R, 20]),
+ exit(error)
+ end.
+
+get_plugin(Key, Default, Opts) ->
+ case proplists:get_value(Key, Opts, Default) of
+ M when is_atom(M) ->
+ M;
+ Other ->
+ report("bad value for option '~w': ~P.", [Key, Other, 10]),
+ exit(error)
+ end.
+
+
+%% ---------------------------------------------------------------------
+%% Error handling
+
+throw_error({missing, C}, L) ->
+ throw_error({"missing '~c'.", [C]}, L);
+throw_error(eof, L) ->
+ throw({error,L,"unexpected end of expression."});
+throw_error({L, M, D}, _L) ->
+ throw({error,L,{format_error,M,D}});
+throw_error(D, L) ->
+ throw({error, L, D}).
diff --git a/lib/edoc/src/edoc_macros.erl b/lib/edoc/src/edoc_macros.erl
new file mode 100644
index 0000000000..2874e2940c
--- /dev/null
+++ b/lib/edoc/src/edoc_macros.erl
@@ -0,0 +1,327 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright 2001-2005 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc EDoc macro expansion
+
+-module(edoc_macros).
+
+-export([expand_tags/3, std_macros/1, check_defs/1]).
+
+-import(edoc_report, [report/2, error/3, warning/4]).
+
+-include("edoc.hrl").
+-include("edoc_types.hrl").
+
+-define(DEFAULT_XML_EXPORT, xmerl_html).
+
+
+std_macros(Env) ->
+ (if Env#env.module =:= [] -> [];
+ true -> [{module, atom_to_list(Env#env.module)}]
+ end
+ ++
+ if Env#env.package =:= [] -> [];
+ true -> [{package, atom_to_list(Env#env.package)}]
+ end
+ ++
+ [{date, fun date_macro/3},
+ {docRoot, Env#env.root},
+ {link, fun link_macro/3},
+ {section, fun section_macro/3},
+ {time, fun time_macro/3},
+ {type, fun type_macro/3},
+ {version, fun version_macro/3}]).
+
+
+%% Check well-formedness of user-specified list of macro definitions.
+
+check_defs([{K, D} | Ds]) when is_atom(K), is_list(D) ->
+ check_defs(Ds);
+check_defs([X | _Ds]) ->
+ report("bad macro definition: ~P.", [X, 10]),
+ exit(error);
+check_defs([]) ->
+ ok.
+
+%% Code for special macros should throw {error, Line, Reason} for error
+%% reporting, where Reason and Line are passed to edoc_report:error(...)
+%% together with the file name etc. The expanded text must be flat!
+
+date_macro(_S, _Line, _Env) ->
+ edoc_lib:datestr(date()).
+
+time_macro(_S, _Line, _Env) ->
+ edoc_lib:timestr(time()).
+
+version_macro(S, Line, Env) ->
+ date_macro(S, Line, Env)
+ ++ " " ++ time_macro(S, Line, Env).
+
+link_macro(S, Line, Env) ->
+ {S1, S2} = edoc_lib:split_at_stop(S),
+ Ref = edoc_parser:parse_ref(S1, Line),
+ URI = edoc_refs:get_uri(Ref, Env),
+ Txt = if S2 =:= [] -> "<code>" ++ S1 ++ "</code>";
+ true -> S2
+ end,
+ Target = case edoc_refs:is_top(Ref, Env) of
+ true -> " target=\"_top\""; % note the initial space
+ false -> ""
+ end,
+ lists:flatten(io_lib:fwrite("<a href=\"~s\"~s>~s</a>",
+ [URI, Target, Txt])).
+
+section_macro(S, _Line, _Env) ->
+ S1 = lists:reverse(edoc_lib:strip_space(
+ lists:reverse(edoc_lib:strip_space(S)))),
+ lists:flatten(io_lib:format("<a href=\"#~s\">~s</a>",
+ [edoc_lib:to_label(S1), S1])).
+
+type_macro(S, Line, Env) ->
+ S1 = "t()=" ++ S,
+ Def = edoc_parser:parse_typedef(S1, Line),
+ {#t_typedef{type = T}, _} = Def,
+ Txt = edoc_layout:type(edoc_data:type(T, Env)),
+ lists:flatten(io_lib:fwrite("<code>~s</code>", [Txt])).
+
+
+%% Expand inline macros in tag content.
+
+expand_tags(Ts, Env, Where) ->
+ Defs = dict:from_list(lists:reverse(Env#env.macros)),
+ expand_tags(Ts, Defs, Env, Where).
+
+expand_tags([#tag{data = Cs, line = L} = T | Ts], Defs, Env, Where) ->
+ [T#tag{data = expand_tag(Cs, L, Defs, Env, Where)}
+ | expand_tags(Ts, Defs, Env, Where)];
+expand_tags([T | Ts], Defs, Env, Where) ->
+ [T | expand_tags(Ts, Defs, Env, Where)];
+expand_tags([], _, _, _) ->
+ [].
+
+expand_tag(Cs, L, Defs, Env, Where) ->
+ case catch {ok, expand_text(Cs, L, Defs, Env, Where)} of
+ {ok, Cs1} ->
+ lists:reverse(Cs1);
+ {'EXIT', R} ->
+ exit(R);
+ {error, L1, Error} ->
+ error(L1, Where, Error),
+ exit(error);
+ Other ->
+ throw(Other)
+ end.
+
+%% Expand macros in arbitrary lines of text.
+%% The result is in reverse order.
+
+-record(state, {where, env, seen}).
+
+expand_text(Cs, L, Defs, Env, Where) ->
+ St = #state{where = Where,
+ env = Env,
+ seen = sets:new()},
+ expand(Cs, L, Defs, St, []).
+
+%% Inline macro syntax: "{@name content}"
+%% where 'content' is optional, and separated from 'name' by one or
+%% more whitespace characters. The content is bound to the '{@?}'
+%% parameter variable, and the macro definition is expanded and
+%% substituted for the call. Recursion is detected and reported as an
+%% error, since there are (currently) no control flow operators.
+%% Escape sequences:
+%% "@{" -> "{"
+%% "@}" -> "}"
+%% "@@" -> "@"
+
+expand([$@, $@ | Cs], L, Defs, St, As) ->
+ expand(Cs, L, Defs, St, [$@ | As]);
+expand([$@, ${ | Cs], L, Defs, St, As) ->
+ expand(Cs, L, Defs, St, [${ | As]);
+expand([$@, $} | Cs], L, Defs, St, As) ->
+ expand(Cs, L, Defs, St, [$} | As]);
+expand([${, $@ | Cs], L, Defs, St, As) ->
+ expand_macro(Cs, L, Defs, St, As);
+expand([$\n = C | Cs], L, Defs, St, As) ->
+ expand(Cs, L + 1, Defs, St, [C | As]);
+expand([C | Cs], L, Defs, St, As) ->
+ expand(Cs, L, Defs, St, [C | As]);
+expand([], _, _, _, As) ->
+ As.
+
+expand_macro(Cs, L, Defs, St, As) ->
+ {M, Cs1, L1} = macro_name(Cs, L),
+ {Arg, Cs2, L2} = macro_content(Cs1, L1),
+ As1 = expand_macro_def(M, Arg, L, Defs, St, As),
+ expand(Cs2, L2, Defs, St, As1).
+
+%% The macro argument (the "content") is expanded in the environment of
+%% the call, and the result is bound to the '{@?}' parameter. The result
+%% of the macro expansion is then expanded again. This allows macro
+%% definitions to contain calls to other macros, avoids name capture of
+%% '{@?}', and makes it easier to write handler functions for special
+%% macros such as '{@link ...}', since the argument is already expanded.
+
+expand_macro_def(M, Arg, L, Defs, St, As) ->
+ Seen = St#state.seen,
+ case sets:is_element(M, Seen) of
+ true ->
+ throw_error(L, {"recursive macro expansion of {@~s}.",
+ [M]});
+ false ->
+ Arg1 = lists:reverse(expand(Arg, L, Defs, St, [])),
+ Defs1 = dict:store('?', Arg1, Defs),
+ St1 = St#state{seen = sets:add_element(M, Seen)},
+ case dict:find(M, Defs) of
+ {ok, Def} ->
+ Txt = if is_function(Def) ->
+ Def(Arg1, L, St1#state.env);
+ is_list(Def) ->
+ Def
+ end,
+ expand(Txt, L, Defs1, St1, As);
+ error ->
+ warning(L, St1#state.where,
+ "undefined macro {@~s}.", [M]),
+ "??"
+ end
+ end.
+
+%% The macro name ends at the first whitespace or '}' character. The
+%% content, if any, starts at the next non-whitespace character.
+
+%% See edoc_tags:scan_tag/is_name/1 for details on what is a valid
+%% name. In macro names we also allow '?' as the initial character.
+
+macro_name(Cs, L) ->
+ macro_name(Cs, [], L).
+
+macro_name([C | Cs], As, L) when C >= $a, C =< $z ->
+ macro_name_1(Cs, [C | As], L);
+macro_name([C | Cs], As, L) when C >= $A, C =< $Z ->
+ macro_name_1(Cs, [C | As], L);
+macro_name([C | Cs], As, L) when C >= $\300, C =< $\377,
+ C =/= $\327, C =/= $\367 ->
+ macro_name_1(Cs, [C | As], L);
+macro_name([$_ | Cs], As, L) ->
+ macro_name_1(Cs, [$_ | As], L);
+macro_name([$? | Cs], As, L) ->
+ macro_name_1(Cs, [$? | As], L);
+macro_name([$\s | _Cs], _As, L) ->
+ throw_error(L, macro_name);
+macro_name([$\t | _Cs], _As, L) ->
+ throw_error(L, macro_name);
+macro_name([$\n | _Cs], _As, L) ->
+ throw_error(L, macro_name);
+macro_name([C | _Cs], As, L) ->
+ throw_error(L, {macro_name, [C | As]});
+macro_name([], _As, L) ->
+ throw_error(L, macro_name).
+
+macro_name_1([C | Cs], As, L) when C >= $a, C =< $z ->
+ macro_name_1(Cs, [C | As], L);
+macro_name_1([C | Cs], As, L) when C >= $A, C =< $Z ->
+ macro_name_1(Cs, [C | As], L);
+macro_name_1([C | Cs], As, L) when C >= $0, C =< $9 ->
+ macro_name_1(Cs, [C | As], L);
+macro_name_1([C | Cs], As, L) when C >= $\300, C =< $\377,
+ C =/= $\327, C =/= $\367 ->
+ macro_name_1(Cs, [C | As], L);
+macro_name_1([$_ | Cs], As, L) ->
+ macro_name_1(Cs, [$_ | As], L);
+macro_name_1([$\s | Cs], As, L) ->
+ macro_name_2(Cs, As, L);
+macro_name_1([$\t | Cs], As, L) ->
+ macro_name_2(Cs, As, L);
+macro_name_1([$\n | Cs], As, L) ->
+ macro_name_2(Cs, As, L + 1);
+macro_name_1([$} | _] = Cs, As, L) ->
+ macro_name_3(Cs, As, L);
+macro_name_1([C | _Cs], As, L) ->
+ throw_error(L, {macro_name, [C | As]});
+macro_name_1([], _As, L) ->
+ throw_error(L, unterminated_macro).
+
+macro_name_2([$\s | Cs], As, L) ->
+ macro_name_2(Cs, As, L);
+macro_name_2([$\t | Cs], As, L) ->
+ macro_name_2(Cs, As, L);
+macro_name_2([$\n | Cs], As, L) ->
+ macro_name_2(Cs, As, L + 1);
+macro_name_2([_ | _] = Cs, As, L) ->
+ macro_name_3(Cs, As, L);
+macro_name_2([], _As, L) ->
+ throw_error(L, unterminated_macro).
+
+macro_name_3(Cs, As, L) ->
+ {list_to_atom(lists:reverse(As)), Cs, L}.
+
+
+%% The macro content ends at the first non-escaped '}' character that is
+%% not balanced by a corresponding non-escaped '{@' sequence.
+%% Escape sequences are those defined above.
+
+macro_content(Cs, L) ->
+ %% If there is an error, we report the start line, not the end line.
+ case catch {ok, macro_content(Cs, [], L, 0)} of
+ {ok, X} ->
+ X;
+ {'EXIT', R} ->
+ exit(R);
+ 'end' ->
+ throw_error(L, unterminated_macro);
+ Other ->
+ throw(Other)
+ end.
+
+%% @throws 'end'
+
+macro_content([$@, $@ | Cs], As, L, N) ->
+ macro_content(Cs, [$@, $@ | As], L, N); % escaped '@'
+macro_content([$@, $} | Cs], As, L, N) ->
+ macro_content(Cs, [$}, $@ | As], L, N); % escaped '}'
+macro_content([$@, ${ | Cs], As, L, N) ->
+ macro_content(Cs, [${, $@ | As], L, N); % escaped '{'
+macro_content([${, $@ | Cs], As, L, N) ->
+ macro_content(Cs, [$@, ${ | As], L, N + 1);
+macro_content([$} | Cs], As, L, 0) ->
+ {lists:reverse(As), Cs, L};
+macro_content([$} | Cs], As, L, N) ->
+ macro_content(Cs, [$} | As], L, N - 1);
+macro_content([$\n = C | Cs], As, L, N) ->
+ macro_content(Cs, [C | As], L + 1, N);
+macro_content([C | Cs], As, L, N) ->
+ macro_content(Cs, [C | As], L, N);
+macro_content([], _As, _L, _N) ->
+ throw('end').
+
+throw_error(L, unterminated_macro) ->
+ throw_error(L, {"unexpected end of macro.", []});
+throw_error(L, macro_name) ->
+ throw_error(L, {"missing macro name.", []});
+throw_error(L, {macro_name, S}) ->
+ throw_error(L, {"bad macro name: '@~s...'.", [lists:reverse(S)]});
+throw_error(L, D) ->
+ throw({error, L, D}).
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
new file mode 100644
index 0000000000..0eea8ae66f
--- /dev/null
+++ b/lib/edoc/src/edoc_parser.yrl
@@ -0,0 +1,423 @@
+%% ========================== -*-Erlang-*- =============================
+%% EDoc type specification grammar for the Yecc parser generator,
+%% adapted from Sven-Olof Nystr�m's type specification parser.
+%%
+%% Also contains entry points for parsing things like typedefs,
+%% references, and throws-declarations.
+%%
+%% Copyright (C) 2002-2005 Richard Carlsson
+%%
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% Author contact: [email protected]
+%%
+%% $Id$
+%%
+%% =====================================================================
+
+Nonterminals
+start spec func_type utype_list utype_tuple utypes utype ptypes ptype
+nutype function_name where_defs defs def typedef etype throws qname ref
+aref mref lref pref var_list vars fields field.
+
+Terminals
+atom float integer var string start_spec start_typedef start_throws
+start_ref
+
+'(' ')' ',' '.' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*'
+'#' 'where'.
+
+Rootsymbol start.
+
+start -> start_spec spec: '$2'.
+start -> start_throws throws: '$2'.
+start -> start_typedef typedef: '$2'.
+start -> start_ref ref: '$2'.
+
+%% Produced in reverse order.
+qname -> atom: [tok_val('$1')].
+qname -> qname '.' atom: [tok_val('$3') | '$1'].
+
+spec -> func_type where_defs:
+ #t_spec{type = '$1', defs = lists:reverse('$2')}.
+spec -> function_name func_type where_defs:
+ #t_spec{name = '$1', type = '$2', defs = lists:reverse('$3')}.
+
+where_defs -> 'where' defs: '$2'.
+where_defs -> defs: '$1'.
+
+function_name -> atom: #t_name{name = tok_val('$1')}.
+
+func_type -> utype_list '->' utype:
+ #t_fun{args = element(1, '$1'), range = '$3'}.
+
+
+%% Paired with line number, for later error reporting
+utype_list -> '(' ')' : {[], tok_line('$1')}.
+utype_list -> '(' utypes ')' : {lists:reverse('$2'), tok_line('$1')}.
+
+utype_tuple -> '{' '}' : [].
+utype_tuple -> '{' utypes '}' : lists:reverse('$2').
+
+%% Produced in reverse order.
+utypes -> utype : ['$1'].
+utypes -> utypes ',' utype : ['$3' | '$1'].
+
+utype -> nutype string: annotate('$1', tok_val('$2')).
+utype -> nutype: '$1'.
+
+nutype -> var '::' ptypes: annotate(union('$3'), tok_val('$1')).
+nutype -> ptypes: union('$1').
+
+%% Produced in reverse order.
+ptypes -> ptype : ['$1'].
+ptypes -> ptypes '+' ptype : ['$3' | '$1'].
+ptypes -> ptypes '|' ptype : ['$3' | '$1'].
+
+ptype -> var : #t_var{name = tok_val('$1')}.
+ptype -> atom : #t_atom{val = tok_val('$1')}.
+ptype -> integer: #t_integer{val = tok_val('$1')}.
+ptype -> float: #t_float{val = tok_val('$1')}.
+ptype -> utype_tuple : #t_tuple{types = '$1'}.
+ptype -> '[' ']' : #t_nil{}.
+ptype -> '[' utype ']' : #t_list{type = '$2'}.
+ptype -> utype_list:
+ if length(element(1, '$1')) == 1 ->
+ %% there must be exactly one utype in the list
+ hd(element(1, '$1'));
+ length(element(1, '$1')) == 0 ->
+ return_error(element(2, '$1'), "syntax error before: ')'");
+ true ->
+ return_error(element(2, '$1'), "syntax error before: ','")
+ end.
+ptype -> utype_list '->' ptype:
+ #t_fun{args = element(1, '$1'), range = '$3'}.
+ptype -> '#' atom '{' '}' :
+ #t_record{name = #t_atom{val = tok_val('$2')}}.
+ptype -> '#' atom '{' fields '}' :
+ #t_record{name = #t_atom{val = tok_val('$2')},
+ fields = lists:reverse('$4')}.
+ptype -> atom utype_list:
+ #t_type{name = #t_name{name = tok_val('$1')},
+ args = element(1, '$2')}.
+ptype -> qname ':' atom utype_list :
+ #t_type{name = #t_name{module = qname('$1'),
+ name = tok_val('$3')},
+ args = element(1, '$4')}.
+ptype -> '//' atom '/' qname ':' atom utype_list :
+ #t_type{name = #t_name{app = tok_val('$2'),
+ module = qname('$4'),
+ name = tok_val('$6')},
+ args = element(1, '$7')}.
+
+%% Produced in reverse order.
+fields -> field : ['$1'].
+fields -> fields ',' field : ['$3' | '$1'].
+
+field -> atom '=' utype :
+ #t_field{name = #t_atom{val = tok_val('$1')}, type = '$3'}.
+
+%% Produced in reverse order.
+defs -> '$empty' : [].
+defs -> defs def : ['$2' | '$1'].
+defs -> defs ',' def : ['$3' | '$1'].
+
+def -> var '=' utype:
+ #t_def{name = #t_var{name = tok_val('$1')},
+ type = '$3'}.
+def -> atom var_list '=' utype:
+ #t_def{name = #t_type{name = #t_name{name = tok_val('$1')},
+ args = '$2'},
+ type = '$4'}.
+
+var_list -> '(' ')' : [].
+var_list -> '(' vars ')' : lists:reverse('$2').
+
+%% Produced in reverse order.
+vars -> var : [#t_var{name = tok_val('$1')}].
+vars -> vars ',' var : [#t_var{name = tok_val('$3')} | '$1'].
+
+typedef -> atom var_list where_defs:
+ #t_typedef{name = #t_name{name = tok_val('$1')},
+ args = '$2',
+ defs = lists:reverse('$3')}.
+typedef -> atom var_list '=' utype where_defs:
+ #t_typedef{name = #t_name{name = tok_val('$1')},
+ args = '$2',
+ type = '$4',
+ defs = lists:reverse('$5')}.
+
+%% References
+
+ref -> aref: '$1'.
+ref -> mref: '$1'.
+ref -> lref: '$1'.
+ref -> pref: '$1'.
+
+aref -> '//' atom:
+ edoc_refs:app(tok_val('$2')).
+aref -> '//' atom '/' mref:
+ edoc_refs:app(tok_val('$2'), '$4').
+aref -> '//' atom '/' pref:
+ edoc_refs:app(tok_val('$2'), '$4').
+
+mref -> qname ':' atom '/' integer:
+ edoc_refs:function(qname('$1'), tok_val('$3'), tok_val('$5')).
+mref -> qname ':' atom '(' ')':
+ edoc_refs:type(qname('$1'), tok_val('$3')).
+mref -> qname:
+ edoc_refs:module(qname('$1')).
+
+pref -> qname '.' '*':
+ edoc_refs:package(qname('$1')).
+
+lref -> atom '/' integer:
+ edoc_refs:function(tok_val('$1'), tok_val('$3')).
+lref -> atom '(' ')':
+ edoc_refs:type(tok_val('$1')).
+
+%% Exception declarations
+
+etype -> utype: '$1'.
+
+throws -> etype where_defs:
+ #t_throws{type = '$1',
+ defs = lists:reverse('$2')}.
+
+%% (commented out for now)
+%% Header
+%% "%% ========================== -*-Erlang-*- ============================="
+%% "%% EDoc function specification parser, generated from the file"
+%% "%% \"edoc_parser.yrl\" by the Yecc parser generator."
+%% "%%"
+%% "%% Copyright (C) 2002-2005 Richard Carlsson"
+%% "%%"
+%% "%% This library is free software; you can redistribute it and/or modify"
+%% "%% it under the terms of the GNU Lesser General Public License as"
+%% "%% published by the Free Software Foundation; either version 2 of the"
+%% "%% License, or (at your option) any later version."
+%% "%%"
+%% "%% This library is distributed in the hope that it will be useful, but"
+%% "%% WITHOUT ANY WARRANTY; without even the implied warranty of"
+%% "%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU"
+%% "%% Lesser General Public License for more details."
+%% "%%"
+%% "%% You should have received a copy of the GNU Lesser General Public"
+%% "%% License along with this library; if not, write to the Free Software"
+%% "%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307"
+%% "%% USA"
+%% "%%"
+%% "%% @private"
+%% "%% @author Richard Carlsson <[email protected]>"
+%% "%% ===================================================================="
+%% .
+
+Erlang code.
+
+%% ========================== -*-Erlang-*- =============================
+%% EDoc function specification parser, generated from the file
+%% "edoc_parser.yrl" by the Yecc parser generator.
+%%
+%% Copyright (C) 2002-2005 Richard Carlsson
+%%
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%% ====================================================================
+
+-export([parse_spec/2, parse_typedef/2, parse_throws/2, parse_ref/2,
+ parse_see/2, parse_param/2]).
+
+-include("edoc_types.hrl").
+
+%% Multiple entry point hack:
+
+start_spec(Ts, L) -> run_parser(Ts, L, start_spec).
+
+start_typedef(Ts, L) -> run_parser(Ts, L, start_typedef).
+
+start_throws(Ts, L) -> run_parser(Ts, L, start_throws).
+
+start_ref(Ts, L) -> run_parser(Ts, L, start_ref).
+
+%% Error reporting fix
+
+run_parser(Ts, L, Start) ->
+ case parse([{Start,L} | Ts]) of
+ {error, {999999,?MODULE,_}} ->
+ What = case Start of
+ start_spec -> "specification";
+ start_typedef -> "type definition";
+ start_throws -> "exception declaration";
+ start_ref -> "reference"
+ end,
+ {error, {L,?MODULE,["unexpected end of ", What]}};
+ Other -> Other
+ end.
+
+%% Utility functions:
+
+tok_val(T) -> element(3, T).
+
+tok_line(T) -> element(2, T).
+
+qname([A]) ->
+ A; % avoid unnecessary call to packages:concat/1.
+qname(List) ->
+ list_to_atom(packages:concat(lists:reverse(List))).
+
+union(Ts) ->
+ case Ts of
+ [T] -> T;
+ _ -> #t_union{types = lists:reverse(Ts)}
+ end.
+
+annotate(T, A) -> ?add_t_ann(T, A).
+
+%% ---------------------------------------------------------------------
+
+%% @doc EDoc type specification parsing. Parses the content of
+%% <a href="overview-summary.html#ftag-spec">`@spec'</a> declarations.
+
+parse_spec(S, L) ->
+ case edoc_scanner:string(S, L) of
+ {ok, Ts, _} ->
+ case start_spec(Ts, L) of
+ {ok, Spec} ->
+ Spec;
+ {error, E} ->
+ throw_error(E, L)
+ end;
+ {error, E, _} ->
+ throw_error(E, L)
+ end.
+
+%% ---------------------------------------------------------------------
+
+%% @doc EDoc type definition parsing. Parses the content of
+%% <a href="overview-summary.html#gtag-type">`@type'</a> declarations.
+
+parse_typedef(S, L) ->
+ {S1, S2} = edoc_lib:split_at_stop(S),
+ N = edoc_lib:count($\n, S1),
+ L1 = L + N,
+ Text = edoc_lib:strip_space(S2),
+ {parse_typedef_1(S1, L), edoc_wiki:parse_xml(Text, L1)}.
+
+parse_typedef_1(S, L) ->
+ case edoc_scanner:string(S, L) of
+ {ok, Ts, _} ->
+ case start_typedef(Ts, L) of
+ {ok, T} ->
+ T;
+ {error, E} ->
+ throw_error({parse_typedef, E}, L)
+ end;
+ {error, E, _} ->
+ throw_error({parse_typedef, E}, L)
+ end.
+
+%% ---------------------------------------------------------------------
+
+%% @doc Parses a <a
+%% href="overview-summary.html#References">reference</a> to a module,
+%% package, function, type, or application
+
+parse_ref(S, L) ->
+ case edoc_scanner:string(S, L) of
+ {ok, Ts, _} ->
+ case start_ref(Ts, L) of
+ {ok, T} ->
+ T;
+ {error, E} ->
+ throw_error({parse_ref, E}, L)
+ end;
+ {error, E, _} ->
+ throw_error({parse_ref, E}, L)
+ end.
+
+%% ---------------------------------------------------------------------
+
+%% @doc Parses the content of
+%% <a href="overview-summary.html#ftag-see">`@see'</a> references.
+parse_see(S, L) ->
+ {S1, S2} = edoc_lib:split_at_stop(S),
+ N = edoc_lib:count($\n, S1),
+ L1 = L + N,
+ Text = edoc_lib:strip_space(S2),
+ {parse_ref(S1, L), edoc_wiki:parse_xml(Text, L1)}.
+
+%% ---------------------------------------------------------------------
+
+%% @doc Parses the content of
+%% <a href="overview-summary.html#ftag-param">`@param'</a> tags.
+parse_param(S, L) ->
+ {S1, S2} = edoc_lib:split_at_space(edoc_lib:strip_space(S)),
+ case edoc_lib:strip_space(S1) of
+ "" -> throw_error(parse_param, L);
+ Name ->
+ Text = edoc_lib:strip_space(S2),
+ {list_to_atom(Name), edoc_wiki:parse_xml(Text, L)}
+ end.
+
+%% ---------------------------------------------------------------------
+
+%% @doc EDoc exception specification parsing. Parses the content of
+%% <a href="overview-summary.html#ftag-throws">`@throws'</a> declarations.
+
+parse_throws(S, L) ->
+ case edoc_scanner:string(S, L) of
+ {ok, Ts, _} ->
+ case start_throws(Ts, L) of
+ {ok, Spec} ->
+ Spec;
+ {error, E} ->
+ throw_error({parse_throws, E}, L)
+ end;
+ {error, E, _} ->
+ throw_error({parse_throws, E}, L)
+ end.
+
+%% ---------------------------------------------------------------------
+
+throw_error({L, M, D}, _L0) ->
+ throw({error,L,{format_error,M,D}});
+throw_error({parse_spec, E}, L) ->
+ throw_error({"specification", E}, L);
+throw_error({parse_typedef, E}, L) ->
+ throw_error({"type definition", E}, L);
+throw_error({parse_ref, E}, L) ->
+ throw_error({"reference", E}, L);
+throw_error({parse_throws, E}, L) ->
+ throw_error({"throws-declaration", E}, L);
+throw_error(parse_param, L) ->
+ throw({error, L, "missing parameter name"});
+throw_error({Where, E}, L) when is_list(Where) ->
+ throw({error,L,{"unknown error parsing ~s: ~P.",[Where,E,15]}});
+throw_error(E, L) ->
+ %% Just in case.
+ throw({error,L,{"unknown parse error: ~P.",[E,15]}}).
diff --git a/lib/edoc/src/edoc_refs.erl b/lib/edoc/src/edoc_refs.erl
new file mode 100644
index 0000000000..c2146bbe02
--- /dev/null
+++ b/lib/edoc/src/edoc_refs.erl
@@ -0,0 +1,217 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright 2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @see edoc_parse_ref
+%% @end
+%% =====================================================================
+
+%% @doc Representation and handling of EDoc object references. See
+%% {@link edoc_parse_ref} for more details.
+
+-module(edoc_refs).
+
+-export([app/1, app/2, package/1, module/1, module/2, module/3,
+ function/2, function/3, function/4, type/1, type/2, type/3,
+ to_string/1, to_label/1, get_uri/2, is_top/2,
+ relative_module_path/2, relative_package_path/2]).
+
+-import(edoc_lib, [join_uri/2, escape_uri/1]).
+
+-include("edoc.hrl").
+
+-define(INDEX_FILE, "index.html").
+
+
+%% Creating references:
+
+app(App) ->
+ {app, App}.
+
+app(App, Ref) ->
+ {app, App, Ref}.
+
+module(M) ->
+ {module, M}.
+
+module(M, Ref) ->
+ {module, M, Ref}.
+
+module(App, M, Ref) ->
+ app(App, module(M, Ref)).
+
+package(P) ->
+ {package, P}.
+
+function(F, A) ->
+ {function, F, A}.
+
+function(M, F, A) ->
+ module(M, function(F, A)).
+
+function(App, M, F, A) ->
+ module(App, M, function(F, A)).
+
+type(T) ->
+ {type, T}.
+
+type(M, T) ->
+ module(M, type(T)).
+
+type(App, M, T) ->
+ module(App, M, type(T)).
+
+
+%% Creating a print string for a reference
+
+to_string({app, A}) ->
+ "//" ++ atom_to_list(A);
+to_string({app, A, Ref}) ->
+ "//" ++ atom_to_list(A) ++ "/" ++ to_string(Ref);
+to_string({module, M}) ->
+ atom_to_list(M) ;
+to_string({module, M, Ref}) ->
+ atom_to_list(M) ++ ":" ++ to_string(Ref);
+to_string({package, P}) ->
+ atom_to_list(P) ++ ".*";
+to_string({function, F, A}) ->
+ atom_to_list(F) ++ "/" ++ integer_to_list(A);
+to_string({type, T}) ->
+ atom_to_list(T) ++ "()".
+
+
+%% Creating URIs and anchors.
+
+to_label({function, F, A}) ->
+ escape_uri(atom_to_list(F)) ++ "-" ++ integer_to_list(A);
+to_label({type, T}) ->
+ "type-" ++ escape_uri(atom_to_list(T)).
+
+get_uri({app, App}, Env) ->
+ join_uri(app_ref(App, Env), ?INDEX_FILE);
+get_uri({app, App, Ref}, Env) ->
+ app_ref(App, Ref, Env);
+get_uri({module, M, Ref}, Env) ->
+ module_ref(M, Env) ++ "#" ++ to_label(Ref);
+get_uri({module, M}, Env) ->
+ module_ref(M, Env);
+get_uri({package, P}, Env) ->
+ package_ref(P, Env);
+get_uri(Ref, _Env) ->
+ "#" ++ to_label(Ref).
+
+abs_uri({module, M}, Env) ->
+ module_absref(M, Env);
+abs_uri({module, M, Ref}, Env) ->
+ module_absref(M, Env) ++ "#" ++ to_label(Ref);
+abs_uri({package, P}, Env) ->
+ package_absref(P, Env).
+
+module_ref(M, Env) ->
+ case (Env#env.modules)(M) of
+ "" ->
+ File = packages:last(M) ++ Env#env.file_suffix,
+ Path = relative_module_path(M, Env#env.package),
+ join_uri(Path, escape_uri(File));
+ Base ->
+ join_uri(Base, module_absref(M, Env))
+ end.
+
+module_absref(M, Env) ->
+ join_segments(packages:split(M))
+ ++ escape_uri(Env#env.file_suffix).
+
+package_ref(P, Env) ->
+ case (Env#env.packages)(P) of
+ "" ->
+ join_uri(relative_package_path(P, Env#env.package),
+ escape_uri(Env#env.package_summary));
+ Base ->
+ join_uri(Base, package_absref(P, Env))
+ end.
+
+package_absref(P, Env) ->
+ join_uri(join_segments(packages:split(P)),
+ escape_uri(Env#env.package_summary)).
+
+app_ref(A, Env) ->
+ case (Env#env.apps)(A) of
+ "" ->
+ join_uri(Env#env.app_default,
+ join_uri(escape_uri(atom_to_list(A)), ?EDOC_DIR));
+ Base ->
+ Base
+ end.
+
+app_ref(A, Ref, Env) ->
+ join_uri(app_ref(A, Env), abs_uri(Ref, Env)).
+
+is_top({app, _App}, _Env) ->
+ true;
+is_top(_Ref, _Env) ->
+ false.
+
+%% Each segment of a path must be separately escaped before joining.
+
+join_segments([S]) ->
+ escape_uri(S);
+join_segments([S | Ss]) ->
+ join_uri(escape_uri(S), join_segments(Ss)).
+
+%% 'From' is always the "current package" here:
+
+%% The empty string is returned if the To module has only one segment,
+%% implying a local reference.
+
+relative_module_path(To, From) ->
+ case first(packages:split(To)) of
+ [] -> "";
+ P -> relative_path(P, packages:split(From))
+ end.
+
+relative_package_path(To, From) ->
+ relative_path(packages:split(To), packages:split(From)).
+
+%% This takes two lists of path segments (From, To). Note that an empty
+%% string will be returned if the paths are the same. Empty leading
+%% segments are stripped from both paths.
+
+relative_path(Ts, ["" | Fs]) ->
+ relative_path(Ts, Fs);
+relative_path(["" | Ts], Fs) ->
+ relative_path(Ts, Fs);
+relative_path(Ts, Fs) ->
+ relative_path_1(Ts, Fs).
+
+relative_path_1([T | Ts], [F | Fs]) when F == T ->
+ relative_path_1(Ts, Fs);
+relative_path_1(Ts, Fs) ->
+ relative_path_2(Fs, Ts).
+
+relative_path_2([_F | Fs], Ts) ->
+ relative_path_2(Fs, [".." | Ts]);
+relative_path_2([], []) ->
+ "";
+relative_path_2([], Ts) ->
+ join_segments(Ts).
+
+first([H | T]) when T /= [] -> [H | first(T)];
+first(_) -> [].
diff --git a/lib/edoc/src/edoc_report.erl b/lib/edoc/src/edoc_report.erl
new file mode 100644
index 0000000000..b87c58dde3
--- /dev/null
+++ b/lib/edoc/src/edoc_report.erl
@@ -0,0 +1,96 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright 2001-2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc EDoc verbosity/error reporting.
+
+-module(edoc_report).
+
+-export([error/1,
+ error/2,
+ error/3,
+ report/2,
+ report/3,
+ report/4,
+ warning/1,
+ warning/2,
+ warning/3,
+ warning/4]).
+
+-include("edoc.hrl").
+
+
+error(What) ->
+ error([], What).
+
+error(Where, What) ->
+ error(0, Where, What).
+
+error(Line, Where, S) when is_list(S) ->
+ report(Line, Where, S, []);
+error(Line, Where, {S, D}) when is_list(S) ->
+ report(Line, Where, S, D);
+error(Line, Where, {format_error, M, D}) ->
+ report(Line, Where, M:format_error(D), []).
+
+warning(S) ->
+ warning(S, []).
+
+warning(S, Vs) ->
+ warning([], S, Vs).
+
+warning(Where, S, Vs) ->
+ warning(0, Where, S, Vs).
+
+warning(L, Where, S, Vs) ->
+ report(L, Where, "warning: " ++ S, Vs).
+
+report(S, Vs) ->
+ report([], S, Vs).
+
+report(Where, S, Vs) ->
+ report(0, Where, S, Vs).
+
+report(L, Where, S, Vs) ->
+ io:put_chars(where(Where)),
+ if is_integer(L), L > 0 ->
+ io:fwrite("at line ~w: ", [L]);
+ true ->
+ ok
+ end,
+ io:fwrite(S, Vs),
+ io:nl().
+
+where({File, module}) ->
+ io_lib:fwrite("~s, in module header: ", [File]);
+where({File, footer}) ->
+ io_lib:fwrite("~s, in module footer: ", [File]);
+where({File, header}) ->
+ io_lib:fwrite("~s, in header file: ", [File]);
+where({File, {F, A}}) ->
+ io_lib:fwrite("~s, function ~s/~w: ", [File, F, A]);
+where([]) ->
+ io_lib:fwrite("~s: ", [?APPLICATION]);
+where(File) when is_list(File) ->
+ File ++ ": ".
diff --git a/lib/edoc/src/edoc_run.erl b/lib/edoc/src/edoc_run.erl
new file mode 100644
index 0000000000..37025d6621
--- /dev/null
+++ b/lib/edoc/src/edoc_run.erl
@@ -0,0 +1,225 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @copyright 2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc Interface for calling EDoc from Erlang startup options.
+%%
+%% The following is an example of typical usage in a Makefile:
+%% ```docs:
+%% erl -noshell -run edoc_run application "'$(APP_NAME)'" \
+%% '"."' '[{def,{vsn,"$(VSN)"}}]'
+%% '''
+%% (note the single-quotes to avoid shell expansion, and the
+%% double-quotes enclosing the strings).
+%%
+%% <strong>New feature in version 0.6.9</strong>: It is no longer
+%% necessary to write `-s init stop' last on the command line in order
+%% to make the execution terminate. The termination (signalling success
+%% or failure to the operating system) is now built into these
+%% functions.
+
+-module(edoc_run).
+
+-export([file/1, application/1, packages/1, files/1, toc/1]).
+
+-import(edoc_report, [report/2, error/1]).
+
+
+%% @spec application([string()]) -> none()
+%%
+%% @doc Calls {@link edoc:application/3} with the corresponding
+%% arguments. The strings in the list are parsed as Erlang constant
+%% terms. The list can be either `[App]', `[App, Options]' or `[App,
+%% Dir, Options]'. In the first case {@link edoc:application/1} is
+%% called instead; in the second case, {@link edoc:application/2} is
+%% called.
+%%
+%% The function call never returns; instead, the emulator is
+%% automatically terminated when the call has completed, signalling
+%% success or failure to the operating system.
+
+application(Args) ->
+ F = fun () ->
+ case parse_args(Args) of
+ [App] -> edoc:application(App);
+ [App, Opts] -> edoc:application(App, Opts);
+ [App, Dir, Opts] -> edoc:application(App, Dir, Opts);
+ _ ->
+ invalid_args("edoc_run:application/1", Args)
+ end
+ end,
+ run(F).
+
+%% @spec files([string()]) -> none()
+%%
+%% @doc Calls {@link edoc:files/2} with the corresponding arguments. The
+%% strings in the list are parsed as Erlang constant terms. The list can
+%% be either `[Files]' or `[Files, Options]'. In the first case, {@link
+%% edoc:files/1} is called instead.
+%%
+%% The function call never returns; instead, the emulator is
+%% automatically terminated when the call has completed, signalling
+%% success or failure to the operating system.
+
+files(Args) ->
+ F = fun () ->
+ case parse_args(Args) of
+ [Files] -> edoc:files(Files);
+ [Files, Opts] -> edoc:files(Files, Opts);
+ _ ->
+ invalid_args("edoc_run:files/1", Args)
+ end
+ end,
+ run(F).
+
+%% @spec packages([string()]) -> none()
+%%
+%% @doc Calls {@link edoc:application/2} with the corresponding
+%% arguments. The strings in the list are parsed as Erlang constant
+%% terms. The list can be either `[Packages]' or `[Packages, Options]'.
+%% In the first case {@link edoc:application/1} is called instead.
+%%
+%% The function call never returns; instead, the emulator is
+%% automatically terminated when the call has completed, signalling
+%% success or failure to the operating system.
+
+packages(Args) ->
+ F = fun () ->
+ case parse_args(Args) of
+ [Packages] -> edoc:packages(Packages);
+ [Packages, Opts] -> edoc:packages(Packages, Opts);
+ _ ->
+ invalid_args("edoc_run:packages/1", Args)
+ end
+ end,
+ run(F).
+
+%% @hidden Not official yet
+toc(Args) ->
+ F = fun () ->
+ case parse_args(Args) of
+ [Dir, Paths] -> edoc:toc(Dir,Paths);
+ [Dir, Paths, Opts] -> edoc:toc(Dir,Paths,Opts);
+ _ ->
+ invalid_args("edoc_run:toc/1", Args)
+ end
+ end,
+ run(F).
+
+
+%% @spec file([string()]) -> none()
+%%
+%% @deprecated This is part of the old interface to EDoc and is mainly
+%% kept for backwards compatibility. The preferred way of generating
+%% documentation is through one of the functions {@link application/1},
+%% {@link packages/1} and {@link files/1}.
+%%
+%% @doc Calls {@link edoc:file/2} with the corresponding arguments. The
+%% strings in the list are parsed as Erlang constant terms. The list can
+%% be either `[File]' or `[File, Options]'. In the first case, an empty
+%% list of options is passed to {@link edoc:file/2}.
+%%
+%% The following is an example of typical usage in a Makefile:
+%% ```$(DOCDIR)/%.html:%.erl
+%% erl -noshell -run edoc_run file '"$<"' '[{dir,"$(DOCDIR)"}]' \
+%% -s init stop'''
+%%
+%% The function call never returns; instead, the emulator is
+%% automatically terminated when the call has completed, signalling
+%% success or failure to the operating system.
+
+file(Args) ->
+ F = fun () ->
+ case parse_args(Args) of
+ [File] -> edoc:file(File, []);
+ [File, Opts] -> edoc:file(File, Opts);
+ _ ->
+ invalid_args("edoc_run:file/1", Args)
+ end
+ end,
+ run(F).
+
+-spec invalid_args(string(), list()) -> no_return().
+
+invalid_args(Where, Args) ->
+ report("invalid arguments to ~s: ~w.", [Where, Args]),
+ shutdown_error().
+
+run(F) ->
+ wait_init(),
+ case catch {ok, F()} of
+ {ok, _} ->
+ shutdown_ok();
+ {'EXIT', E} ->
+ report("edoc terminated abnormally: ~P.", [E, 10]),
+ shutdown_error();
+ Thrown ->
+ report("internal error: throw without catch in edoc: ~P.",
+ [Thrown, 15]),
+ shutdown_error()
+ end.
+
+wait_init() ->
+ case erlang:whereis(code_server) of
+ undefined ->
+ erlang:yield(),
+ wait_init();
+ _ ->
+ ok
+ end.
+
+%% When and if a function init:stop/1 becomes generally available, we
+%% can use that instead of delay-and-pray when there is an error.
+
+shutdown_ok() ->
+ %% shut down emulator nicely, signalling "normal termination"
+ init:stop().
+
+shutdown_error() ->
+ %% delay 1 second to allow I/O to finish
+ receive after 1000 -> ok end,
+ %% stop emulator the hard way with a nonzero exit value
+ halt(1).
+
+parse_args([A | As]) when is_atom(A) ->
+ [parse_arg(atom_to_list(A)) | parse_args(As)];
+parse_args([A | As]) ->
+ [parse_arg(A) | parse_args(As)];
+parse_args([]) ->
+ [].
+
+parse_arg(A) ->
+ case catch {ok, edoc_lib:parse_expr(A, 1)} of
+ {ok, Expr} ->
+ case catch erl_parse:normalise(Expr) of
+ {'EXIT', _} ->
+ report("bad argument: '~s':", [A]),
+ exit(error);
+ Term ->
+ Term
+ end;
+ {error, _, D} ->
+ report("error parsing argument '~s'", [A]),
+ error(D),
+ exit(error)
+ end.
diff --git a/lib/edoc/src/edoc_scanner.erl b/lib/edoc/src/edoc_scanner.erl
new file mode 100644
index 0000000000..d3dff64682
--- /dev/null
+++ b/lib/edoc/src/edoc_scanner.erl
@@ -0,0 +1,358 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings
+%% AB. Portions created by Ericsson are Copyright 1999, Ericsson
+%% Utvecklings AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright Richard Carlsson 2001-2003. Portions created by Ericsson
+%% are Copyright 1999, Ericsson Utvecklings AB. All Rights Reserved.
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+
+%% @doc Tokeniser for EDoc. Based on the Erlang standard library module
+%% {@link //stdlib/erl_scan}.
+
+-module(edoc_scanner).
+
+%% NOTE: the interface to this module is ancient and should be updated.
+%% Please do not regard these exported functions as stable. Their
+%% behaviour is described in the documentation of the module `erl_scan'.
+%%
+%% Since there are no `full stop' tokens in EDoc specifications, the
+%% `tokens' function *always* returns `{more, Continuation}' unless an
+%% error occurs.
+
+-export([string/1,string/2,format_error/1]).
+
+-import(lists, [reverse/1]).
+
+string(Cs) -> string(Cs, 1).
+
+string(Cs, StartPos) ->
+ case scan(Cs, StartPos) of
+ {ok,Toks} -> {ok,Toks,StartPos};
+ {error,E} -> {error,E,StartPos}
+ end.
+
+%% format_error(Error)
+%% Return a string describing the error.
+
+format_error({string,Quote,Head}) ->
+ ["unterminated string starting with " ++ io_lib:write_string(Head,Quote)];
+format_error({illegal,Type}) -> io_lib:fwrite("illegal ~w", [Type]);
+format_error(char) -> "unterminated character";
+format_error(scan) -> "premature end";
+format_error({base,Base}) -> io_lib:fwrite("illegal base '~w'", [Base]);
+format_error(float) -> "bad float";
+
+format_error(Other) -> io_lib:write(Other).
+
+%% Reserved words, not atoms:
+reserved('where') -> true;
+reserved(_) -> false.
+
+%% scan(CharList, StartPos)
+%% This takes a list of characters and tries to tokenise them.
+%%
+%% The token list is built in reverse order (in a stack) to save appending
+%% and then reversed when all the tokens have been collected. Most tokens
+%% are built in the same way.
+%%
+%% Returns:
+%% {ok,[Tok]}
+%% {error,{ErrorPos,edoc_scanner,What}}
+
+scan(Cs, Pos) ->
+ scan1(Cs, [], Pos).
+
+%% scan1(Characters, TokenStack, Position)
+%% Scan a list of characters into tokens.
+
+scan1([$\n|Cs], Toks, Pos) -> % Newline
+ scan1(Cs, Toks, Pos+1);
+scan1([C|Cs], Toks, Pos) when C >= 0, C =< $ -> % Skip blanks
+ scan1(Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $a, C =< $z -> % Unquoted atom
+ scan_atom(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $0, C =< $9 -> % Numbers
+ scan_number(C, Cs, Toks, Pos);
+scan1([$-,C| Cs], Toks, Pos) when C >= $0, C =< $9 -> % Signed numbers
+ scan_signed_number($-, C, Cs, Toks, Pos);
+scan1([$+,C| Cs], Toks, Pos) when C >= $0, C =< $9 -> % Signed numbers
+ scan_signed_number($+, C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $A, C =< $Z -> % Variables
+ scan_variable(C, Cs, Toks, Pos);
+scan1([$_|Cs], Toks, Pos) -> % Variables
+ scan_variable($_, Cs, Toks, Pos);
+scan1([$$|Cs], Toks, Pos) -> % Character constant
+ case scan_char_const(Cs, Toks, Pos) of
+ {ok, Result} ->
+ {ok, Result};
+ {error, truncated_char} ->
+ scan_error(char, Pos);
+ {error, illegal_character} ->
+ scan_error({illegal, char}, Pos)
+ end;
+scan1([$'|Cs0], Toks, Pos) -> % Quoted atom
+ case scan_string(Cs0, $', Pos) of
+ {S,Cs1,Pos1} ->
+ case catch list_to_atom(S) of
+ A when is_atom(A) ->
+ scan1(Cs1, [{atom,Pos,A}|Toks], Pos1);
+ _Error -> scan_error({illegal,atom}, Pos)
+ end;
+ {error, premature_end} ->
+ scan_error({string,$',Cs0}, Pos);
+ {error, truncated_char} ->
+ scan_error(char, Pos);
+ {error, illegal_character} ->
+ scan_error({illegal, atom}, Pos)
+ end;
+scan1([$"|Cs0], Toks, Pos) -> % String
+ case scan_string(Cs0, $", Pos) of
+ {S,Cs1,Pos1} ->
+ case Toks of
+ [{string, Pos0, S0} | Toks1] ->
+ scan1(Cs1, [{string, Pos0, S0 ++ S} | Toks1],
+ Pos1);
+ _ ->
+ scan1(Cs1, [{string,Pos,S}|Toks], Pos1)
+ end;
+ {error, premature_end} ->
+ scan_error({string,$",Cs0}, Pos);
+ {error, truncated_char} ->
+ scan_error(char, Pos);
+ {error, illegal_character} ->
+ scan_error({illegal, string}, Pos)
+ end;
+%% Punctuation characters and operators, first recognise multiples.
+scan1([$-,$>|Cs], Toks, Pos) ->
+ scan1(Cs, [{'->',Pos}|Toks], Pos);
+scan1([$:,$:|Cs], Toks, Pos) ->
+ scan1(Cs, [{'::',Pos}|Toks], Pos);
+scan1([$/,$/|Cs], Toks, Pos) ->
+ scan1(Cs, [{'//',Pos}|Toks], Pos);
+scan1([C|Cs], Toks, Pos) -> % Punctuation character
+ P = list_to_atom([C]),
+ scan1(Cs, [{P,Pos}|Toks], Pos);
+scan1([], Toks0, _Pos) ->
+ Toks = reverse(Toks0),
+ {ok,Toks}.
+
+%% Note that `_' is not accepted as a variable token.
+scan_variable(C, Cs, Toks, Pos) ->
+ {Wcs,Cs1} = scan_name(Cs, []),
+ W = [C|reverse(Wcs)],
+ case W of
+ "_" ->
+ scan_error({illegal,token}, Pos);
+ _ ->
+ case catch list_to_atom(W) of
+ A when is_atom(A) ->
+ scan1(Cs1, [{var,Pos,A}|Toks], Pos);
+ _ ->
+ scan_error({illegal,variable}, Pos)
+ end
+ end.
+
+scan_atom(C, Cs, Toks, Pos) ->
+ {Wcs,Cs1} = scan_name(Cs, []),
+ W = [C|reverse(Wcs)],
+ case catch list_to_atom(W) of
+ A when is_atom(A) ->
+ case reserved(A) of
+ true ->
+ scan1(Cs1, [{A,Pos}|Toks], Pos);
+ false ->
+ scan1(Cs1, [{atom,Pos,A}|Toks], Pos)
+ end;
+ _ ->
+ scan_error({illegal,token}, Pos)
+ end.
+
+%% scan_name(Cs) -> lists:splitwith(fun (C) -> name_char(C) end, Cs).
+
+scan_name([C|Cs], Ncs) ->
+ case name_char(C) of
+ true ->
+ scan_name(Cs, [C|Ncs]);
+ false ->
+ {Ncs,[C|Cs]} % Must rebuild here, sigh!
+ end;
+scan_name([], Ncs) ->
+ {Ncs,[]}.
+
+name_char(C) when C >= $a, C =< $z -> true;
+name_char(C) when C >= $\337, C =< $\377, C /= $\367 -> true;
+name_char(C) when C >= $A, C =< $Z -> true;
+name_char(C) when C >= $\300, C =< $\336, C /= $\327 -> true;
+name_char(C) when C >= $0, C =< $9 -> true;
+name_char($_) -> true;
+name_char($@) -> true;
+name_char(_) -> false.
+
+%% scan_string(CharList, QuoteChar, Pos) ->
+%% {StringChars,RestChars, NewPos}
+
+scan_string(Cs, Quote, Pos) ->
+ scan_string(Cs, [], Quote, Pos).
+
+scan_string([Quote|Cs], Scs, Quote, Pos) ->
+ {reverse(Scs),Cs,Pos};
+scan_string([], _Scs, _Quote, _Pos) ->
+ {error, premature_end};
+scan_string(Cs0, Scs, Quote, Pos) ->
+ case scan_char(Cs0, Pos) of
+ {C,Cs,Pos1} ->
+ %% Only build the string here
+ scan_string(Cs, [C|Scs], Quote, Pos1);
+ Error ->
+ Error
+ end.
+
+%% Note that space characters are not allowed
+scan_char_const([$\040 | _Cs0], _Toks, _Pos) ->
+ {error, illegal_character};
+scan_char_const(Cs0, Toks, Pos) ->
+ case scan_char(Cs0, Pos) of
+ {C,Cs,Pos1} ->
+ scan1(Cs, [{char,Pos,C}|Toks], Pos1);
+ Error ->
+ Error
+ end.
+
+%% {Character,RestChars,NewPos} = scan_char(Chars, Pos)
+%% Read a single character from a string or character constant. The
+%% pre-scan phase has checked for errors here.
+%% Note that control characters are not allowed.
+
+scan_char([$\\|Cs], Pos) ->
+ scan_escape(Cs, Pos);
+scan_char([C | _Cs], _Pos) when C =< 16#1f ->
+ {error, illegal_character};
+scan_char([C|Cs], Pos) ->
+ {C,Cs,Pos};
+scan_char([], _Pos) ->
+ {error, truncated_char}.
+
+%% The following conforms to Standard Erlang escape sequences.
+
+scan_escape([O1, O2, O3 | Cs], Pos) when % \<1-3> octal digits
+ O1 >= $0, O1 =< $3, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
+ Val = (O1*8 + O2)*8 + O3 - 73*$0,
+ {Val,Cs,Pos};
+scan_escape([O1, O2 | Cs], Pos) when
+ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7 ->
+ Val = (O1*8 + O2) - 9*$0,
+ {Val,Cs,Pos};
+scan_escape([O1 | Cs], Pos) when
+ O1 >= $0, O1 =< $7 ->
+ {O1 - $0,Cs,Pos};
+scan_escape([$^, C | Cs], Pos) -> % \^X -> CTL-X
+ if C >= $\100, C =< $\137 ->
+ {C - $\100,Cs,Pos};
+ true -> {error, illegal_control_character}
+ end;
+scan_escape([C | Cs], Pos) ->
+ case escape_char(C) of
+ C1 when C1 > $\000 -> {C1,Cs,Pos};
+ _ -> {error, undefined_escape_sequence}
+ end;
+scan_escape([], _Pos) ->
+ {error, truncated_char}.
+
+%% Note that we return $\000 for undefined escapes.
+escape_char($b) -> $\010; % \b = BS
+escape_char($d) -> $\177; % \d = DEL
+escape_char($e) -> $\033; % \e = ESC
+escape_char($f) -> $\014; % \f = FF
+escape_char($n) -> $\012; % \n = LF
+escape_char($r) -> $\015; % \r = CR
+escape_char($s) -> $\040; % \s = SPC
+escape_char($t) -> $\011; % \t = HT
+escape_char($v) -> $\013; % \v = VT
+escape_char($\\) -> $\134; % \\ = \
+escape_char($') -> $\047; % \' = '
+escape_char($") -> $\042; % \" = "
+escape_char(_C) -> $\000.
+
+%% scan_number(Char, CharList, TokenStack, Pos)
+%% We handle sign and radix notation:
+%% [+-]<digits> - the digits in base [+-]10
+%% [+-]<digits>.<digits>
+%% [+-]<digits>.<digits>E+-<digits>
+%% [+-]<digits>#<digits> - the digits read in base [+-]B
+%%
+%% Except for explicitly based integers we build a list of all the
+%% characters and then use list_to_integer/1 or list_to_float/1 to
+%% generate the value.
+
+%% SPos == Start position
+%% CPos == Current position
+
+scan_number(C, Cs0, Toks, Pos) ->
+ {Ncs,Cs,Pos1} = scan_integer(Cs0, [C], Pos),
+ scan_after_int(Cs, Ncs, Toks, Pos, Pos1).
+
+scan_signed_number(S, C, Cs0, Toks, Pos) ->
+ {Ncs,Cs,Pos1} = scan_integer(Cs0, [C, S], Pos),
+ scan_after_int(Cs, Ncs, Toks, Pos, Pos1).
+
+scan_integer([C|Cs], Stack, Pos) when C >= $0, C =< $9 ->
+ scan_integer(Cs, [C|Stack], Pos);
+scan_integer(Cs, Stack, Pos) ->
+ {Stack,Cs,Pos}.
+
+scan_after_int([$.,C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
+ {Ncs,Cs,CPos1} = scan_integer(Cs0, [C,$.|Ncs0], CPos),
+ scan_after_fraction(Cs, Ncs, Toks, SPos, CPos1);
+scan_after_int(Cs, Ncs, Toks, SPos, CPos) ->
+ N = list_to_integer(reverse(Ncs)),
+ scan1(Cs, [{integer,SPos,N}|Toks], CPos).
+
+scan_after_fraction([$E|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent(Cs, [$E|Ncs], Toks, SPos, CPos);
+scan_after_fraction([$e|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent(Cs, [$e|Ncs], Toks, SPos, CPos);
+scan_after_fraction(Cs, Ncs, Toks, SPos, CPos) ->
+ case catch list_to_float(reverse(Ncs)) of
+ N when is_float(N) ->
+ scan1(Cs, [{float,SPos,N}|Toks], CPos);
+ _Error -> scan_error({illegal,float}, SPos)
+ end.
+
+%% scan_exponent(CharList, NumberCharStack, TokenStack, StartPos, CurPos)
+%% Generate an error here if E{+|-} not followed by any digits.
+
+scan_exponent([$+|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, [$+|Ncs], Toks, SPos, CPos);
+scan_exponent([$-|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, [$-|Ncs], Toks, SPos, CPos);
+scan_exponent(Cs, Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, Ncs, Toks, SPos, CPos).
+
+scan_exponent1([C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
+ {Ncs,Cs,CPos1} = scan_integer(Cs0, [C|Ncs0], CPos),
+ case catch list_to_float(reverse(Ncs)) of
+ N when is_float(N) ->
+ scan1(Cs, [{float,SPos,N}|Toks], CPos1);
+ _Error -> scan_error({illegal,float}, SPos)
+ end;
+scan_exponent1(_, _, _, _, CPos) ->
+ scan_error(float, CPos).
+
+scan_error(In, Pos) ->
+ {error,{Pos,edoc_scanner,In}}.
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
new file mode 100644
index 0000000000..1f2cb99c75
--- /dev/null
+++ b/lib/edoc/src/edoc_tags.erl
@@ -0,0 +1,373 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright 2001-2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc EDoc tag scanning.
+
+%% TODO: tag/macro for including the code of a function as `<pre>'-text.
+%% TODO: consider new tag: @license text
+
+-module(edoc_tags).
+
+-export([tags/0, tags/1, tag_names/0, tag_parsers/0, scan_lines/2,
+ filter_tags/3, check_tags/4, parse_tags/4]).
+
+-import(edoc_report, [report/4, warning/4, error/3]).
+
+-include("edoc.hrl").
+-include("edoc_types.hrl").
+
+
+%% Tags are described by {Name, Parser, Flags}.
+%% Name = atom()
+%% Parser = text | xml | (Text,Line,Where) -> term()
+%% Flags = [Flag]
+%% Flag = module | function | package | overview | single
+%%
+%% Note that the pseudo-tag '@clear' is not listed here.
+%% (Cf. the function 'filter_tags'.)
+%%
+%% Rejected tag suggestions:
+%% - @keywords (never up to date; free text search is better)
+%% - @uses [modules] (never up to date; false dependencies)
+%% - @maintainer (never up to date; duplicates author info)
+%% - @contributor (unnecessary; mention in normal documentation)
+%% - @creator (unnecessary; already have copyright/author)
+%% - @history (never properly updated; use version control etc.)
+%% - @category (useless; superseded by keywords or free text search)
+
+tags() ->
+ All = [module,footer,function,package,overview],
+ [{author, fun parse_contact/4, [module,package,overview]},
+ {copyright, text, [module,package,overview,single]},
+ {deprecated, xml, [module,function,package,single]},
+ {doc, xml, [module,function,package,overview,single]},
+ {docfile, fun parse_file/4, All},
+ {'end', text, All},
+ {equiv, fun parse_expr/4, [function,single]},
+ {headerfile, fun parse_header/4, All},
+ {hidden, text, [module,function,single]},
+ {param, fun parse_param/4, [function]},
+ {private, text, [module,function,single]},
+ {reference, xml, [module,footer,package,overview]},
+ {returns, xml, [function,single]},
+ {see, fun parse_see/4, [module,function,package,overview]},
+ {since, text, [module,function,package,overview,single]},
+ {spec, fun parse_spec/4, [function,single]},
+ {throws, fun parse_throws/4, [function,single]},
+ {title, text, [overview,single]},
+ {'TODO', xml, All},
+ {todo, xml, All},
+ {type, fun parse_typedef/4, [module,footer,function]},
+ {version, text, [module,package,overview,single]}].
+
+aliases('TODO') -> todo;
+aliases(return) -> returns;
+aliases(T) -> T.
+
+%% Selecting tags based on flags.
+tags(Flag) ->
+ [T || {T,_,Fs} <- tags(), lists:member(Flag, Fs)].
+
+%% The set of known tags.
+tag_names() ->
+ [T || {T,_,_} <- tags()].
+
+%% The pairs of tags and their parsers.
+tag_parsers() ->
+ [{T,F} || {T,F,_} <- tags()].
+
+
+%% Scanning lines of comment text.
+
+scan_lines(Ss, L) ->
+ lists:reverse(scan_lines(Ss, L, [])).
+
+scan_lines([S | Ss], L, As) ->
+ scan_lines(S, Ss, L, As);
+scan_lines([], _L, As) ->
+ As.
+
+%% Looking for a leading '@', skipping whitespace.
+%% Also accept "TODO:" at start of line as equivalent to "@TODO".
+
+scan_lines([$\s | Cs], Ss, L, As) -> scan_lines(Cs, Ss, L, As);
+scan_lines([$\t | Cs], Ss, L, As) -> scan_lines(Cs, Ss, L, As);
+scan_lines([$@ | Cs], Ss, L, As) -> scan_tag(Cs, Ss, L, As, []);
+scan_lines(("TODO:"++_)=Cs, Ss, L, As) -> scan_tag(Cs, Ss, L, As, []);
+scan_lines(_, Ss, L, As) -> scan_lines(Ss, L + 1, As).
+
+%% Scanning chars following '@', accepting only nonempty valid names.
+%% See edoc_lib:is_name/1 for details on what is a valid name. In tags
+%% we also allow the initial letter to be uppercase or underscore.
+
+scan_tag([C | Cs], Ss, L, As, Ts) when C >= $a, C =< $z ->
+ scan_tag_1(Cs, Ss, L, As, [C | Ts]);
+scan_tag([C | Cs], Ss, L, As, Ts) when C >= $A, C =< $Z ->
+ scan_tag_1(Cs, Ss, L, As, [C | Ts]);
+scan_tag([C | Cs], Ss, L, As, Ts) when C >= $\300, C =< $\377,
+ C =/= $\327, C =/= $\367 ->
+ scan_tag_1(Cs, Ss, L, As, [C | Ts]);
+scan_tag([$_ | Cs], Ss, L, As, Ts) ->
+ scan_tag_1(Cs, Ss, L, As, [$_ | Ts]);
+scan_tag(_Cs, Ss, L, As, _Ts) ->
+ scan_lines(Ss, L + 1, As). % not a valid name
+
+scan_tag_1([C | Cs], Ss, L, As, Ts) when C >= $a, C =< $z ->
+ scan_tag_1(Cs, Ss, L, As, [C | Ts]);
+scan_tag_1([C | Cs], Ss, L, As, Ts) when C >= $A, C =< $Z ->
+ scan_tag_1(Cs, Ss, L, As, [C | Ts]);
+scan_tag_1([C | Cs], Ss, L, As, Ts) when C >= $0, C =< $9 ->
+ scan_tag_1(Cs, Ss, L, As, [C | Ts]);
+scan_tag_1([C | Cs], Ss, L, As, Ts) when C >= $\300, C =< $\377,
+ C =/= $\327, C =/= $\367 ->
+ scan_tag_1(Cs, Ss, L, As, [C | Ts]);
+scan_tag_1([$_ | Cs], Ss, L, As, Ts) ->
+ scan_tag_1(Cs, Ss, L, As, [$_ | Ts]);
+scan_tag_1(Cs, Ss, L, As, Ts) ->
+ scan_tag_2(Cs, Ss, L, As, {Ts, L}).
+
+%% Check that the tag is followed by whitespace, linebreak, or colon.
+
+scan_tag_2([$\s | Cs], Ss, L, As, T) ->
+ scan_tag_lines(Ss, T, [Cs], L + 1, As);
+scan_tag_2([$\t | Cs], Ss, L, As, T) ->
+ scan_tag_lines(Ss, T, [Cs], L + 1, As);
+scan_tag_2([$: | Cs], Ss, L, As, T) ->
+ scan_tag_lines(Ss, T, [Cs], L + 1, As);
+scan_tag_2([], Ss, L, As, T) ->
+ scan_tag_lines(Ss, T, [[]], L + 1, As);
+scan_tag_2(_, Ss, L, As, _T) ->
+ scan_lines(Ss, L + 1, As).
+
+%% Scanning lines after a tag is found.
+
+scan_tag_lines([S | Ss], T, Ss1, L, As) ->
+ scan_tag_lines(S, S, Ss, T, Ss1, L, As);
+scan_tag_lines([], {Ts, L1}, Ss1, _L, As) ->
+ [make_tag(Ts, L1, Ss1) | As].
+
+%% Collecting tag text lines until end of comment or next tagged line.
+
+scan_tag_lines([$\s | Cs], S, Ss, T, Ss1, L, As) ->
+ scan_tag_lines(Cs, S, Ss, T, Ss1, L, As);
+scan_tag_lines([$\t | Cs], S, Ss, T, Ss1, L, As) ->
+ scan_tag_lines(Cs, S, Ss, T, Ss1, L, As);
+scan_tag_lines([$@, C | _Cs], S, Ss, {Ts, L1}, Ss1, L, As)
+ when C >= $a, C =< $z ->
+ scan_lines(S, Ss, L, [make_tag(Ts, L1, Ss1) | As]);
+scan_tag_lines([$@, C | _Cs], S, Ss, {Ts, L1}, Ss1, L, As)
+ when C >= $A, C =< $Z ->
+ scan_lines(S, Ss, L, [make_tag(Ts, L1, Ss1) | As]);
+scan_tag_lines([$@, C | _Cs], S, Ss, {Ts, L1}, Ss1, L, As)
+ when C >= $\300, C =< $\377, C =/= $\327, C =/= $\367 ->
+ scan_lines(S, Ss, L, [make_tag(Ts, L1, Ss1) | As]);
+scan_tag_lines("TODO:"++_, S, Ss, {Ts, L1}, Ss1, L, As) ->
+ scan_lines(S, Ss, L, [make_tag(Ts, L1, Ss1) | As]);
+scan_tag_lines(_Cs, S, Ss, T, Ss1, L, As) ->
+ scan_tag_lines(Ss, T, [S | Ss1], L + 1, As).
+
+make_tag(Cs, L, Ss) ->
+ #tag{name = aliases(list_to_atom(lists:reverse(Cs))),
+ line = L,
+ data = append_lines(lists:reverse(Ss))}.
+
+%% Flattening lines of text and inserting line breaks.
+
+append_lines([L]) -> L;
+append_lines([L | Ls]) -> L ++ [$\n | append_lines(Ls)];
+append_lines([]) -> [].
+
+%% Filtering out unknown tags.
+
+filter_tags(Ts, Tags, Where) ->
+ filter_tags(Ts, Tags, Where, []).
+
+filter_tags([#tag{name = clear} | Ts], Tags, Where, _Ts1) ->
+ filter_tags(Ts, Tags, Where);
+filter_tags([#tag{name = N, line = L} = T | Ts], Tags, Where, Ts1) ->
+ case sets:is_element(N, Tags) of
+ true ->
+ filter_tags(Ts, Tags, Where, [T | Ts1]);
+ false ->
+ warning(L, Where, "tag @~s not recognized.", [N]),
+ filter_tags(Ts, Tags, Where, Ts1)
+ end;
+filter_tags([], _, _, Ts) ->
+ lists:reverse(Ts).
+
+%% Check occurrances of tags.
+
+check_tags(Ts, Allow, Single, Where) ->
+ check_tags(Ts, Allow, Single, Where, false, sets:new()).
+
+check_tags([#tag{name = T, line = L} | Ts], Allow, Single, Where, Error, Seen) ->
+ case sets:is_element(T, Seen) of
+ true ->
+ case sets:is_element(T, Single) of
+ false ->
+ check_tags(Ts, Allow, Single, Where, Error, Seen);
+ true ->
+ report(L, Where, "multiple @~s tag.", [T]),
+ check_tags(Ts, Allow, Single, Where, true, Seen)
+ end;
+ false ->
+ Seen1 = sets:add_element(T, Seen),
+ case sets:is_element(T, Allow) of
+ true ->
+ check_tags(Ts, Allow, Single, Where, Error, Seen1);
+ false ->
+ report(L, Where, "tag @~s not allowed here.", [T]),
+ check_tags(Ts, Allow, Single, Where, true, Seen1)
+ end
+ end;
+check_tags([], _, _, _, Error, _) ->
+ Error.
+
+
+%% Parses tag contents for specific tags.
+
+parse_tags(Ts, How, Env, Where) ->
+ parse_tags(Ts, How, Env, Where, []).
+
+parse_tags([#tag{name = Name} = T | Ts], How, Env, Where, Ts1) ->
+ case dict:fetch(Name, How) of
+ text ->
+ parse_tags(Ts, How, Env, Where, [T | Ts1]);
+ xml ->
+ [T1] = parse_tag(T, fun parse_xml/4, Env, Where),
+ parse_tags(Ts, How, Env, Where, [T1 | Ts1]);
+ F when is_function(F) ->
+ Ts2 = parse_tag(T, F, Env, Where),
+ parse_tags(Ts, How, Env, Where, lists:reverse(Ts2, Ts1))
+ end;
+parse_tags([], _How, _Env, _Where, Ts) ->
+ lists:reverse(Ts).
+
+parse_tag(T, F, Env, Where) ->
+ case catch {ok, F(T#tag.data, T#tag.line, Env, Where)} of
+ {ok, Data} ->
+ [T#tag{data = Data}];
+ {expand, Ts} ->
+ Ts;
+ {error, L, Error} ->
+ error(L, Where, Error),
+ exit(error);
+ {'EXIT', R} -> exit(R);
+ Other -> throw(Other)
+ end.
+
+%% parser functions for the built-in content types. They also perform
+%% some sanity checks on the results.
+
+parse_xml(Data, Line, _Env, _Where) ->
+ edoc_wiki:parse_xml(Data, Line).
+
+parse_see(Data, Line, _Env, _Where) ->
+ edoc_parser:parse_see(Data, Line).
+
+parse_expr(Data, Line, _Env, _Where) ->
+ edoc_lib:parse_expr(Data, Line).
+
+parse_spec(Data, Line, _Env, {_, {F, A}} = _Where) ->
+ Spec = edoc_parser:parse_spec(Data, Line),
+ #t_spec{name = N, type = #t_fun{args = As}} = Spec,
+ if length(As) /= A ->
+ throw_error(Line, "@spec arity does not match.");
+ true ->
+ case N of
+ undefined ->
+ Spec#t_spec{name = #t_name{module = [], name = F}};
+ #t_name{module = [], name = F} ->
+ Spec;
+ _ ->
+ throw_error(Line, "@spec name does not match.")
+ end
+ end.
+
+parse_param(Data, Line, _Env, {_, {_F, _A}} = _Where) ->
+ edoc_parser:parse_param(Data, Line).
+
+parse_throws(Data, Line, _Env, {_, {_F, _A}} = _Where) ->
+ edoc_parser:parse_throws(Data, Line).
+
+parse_contact(Data, Line, _Env, _Where) ->
+ case edoc_lib:parse_contact(Data, Line) of
+ {"", "", _URI} ->
+ throw_error(Line, "must specify name or e-mail.");
+ Info ->
+ Info
+ end.
+
+parse_typedef(Data, Line, _Env, _Where) ->
+ Def = edoc_parser:parse_typedef(Data, Line),
+ {#t_typedef{name = #t_name{name = T}}, _} = Def,
+ case edoc_types:is_predefined(T) of
+ true ->
+ throw_error(Line, {"redefining built-in type '~w'.", [T]});
+ false ->
+ Def
+ end.
+
+parse_file(Data, Line, Env, _Where) ->
+ case edoc_lib:parse_expr(Data, Line) of
+ {string, _, File0} ->
+ File = edoc_lib:strip_space(File0),
+ case edoc_extract:file(File, module, Env, []) of
+ {ok, Ts} ->
+ throw({expand, Ts});
+ {error, R} ->
+ throw_error(Line, {read_file, File, R})
+ end;
+ _ ->
+ throw_error(Line, file_not_string)
+ end.
+
+parse_header(Data, Line, Env, {Where, _}) ->
+ parse_header(Data, Line, Env, Where);
+parse_header(Data, Line, Env, Where) when is_list(Where) ->
+ case edoc_lib:parse_expr(Data, Line) of
+ {string, _, File} ->
+ Dir = filename:dirname(Where),
+ Path = Env#env.includes ++ [Dir],
+ case edoc_lib:find_file(Path, "", File) of
+ "" ->
+ throw_error(Line, {file_not_found, File});
+ File1 ->
+ Ts = edoc_extract:header(File1, Env, []),
+ throw({expand, Ts})
+ end;
+ _ ->
+ throw_error(Line, file_not_string)
+ end.
+
+throw_error(L, {read_file, File, R}) ->
+ throw_error(L, {"error reading file '~s': ~w",
+ [edoc_lib:filename(File), R]});
+throw_error(L, {file_not_found, F}) ->
+ throw_error(L, {"file not found: ~s", [F]});
+throw_error(L, file_not_string) ->
+ throw_error(L, "expected file name as a string");
+throw_error(L, D) ->
+ throw({error, L, D}).
diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl
new file mode 100644
index 0000000000..85c9ee6f2a
--- /dev/null
+++ b/lib/edoc/src/edoc_types.erl
@@ -0,0 +1,204 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright 2001-2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc Datatype representation for EDoc.
+
+-module(edoc_types).
+
+-export([is_predefined/1, to_ref/1, to_xml/2, to_label/1, arg_names/1,
+ set_arg_names/2, arg_descs/1, range_desc/1]).
+
+%% @headerfile "edoc_types.hrl"
+
+-include("edoc_types.hrl").
+-include("xmerl.hrl").
+
+
+is_predefined(any) -> true;
+is_predefined(atom) -> true;
+is_predefined(binary) -> true;
+is_predefined(bool) -> true;
+is_predefined(char) -> true;
+is_predefined(cons) -> true;
+is_predefined(deep_string) -> true;
+is_predefined(float) -> true;
+is_predefined(function) -> true;
+is_predefined(integer) -> true;
+is_predefined(list) -> true;
+is_predefined(nil) -> true;
+is_predefined(none) -> true;
+is_predefined(number) -> true;
+is_predefined(pid) -> true;
+is_predefined(port) -> true;
+is_predefined(reference) -> true;
+is_predefined(string) -> true;
+is_predefined(term) -> true;
+is_predefined(tuple) -> true;
+is_predefined(_) -> false.
+
+to_ref(#t_typedef{name = N}) ->
+ to_ref(N);
+to_ref(#t_def{name = N}) ->
+ to_ref(N);
+to_ref(#t_type{name = N}) ->
+ to_ref(N);
+to_ref(#t_name{module = [], name = N}) ->
+ edoc_refs:type(N);
+to_ref(#t_name{app = [], module = M, name = N}) ->
+ edoc_refs:type(M, N);
+to_ref(#t_name{app = A, module = M, name = N}) ->
+ edoc_refs:type(A, M, N).
+
+to_label(N) ->
+ edoc_refs:to_label(to_ref(N)).
+
+get_uri(Name, Env) ->
+ edoc_refs:get_uri(to_ref(Name), Env).
+
+to_xml(#t_var{name = N}, _Env) ->
+ {typevar, [{name, atom_to_list(N)}], []};
+to_xml(#t_name{module = [], name = N}, _Env) ->
+ {erlangName, [{name, atom_to_list(N)}], []};
+to_xml(#t_name{app = [], module = M, name = N}, _Env) ->
+ {erlangName, [{module, atom_to_list(M)},
+ {name, atom_to_list(N)}], []};
+to_xml(#t_name{app = A, module = M, name = N}, _Env) ->
+ {erlangName, [{app, atom_to_list(A)},
+ {module, atom_to_list(M)},
+ {name, atom_to_list(N)}], []};
+to_xml(#t_type{name = N, args = As}, Env) ->
+ Predef = case N of
+ #t_name{module = [], name = T} ->
+ is_predefined(T);
+ _ ->
+ false
+ end,
+ HRef = case Predef of
+ true -> [];
+ false -> [{href, get_uri(N, Env)}]
+ end,
+ {abstype, HRef, [to_xml(N, Env) | map(fun wrap_utype/2, As, Env)]};
+to_xml(#t_fun{args = As, range = T}, Env) ->
+ {'fun', [{argtypes, map(fun wrap_utype/2, As, Env)},
+ wrap_utype(T, Env)]};
+to_xml(#t_tuple{types = Ts}, Env) ->
+ {tuple, map(fun wrap_utype/2, Ts, Env)};
+to_xml(#t_list{type = T}, Env) ->
+ {list, [wrap_utype(T, Env)]};
+to_xml(#t_nil{}, _Env) ->
+ nil;
+to_xml(#t_atom{val = V}, _Env) ->
+ {atom, [{value, io_lib:write(V)}], []};
+to_xml(#t_integer{val = V}, _Env) ->
+ {integer, [{value, integer_to_list(V)}], []};
+to_xml(#t_float{val = V}, _Env) ->
+ {float, [{value, io_lib:write(V)}], []};
+to_xml(#t_union{types = Ts}, Env) ->
+ {union, map(fun wrap_type/2, Ts, Env)};
+to_xml(#t_record{name = N = #t_atom{}, fields = Fs}, Env) ->
+ {record, [to_xml(N, Env) | map(fun to_xml/2, Fs, Env)]};
+to_xml(#t_field{name = N = #t_atom{}, type = T}, Env) ->
+ {field, [to_xml(N, Env), wrap_type(T, Env)]};
+to_xml(#t_def{name = N = #t_var{}, type = T}, Env) ->
+ {localdef, [to_xml(N, Env), wrap_type(T, Env)]};
+to_xml(#t_def{name = N, type = T}, Env) ->
+ {localdef, [{label, to_label(N)}],
+ [to_xml(N, Env), wrap_type(T, Env)]};
+to_xml(#t_spec{name = N, type = T, defs = Ds}, Env) ->
+ {typespec, [to_xml(N, Env), wrap_utype(T, Env)
+ | map(fun to_xml/2, Ds, Env)]};
+to_xml(#t_typedef{name = N, args = As, type = undefined, defs = Ds},
+ Env) ->
+ {typedef, [to_xml(N, Env),
+ {argtypes, map(fun wrap_utype/2, As, Env)}
+ | map(fun to_xml/2, Ds, Env)]};
+to_xml(#t_typedef{name = N, args = As, type = T, defs = Ds}, Env) ->
+ {typedef, [to_xml(N, Env),
+ {argtypes, map(fun wrap_utype/2, As, Env)},
+ wrap_type(T, Env)
+ | map(fun to_xml/2, Ds, Env)]};
+to_xml(#t_throws{type = T, defs = Ds}, Env) ->
+ {throws, [wrap_type(T, Env)
+ | map(fun to_xml/2, Ds, Env)]}.
+
+wrap_type(T, Env) ->
+ {type, [to_xml(T, Env)]}.
+
+wrap_utype(T, Env) ->
+ E = to_xml(T, Env),
+ case arg_name(T) of
+ '_' -> {type, [E]};
+ A -> {type, [{name, atom_to_list(A)}], [E]}
+ end.
+
+map(F, Xs, Env) ->
+ [F(X, Env) || X <- Xs].
+
+is_name(A) when is_atom(A) -> true;
+is_name(_) -> false.
+
+is_desc(A) when is_list(A) -> true;
+is_desc(_) -> false.
+
+arg_name(T) ->
+ find(?t_ann(T), fun is_name/1, '_').
+
+arg_names(S) ->
+ arg_anns(S, fun is_name/1, '_').
+
+arg_descs(S) ->
+ arg_anns(S, fun is_desc/1, "").
+
+range_desc(#t_spec{type = #t_fun{range = T}}) ->
+ find(?t_ann(T), fun is_desc/1, "").
+
+arg_anns(#t_spec{type = #t_fun{args = As}}, F, Def) ->
+ [find(?t_ann(A), F, Def) || A <- As].
+
+find([A| As], F, Def) ->
+ case F(A) of
+ true -> A;
+ false -> find(As, F, Def)
+ end;
+find([], _, Def) -> Def.
+
+set_arg_names(S, Ns) ->
+ set_arg_anns(S, Ns, fun is_name/1).
+
+%% set_arg_descs(S, Ns) ->
+%% set_arg_anns(S, Ns, fun is_desc/1).
+
+set_arg_anns(#t_spec{type = #t_fun{args = As}=T}=S, Ns, F) ->
+ Zip = fun (A, N) ->
+ ?set_t_ann(A, update(?t_ann(A), N, F))
+ end,
+ S#t_spec{type = T#t_fun{args = lists:zipwith(Zip, As, Ns)}}.
+
+update([A| As], N, F) ->
+ case F(A) of
+ true -> [N | As];
+ false -> [A| update(As, N, F)]
+ end;
+update([], N, _) -> [N].
diff --git a/lib/edoc/src/edoc_types.hrl b/lib/edoc/src/edoc_types.hrl
new file mode 100644
index 0000000000..1dcbdd9493
--- /dev/null
+++ b/lib/edoc/src/edoc_types.hrl
@@ -0,0 +1,130 @@
+%% =====================================================================
+%% Header file for EDoc Type Representations
+%%
+%% Copyright (C) 2001-2005 Richard Carlsson
+%%
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% Author contact: [email protected]
+%% =====================================================================
+
+%% Type specification data structures
+
+%% @type t_spec() = #t_spec{name = t_name(),
+%% type = t_type(),
+%% defs = [t_def()]}
+
+-record(t_spec, {name, type, defs=[]}). % function specification
+
+%% @type type() = t_atom() | t_fun() | t_integer() | t_list() | t_nil()
+%% | t_tuple() | t_type() | t_union() | t_var()
+
+%% @type t_typedef() = #t_typedef{name = t_name(),
+%% args = [type()],
+%% type = type(),
+%% defs = [t_def()]}
+
+-record(t_typedef, {name, args, type,
+ defs=[]}). % type declaration/definition
+
+%% @type t_throws() = #t_throws{type = type(),
+%% defs = [t_def()]}
+
+-record(t_throws, {type, defs=[]}). % exception declaration
+
+%% @type t_def() = #t_def{name = t_name(),
+%% type = type()}
+
+-record(t_def, {name, type}). % local definition 'name = type'
+%% @type t_name() = #t_name{app = [] | atom(),
+%% module = [] | atom(),
+%% name = [] | atom()}
+
+-record(t_name, {app = [], % app = [] if module = []
+ module=[], % unqualified if module = []
+ name=[]}).
+
+%% The following records all have 'a=[]' as their first field.
+%% This is used for name and usage annotations; in particular, the
+%% fun-argument types of a function specification (t_spec) are often
+%% annotated with the names of the corresponding formal parameters,
+%% and/or usage summaries.
+
+-define(t_ann(X), element(2, X)).
+-define(set_t_ann(X, Y), setelement(2, X, Y)).
+-define(add_t_ann(X, Y), ?set_t_ann(X, [Y | ?t_ann(X)])).
+
+%% @type t_var() = #t_var{a = list(), name = [] | atom()}
+
+-record(t_var, {a=[], name=[]}). % type variable
+
+%% @type t_type() = #t_type{a = list(),
+%% name = t_name(),
+%% args = [type()]}
+
+-record(t_type, {a=[], name, args = []}). % abstract type 'name(...)'
+
+%% @type t_union() = #t_union{a = list(),
+%% types = [type()]}
+
+-record(t_union, {a=[], types = []}). % union type 't1|...|tN'
+
+%% @type t_fun() = #t_fun{a = list(),
+%% args = [type()],
+%% range = type()}
+
+-record(t_fun, {a=[], args, range}). % function '(t1,...,tN) -> range'
+
+%% @type t_tuple() = #t_tuple{a = list(),
+%% types = [type()]}
+
+-record(t_tuple, {a=[], types = []}). % tuple type '{t1,...,tN}'
+
+%% @type t_list() = #t_list{a = list(),
+%% type = type()}
+
+-record(t_list, {a=[], type}). % list type '[type]'
+
+%% @type t_nil() = #t_nil{a = list()}
+
+-record(t_nil, {a=[]}). % empty-list constant '[]'
+
+%% @type t_atom() = #t_atom{a = list(),
+%% val = atom()}
+
+-record(t_atom, {a=[], val}). % atom constant
+
+%% @type t_integer() = #t_integer{a = list(),
+%% val = integer()}
+
+-record(t_integer, {a=[], val}). % integer constant
+
+%% @type t_float() = #t_float{a = list(),
+%% val = float()}
+
+-record(t_float, {a=[], val}). % floating-point constant
+
+%% @type t_record() = #t_list{a = list(),
+%% name = type(),
+%% fields = [field()]}
+
+-record(t_record, {a=[], name, fields = []}). % record type '#r{f1,...,fN}'
+
+%% @type t_field() = #t_field{a = list(),
+%% name = type(),
+%% type = type()}
+
+-record(t_field, {a=[], name, type}). % named field 'n1=t1'
diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl
new file mode 100644
index 0000000000..e4a3d74734
--- /dev/null
+++ b/lib/edoc/src/edoc_wiki.erl
@@ -0,0 +1,456 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @private
+%% @copyright 2001-2003 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @see edoc
+%% @end
+%% =====================================================================
+
+%% @doc EDoc wiki expansion, parsing and postprocessing of XML text.
+%% Uses {@link //xmerl. XMerL}.
+%% @end
+
+%% Notes:
+%%
+%% * Whatever happens in this module, it must interact nicely with the
+%% actual XML-parsing. It is not acceptable to break any existing and
+%% legal XML markup so that it does not parse or is rendered wrong.
+%%
+%% * The focus should always be on making *documentation* easier to
+%% write. No wiki notation should be introduced unless it is clear that
+%% it is better than using plain XHTML, making typing less cumbersome
+%% and the resulting text easier to read. The wiki notation should be a
+%% small bag of easy-to-remember tricks for making XHTML documentation
+%% easier to write, not a complete markup language in itself. As a
+%% typical example, it is hardly worthwile to introduce a special
+%% notation like say, ""..."" for emphasized text, since <em>...</em> is
+%% not much harder to write, not any less readable, and no more
+%% difficult to remember, especially since emphasis is not very often
+%% occurring in normal documentation.
+%%
+%% * The central reasoning for the code-quoting goes like this: I don't
+%% want to have special escape characters within the quotes (like
+%% backslash in C), to allow quoting of the quote characters themselves.
+%% I also don't want to use the "`" character both for opening and
+%% closing quotes. Therefore, you can either use `...' - and then you
+%% cannot use the "'" character without ending the quote - or you can
+%% use ``...'' - which allows single but not double "'" characters
+%% within the quote. Whitespace is automatically removed from the
+%% beginning and the end of the quoted strings; this allows you to write
+%% things like "`` 'foo@bar' ''". Text that contains "''" has to be
+%% written within <code>...</code>.
+%%
+%% To produce a single "`" character without starting a quote, write
+%% "`'" (no space between "`" and "'").
+%%
+%% For verbatim/preformatted text, the ```...'''-quotes expand to
+%% "<pre><![CDATA[...]]></pre>". The indentation at the start of the
+%% quoted string is preserved; whitespace is stripped only at the end.
+%% Whole leading lines of whitespace are however skipped.
+
+-module(edoc_wiki).
+
+-export([parse_xml/2, expand_text/2]).
+
+-include("edoc.hrl").
+-include("xmerl.hrl").
+
+-define(BASE_HEADING, 3).
+
+
+%% Parsing Wiki-XML with pre-and post-expansion.
+
+parse_xml(Data, Line) ->
+ par(parse_xml_1(expand_text(Data, Line), Line)).
+
+parse_xml_1(Text, Line) ->
+ Text1 = "<doc>" ++ Text ++ "</doc>",
+ case catch {ok, xmerl_scan:string(Text1, [{line, Line}])} of
+ {ok, {E, _}} ->
+ E#xmlElement.content;
+ {'EXIT', {fatal, {Reason, L, _C}}} ->
+ throw_error(L, {"XML parse error: ~p.", [Reason]});
+ {'EXIT', Reason} ->
+ throw_error(Line, {"error in XML parser: ~P.", [Reason, 10]});
+ Other ->
+ throw_error(Line, {"nocatch in XML parser: ~P.", [Other, 10]})
+ end.
+
+%% Expand wiki stuff in arbitrary text.
+
+expand_text(Cs, L) ->
+ lists:reverse(expand_new_line(Cs, L, [])).
+
+%% Interestingly, the reverse of "code" is "edoc". :-)
+
+expand_new_line([$\s = C | Cs], L, As) ->
+ expand_new_line(Cs, L, [C | As]);
+expand_new_line([$\t = C | Cs], L, As) ->
+ expand_new_line(Cs, L, [C | As]);
+expand_new_line([$\n = C | Cs], L, As) ->
+ expand_new_line(Cs, L + 1, [C | As]);
+expand_new_line([$=, $=, $=, $= | Cs], L, As) ->
+ expand_heading(Cs, 2, L, As);
+expand_new_line([$=, $=, $= | Cs], L, As) ->
+ expand_heading(Cs, 1, L, As);
+expand_new_line([$=, $= | Cs], L, As) ->
+ expand_heading(Cs, 0, L, As);
+expand_new_line(Cs, L, As) ->
+ expand(Cs, L, As).
+
+expand([$`, $' | Cs], L, As) ->
+ expand(Cs, L, [$` | As]); % produce "`" - don't start a new quote
+expand([$`, $`, $` | Cs], L, As) ->
+ %% If this is the first thing on the line, compensate for the
+ %% indentation, unless we had to skip one or more empty lines.
+ {Cs1, Skipped} = strip_empty_lines(Cs), % avoid vertical space
+ N = if Skipped > 0 ->
+ 0;
+ true ->
+ {As1, _} = edoc_lib:split_at(As, $\n),
+ case edoc_lib:is_space(As1) of
+ true -> 3 + length(As1);
+ false -> 2 % nice default - usually right.
+ end
+ end,
+ Ss = lists:duplicate(N, $\s),
+ expand_triple(Cs1, L + Skipped, Ss ++ "[ATADC[!<>erp<" ++ As);
+expand([$`, $` | Cs], L, As) ->
+ expand_double(edoc_lib:strip_space(Cs), L, ">edoc<" ++ As);
+expand([$` | Cs], L, As) ->
+ expand_single(edoc_lib:strip_space(Cs), L, ">edoc<" ++ As);
+expand([$[ | Cs], L, As) ->
+ expand_uri(Cs, L, As);
+expand([$\n = C | Cs], L, As) ->
+ expand_new_line(Cs, L + 1, [C | As]);
+expand([C | Cs], L, As) ->
+ expand(Cs, L, [C | As]);
+expand([], _, As) ->
+ As.
+
+%% == Heading ==
+%% === SubHeading ===
+%% ==== SubSubHeading ====
+
+expand_heading([$= | _] = Cs, N, L, As) ->
+ expand_heading_1(Cs, N, L, As);
+expand_heading(Cs, N, L, As) ->
+ {Cs1, Cs2} = edoc_lib:split_at(Cs, $\n),
+ case edoc_lib:strip_space(lists:reverse(Cs1)) of
+ [$=, $= | Cs3] ->
+ {Es, Ts} = lists:splitwith(fun (X) -> X =:= $= end, Cs3),
+ if length(Es) =:= N ->
+ Ts1 = edoc_lib:strip_space(
+ lists:reverse(edoc_lib:strip_space(Ts))),
+ expand_heading_2(Ts1, Cs2, N, L, As);
+ true ->
+ H1 = lists:duplicate(N+2, $=),
+ H2 = "==" ++ Es,
+ throw_error(L, {"heading end marker mismatch: "
+ "~s...~s", [H1, H2]})
+ end;
+ _ ->
+ expand_heading_1(Cs, N, L, As)
+ end.
+
+expand_heading_1(Cs, N, L, As) ->
+ expand(Cs, L, lists:duplicate(N + 2, $=) ++ As).
+
+expand_heading_2(Ts, Cs, N, L, As) ->
+ H = ?BASE_HEADING + N,
+ Ts1 = io_lib:format("<h~w><a name=\"~s\">~s</a></h~w>\n",
+ [H, make_label(Ts), Ts, H]),
+ expand_new_line(Cs, L + 1, lists:reverse(lists:flatten(Ts1), As)).
+
+make_label([$\s | Cs]) ->
+ [$_ | make_label(edoc_lib:strip_space(Cs))];
+make_label([$\t | Cs]) ->
+ [$_ | make_label(edoc_lib:strip_space(Cs))];
+make_label([$\n | Cs]) ->
+ [$_ | make_label(edoc_lib:strip_space(Cs))];
+make_label([C | Cs]) ->
+ [C | make_label(Cs)];
+make_label([]) ->
+ [].
+
+%% `...'
+
+expand_single(Cs, L, As) ->
+ expand_single(Cs, L, As, L).
+
+expand_single([$' | Cs], L, As, _L0) ->
+ expand(Cs, L, ">edoc/<" ++ edoc_lib:strip_space(As));
+expand_single([$< | Cs], L, As, L0) ->
+ expand_single(Cs, L, ";tl&" ++ As, L0);
+expand_single([$> | Cs], L, As, L0) ->
+ expand_single(Cs, L, ";tg&" ++ As, L0);
+expand_single([$& | Cs], L, As, L0) ->
+ expand_single(Cs, L, ";pma&" ++ As, L0);
+expand_single([$\n = C | Cs], L, As, L0) ->
+ expand_single(Cs, L + 1, [C | As], L0);
+expand_single([C | Cs], L, As, L0) ->
+ expand_single(Cs, L, [C | As], L0);
+expand_single([], L, _, L0) ->
+ throw_error(L0, {"`-quote ended unexpectedly at line ~w", [L]}).
+
+%% ``...''
+
+expand_double(Cs, L, As) ->
+ expand_double(Cs, L, As, L).
+
+expand_double([$', $' | Cs], L, As, _L0) ->
+ expand(Cs, L, ">edoc/<" ++ edoc_lib:strip_space(As));
+expand_double([$< | Cs], L, As, L0) ->
+ expand_double(Cs, L, ";tl&" ++ As, L0);
+expand_double([$> | Cs], L, As, L0) ->
+ expand_double(Cs, L, ";tg&" ++ As, L0);
+expand_double([$& | Cs], L, As, L0) ->
+ expand_double(Cs, L, ";pma&" ++ As, L0);
+expand_double([$\n = C | Cs], L, As, L0) ->
+ expand_double(Cs, L + 1, [C | As], L0);
+expand_double([C | Cs], L, As, L0) ->
+ expand_double(Cs, L, [C | As], L0);
+expand_double([], L, _, L0) ->
+ throw_error(L0, {"``-quote ended unexpectedly at line ~w", [L]}).
+
+%% ```...'''
+
+expand_triple(Cs, L, As) ->
+ expand_triple(Cs, L, As, L).
+
+expand_triple([$', $', $' | Cs], L, As, _L0) -> % ' stupid emacs
+ expand(Cs, L, ">erp/<>]]" ++ edoc_lib:strip_space(As));
+expand_triple([$], $], $> | Cs], L, As, L0) ->
+ expand_triple(Cs, L, ";tg&]]" ++ As, L0);
+expand_triple([$\n = C | Cs], L, As, L0) ->
+ expand_triple(Cs, L + 1, [C | As], L0);
+expand_triple([C | Cs], L, As, L0) ->
+ expand_triple(Cs, L, [C | As], L0);
+expand_triple([], L, _, L0) ->
+ throw_error(L0, {"```-quote ended unexpectedly at line ~w", [L]}).
+
+%% e.g. [file:/...] or [http://... LinkText]
+
+expand_uri("http:/" ++ Cs, L, As) ->
+ expand_uri(Cs, L, "/:ptth", As);
+expand_uri("ftp:/" ++ Cs, L, As) ->
+ expand_uri(Cs, L, "/:ptf", As);
+expand_uri("file:/" ++ Cs, L, As) ->
+ expand_uri(Cs, L, "/:elif", As);
+expand_uri(Cs, L, As) ->
+ expand(Cs, L, [$[ | As]).
+
+expand_uri([$] | Cs], L, Us, As) ->
+ expand(Cs, L, push_uri(Us, ">tt/<" ++ Us ++ ">tt<", As));
+expand_uri([$\s = C | Cs], L, Us, As) ->
+ expand_uri(Cs, 0, L, [C], Us, As);
+expand_uri([$\t = C | Cs], L, Us, As) ->
+ expand_uri(Cs, 0, L, [C], Us, As);
+expand_uri([$\n = C | Cs], L, Us, As) ->
+ expand_uri(Cs, 1, L, [C], Us, As);
+expand_uri([C | Cs], L, Us, As) ->
+ expand_uri(Cs, L, [C | Us], As);
+expand_uri([], L, Us, _As) ->
+ expand_uri_error(Us, L).
+
+expand_uri([$] | Cs], N, L, Ss, Us, As) ->
+ Ss1 = lists:reverse(edoc_lib:strip_space(
+ lists:reverse(edoc_lib:strip_space(Ss)))),
+ expand(Cs, L + N, push_uri(Us, Ss1, As));
+expand_uri([$\n = C | Cs], N, L, Ss, Us, As) ->
+ expand_uri(Cs, N + 1, L, [C | Ss], Us, As);
+expand_uri([C | Cs], N, L, Ss, Us, As) ->
+ expand_uri(Cs, N, L, [C | Ss], Us, As);
+expand_uri([], _, L, _Ss, Us, _As) ->
+ expand_uri_error(Us, L).
+
+-spec expand_uri_error(list(), pos_integer()) -> no_return().
+
+expand_uri_error(Us, L) ->
+ {Ps, _} = edoc_lib:split_at(lists:reverse(Us), $:),
+ throw_error(L, {"reference '[~s:...' ended unexpectedly", [Ps]}).
+
+
+push_uri(Us, Ss, As) ->
+ ">a/<" ++ Ss ++ ">\"pot_\"=tegrat \"" ++ Us ++ "\"=ferh a<" ++ As.
+
+
+strip_empty_lines(Cs) ->
+ strip_empty_lines(Cs, 0).
+
+strip_empty_lines(Cs, N) ->
+ {Cs1, Cs2} = edoc_lib:split_at(Cs, $\n),
+ case edoc_lib:is_space(Cs1) of
+ true ->
+ strip_empty_lines(Cs2, N + 1);
+ false ->
+ {Cs, N}
+ end.
+
+
+%% Scanning element content for paragraph breaks (empty lines).
+%% Paragraphs are flushed by block level elements.
+
+par(Es) ->
+ par(Es, [], []).
+
+par([E=#xmlText{value = Value} | Es], As, Bs) ->
+ par_text(Value, As, Bs, E, Es);
+par([E=#xmlElement{name = Name} | Es], As, Bs) ->
+ %% (Note that paragraphs may not contain any further block-level
+ %% elements, including other paragraphs. Tables get complicated.)
+ case Name of
+ 'p' -> par_flush(Es, [E | As], Bs);
+ 'hr' -> par_flush(Es, [E | As], Bs);
+ 'h1' -> par_flush(Es, [E | As], Bs);
+ 'h2' -> par_flush(Es, [E | As], Bs);
+ 'h3' -> par_flush(Es, [E | As], Bs);
+ 'h4' -> par_flush(Es, [E | As], Bs);
+ 'h5' -> par_flush(Es, [E | As], Bs);
+ 'h6' -> par_flush(Es, [E | As], Bs);
+ 'pre' -> par_flush(Es, [E | As], Bs);
+ 'address' -> par_flush(Es, [E | As], Bs);
+ 'div' -> par_flush(Es, [par_elem(E) | As], Bs);
+ 'blockquote' -> par_flush(Es, [par_elem(E) | As], Bs);
+ 'form' -> par_flush(Es, [par_elem(E) | As], Bs);
+ 'fieldset' -> par_flush(Es, [par_elem(E) | As], Bs);
+ 'noscript' -> par_flush(Es, [par_elem(E) | As], Bs);
+ 'ul' -> par_flush(Es, [par_subelem(E) | As], Bs);
+ 'ol' -> par_flush(Es, [par_subelem(E) | As], Bs);
+ 'dl' -> par_flush(Es, [par_subelem(E) | As], Bs);
+ 'table' -> par_flush(Es, [par_subelem(E) | As], Bs);
+ _ -> par(Es, [E | As], Bs)
+ end;
+par([E | Es], As, Bs) ->
+ par(Es, [E | As], Bs);
+par([], As, Bs) ->
+ lists:reverse(As ++ Bs).
+
+par_text(Cs, As, Bs, E, Es) ->
+ case ptxt(Cs) of
+ none ->
+ %% no blank lines: keep this element as it is
+ par(Es, [E | As], Bs);
+ {Cs1, Ss, Cs2} ->
+ Es1 = case Cs1 of
+ [] -> lists:reverse(As);
+ _ -> lists:reverse(As, [E#xmlText{value = Cs1}])
+ end,
+ Bs0 = case Es1 of
+ [] -> Bs;
+ _ -> [#xmlElement{name = p, content = Es1} | Bs]
+ end,
+ Bs1 = case Ss of
+ [] -> Bs0;
+ _ -> [#xmlText{value = Ss} | Bs0]
+ end,
+ case Cs2 of
+ [] ->
+ par(Es, [], Bs1);
+ _ ->
+ par_text(Cs2, [], Bs1, #xmlText{value = Cs2}, Es)
+ end
+ end.
+
+par_flush(Es, As, Bs) ->
+ par(Es, [], As ++ Bs).
+
+par_elem(E) ->
+ E#xmlElement{content = par(E#xmlElement.content)}.
+
+%% Only process content of subelements; ignore immediate content.
+par_subelem(E) ->
+ E#xmlElement{content = par_subelem_1(E#xmlElement.content)}.
+
+par_subelem_1([E=#xmlElement{name = Name} | Es]) ->
+ E1 = case par_skip(Name) of
+ true ->
+ E;
+ false ->
+ case par_sub(Name) of
+ true ->
+ par_subelem(E);
+ false ->
+ par_elem(E)
+ end
+ end,
+ [E1 | par_subelem_1(Es)];
+par_subelem_1([E | Es]) ->
+ [E | par_subelem_1(Es)];
+par_subelem_1([]) ->
+ [].
+
+par_skip('caption') -> true;
+par_skip('col') -> true;
+par_skip('colgroup') -> true;
+par_skip(_) -> false.
+
+par_sub(tr) -> true;
+par_sub(thead) -> true;
+par_sub(tfoot) -> true;
+par_sub(tbody) -> true;
+par_sub(_) -> false.
+
+
+%% scanning text content for a blank line
+
+ptxt(Cs) ->
+ ptxt(Cs, []).
+
+ptxt([$\n | Cs], As) ->
+ ptxt_1(Cs, As, [$\n]);
+ptxt([C | Cs], As) ->
+ ptxt(Cs, [C | As]);
+ptxt([], _As) ->
+ none.
+
+%% scanning text following an initial newline
+ptxt_1([C=$\s | Cs], As, Ss) ->
+ ptxt_1(Cs, As, [C | Ss]);
+ptxt_1([C=$\t | Cs], As, Ss) ->
+ ptxt_1(Cs, As, [C | Ss]);
+ptxt_1([C=$\n | Cs], As, Ss) ->
+ %% blank line detected
+ ptxt_2(Cs, As, [C | Ss]);
+ptxt_1(Cs, As, Ss) ->
+ %% not a blank line
+ ptxt(Cs, lists:reverse(Ss, As)).
+
+%% collecting whitespace following a blank line
+ptxt_2([C=$\s | Cs], As, Ss) ->
+ ptxt_2(Cs, As, [C | Ss]);
+ptxt_2([C=$\t | Cs], As, Ss) ->
+ ptxt_2(Cs, As, [C | Ss]);
+ptxt_2([C=$\n | Cs], As, Ss) ->
+ ptxt_2(Cs, As, [C | Ss]);
+ptxt_2(Cs, As, Ss) ->
+ %% ended by non-whitespace or end of element
+ case edoc_lib:is_space(As) of
+ true ->
+ {[], lists:reverse(Ss ++ As), Cs};
+ false ->
+ {lists:reverse(As), lists:reverse(Ss), Cs}
+ end.
+
+
+-spec throw_error(non_neg_integer(), {string(), [_]}) -> no_return().
+
+throw_error(L, D) ->
+ throw({error, L, D}).
diff --git a/lib/edoc/src/otpsgml_layout.erl b/lib/edoc/src/otpsgml_layout.erl
new file mode 100644
index 0000000000..45f74b299e
--- /dev/null
+++ b/lib/edoc/src/otpsgml_layout.erl
@@ -0,0 +1,853 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @author Kenneth Lundin <[email protected]>
+%% @copyright 2001-2004 Richard Carlsson
+%% @see edoc_layout
+%% @end
+%% =====================================================================
+
+%% @doc The OTP SGML layout module for EDoc. See the module {@link edoc}
+%% for details on usage.
+
+%% Note that this is written so that it is *not* depending on edoc.hrl!
+
+-module(otpsgml_layout).
+
+-export([module/2, package/2, overview/2,type/1]).
+
+-import(edoc_report, [report/2]).
+
+-include("xmerl.hrl").
+
+-define(SGML_EXPORT, xmerl_otpsgml).
+-define(DEFAULT_XML_EXPORT, ?SGML_EXPORT).
+-define(STYLESHEET, "stylesheet.css").
+-define(NL, "\n").
+-define(DESCRIPTION_TITLE, "Description").
+-define(DESCRIPTION_LABEL, "description").
+-define(DATA_TYPES_TITLE, "Data Types").
+-define(DATA_TYPES_LABEL, "types").
+-define(FUNCTION_INDEX_TITLE, "Function Index").
+-define(FUNCTION_INDEX_LABEL, "index").
+-define(FUNCTIONS_TITLE, "Function Details").
+-define(FUNCTIONS_LABEL, "functions").
+
+
+%% @doc The layout function.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {index_columns, integer()@}}
+%% </dt>
+%% <dd>Specifies the number of column pairs used for the function
+%% index tables. The default value is 1.
+%% </dd>
+%% <dt>{@type {stylesheet, string()@}}
+%% </dt>
+%% <dd>Specifies the URI used for referencing the stylesheet. The
+%% default value is `"stylesheet.css"'. If an empty string is
+%% specified, no stylesheet reference will be generated.
+%% </dd>
+%% <dt>{@type {xml_export, Module::atom()@}}
+%% </dt>
+%% <dd>Specifies an {@link //xmerl. `xmerl'} callback module to be
+%% used for exporting the documentation. See {@link
+%% //xmerl/xmerl:export_simple/3} for details.
+%% </dd>
+%% </dl>
+%%
+%% @see edoc:layout/2
+
+-record(opts, {root, stylesheet, index_columns}).
+
+module(Element, Options) ->
+ XML = layout_module(Element, init_opts(Element, Options)),
+ Export = proplists:get_value(xml_export, Options,
+ ?DEFAULT_XML_EXPORT),
+ xmerl:export_simple([XML], Export, []).
+
+% Put layout options in a data structure for easier access.
+
+init_opts(Element, Options) ->
+ R = #opts{root = get_attrval(root, Element),
+ index_columns = proplists:get_value(index_columns,
+ Options, 1)
+ },
+ case proplists:get_value(stylesheet, Options) of
+ undefined ->
+ S = edoc_lib:join_uri(R#opts.root, ?STYLESHEET),
+ R#opts{stylesheet = S};
+ "" ->
+ R; % don't use any stylesheet
+ S when is_list(S) ->
+ R#opts{stylesheet = S};
+ _ ->
+ report("bad value for option `stylesheet'.", []),
+ exit(error)
+ end.
+
+
+%% =====================================================================
+%% XML-BASED LAYOUT ENGINE
+%% =====================================================================
+
+%% We assume that we have expanded XML data.
+
+%% <!ELEMENT module (moduleName, moduleFullName, behaviour*, description?,
+%% author*, version?, since?, copyright?, deprecated?,
+%% see*, reference*, typedecls?, functions)>
+%% <!ATTLIST module
+%% root CDATA #IMPLIED>
+%% <!ELEMENT moduleName (#PCDATA)>
+%% <!ELEMENT moduleFullName (#PCDATA)>
+%% <!ELEMENT behaviour (#PCDATA)>
+%% <!ATTLIST behaviour
+%% href CDATA #IMPLIED>
+%% <!ELEMENT description (briefDescription, fullDescription?)>
+%% <!ELEMENT briefDescription (#PCDATA)>
+%% <!ELEMENT fullDescription (#PCDATA)>
+%% <!ELEMENT author EMPTY>
+%% <!ATTLIST author
+%% name CDATA #REQUIRED
+%% email CDATA #IMPLIED
+%% website CDATA #IMPLIED>
+%% <!ELEMENT version (#PCDATA)>
+%% <!ELEMENT since (#PCDATA)>
+%% <!ELEMENT copyright (#PCDATA)>
+%% <!ELEMENT deprecated (description)>
+%% <!ELEMENT see (#PCDATA)>
+%% <!ATTLIST see
+%% name CDATA #REQUIRED
+%% href CDATA #IMPLIED>
+%% <!ELEMENT reference (#PCDATA)>
+%% <!ELEMENT typedecls (typedecl+)>
+%% <!ELEMENT functions (function+)>
+
+layout_module(#xmlElement{name = module, content = Es}=E, _Opts) ->
+ Name = get_attrval(name, E),
+ Desc = get_content(description, Es),
+ ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Functions = [E || E <- get_content(functions, Es)],
+ SortedFs = lists:sort([{function_name(E), E} || E <- Functions]),
+ Types = get_content(typedecls, Es),
+ SortedTs = lists:sort([{type_name(E), E} || E <- Types]),
+ Header = {header, [
+ ?NL,{title, [Name]},
+ ?NL,{prepared, [""]},
+ ?NL,{responsible, [""]},
+ ?NL,{docno, ["1"]},
+ ?NL,{approved, [""]},
+ ?NL,{checked, [""]},
+ ?NL,{date, [""]},
+ ?NL,{rev, ["A"]},
+ ?NL,{file, [Name++".sgml"]}
+ ]},
+ Module = {module, [Name]},
+ ModuleSummary = {modulesummary, ShortDesc},
+ {Short,Long} = find_first_p(FullDesc,[]),
+ Description = {description, [?NL,{p,Short}|Long]++[?NL|types(SortedTs)]},
+ Funcs = functions(SortedFs),
+ Authors = {authors, authors(Es)},
+ See = sees1(Es),
+ {erlref, [
+ ?NL,Header,
+ ?NL,Module,
+ ?NL,ModuleSummary,
+ ?NL,Description,
+ ?NL,Funcs,
+ ?NL,See,
+ ?NL,Authors
+ ]
+ }.
+
+stylesheet(Opts) ->
+ case Opts#opts.stylesheet of
+ undefined ->
+ [];
+ CSS ->
+ [{link, [{rel, "stylesheet"},
+ {type, "text/css"},
+ {href, CSS}], []},
+ ?NL]
+ end.
+
+% doc_index(FullDesc, Functions, Types) ->
+% case doc_index_rows(FullDesc, Functions, Types) of
+% [] -> [];
+% Rs ->
+% [{ul, [{li, [{a, [{href, local_label(R)}], [T]}]}
+% || {T, R} <- Rs]}]
+% end.
+
+% doc_index_rows(FullDesc, Functions, Types) ->
+% (if FullDesc == [] -> [];
+% true -> [{?DESCRIPTION_TITLE, ?DESCRIPTION_LABEL}]
+% end
+% ++ if Types == [] -> [];
+% true -> [{?DATA_TYPES_TITLE, ?DATA_TYPES_LABEL}]
+% end
+% ++ if Functions == [] -> [];
+% true -> [{?FUNCTION_INDEX_TITLE, ?FUNCTION_INDEX_LABEL},
+% {?FUNCTIONS_TITLE, ?FUNCTIONS_LABEL}]
+% end).
+
+% function_index(Fs, Cols) ->
+% case function_index_rows(Fs, Cols, []) of
+% [] -> [];
+% Rows ->
+% [?NL,
+% {h2, [{a, [{name, ?FUNCTION_INDEX_LABEL}],
+% [?FUNCTION_INDEX_TITLE]}]},
+% ?NL,
+% {table, [{width, "100%"}, {border, 1}], Rows},
+% ?NL]
+% end.
+
+% function_index_rows(Fs, Cols, Title) ->
+% Rows = (length(Fs) + (Cols - 1)) div Cols,
+% (if Title == [] -> [];
+% true -> [{tr, [{th, [{colspan, Cols * 2}, {align, left}],
+% [Title]}]},
+% ?NL]
+% end
+% ++ lists:flatmap(fun index_row/1,
+% edoc_lib:transpose(edoc_lib:segment(Fs, Rows)))).
+
+% index_row(Fs) ->
+% [{tr, lists:flatmap(fun index_col/1, Fs)}, ?NL].
+
+% index_col({Name, F=#xmlElement{content = Es}}) ->
+% [{td, [{valign, "top"}], label_href([Name], F)},
+% {td, index_desc(Es)}].
+
+index_desc(Es) ->
+ Desc = get_content(description, Es),
+ case get_content(briefDescription, Desc) of
+ [] ->
+ equiv(Es); % no description at all if no equiv
+ ShortDesc ->
+ ShortDesc
+ end.
+
+% label_href(Content, F) ->
+% case get_attrval(label, F) of
+% "" -> Content;
+% Ref -> [{a, [{href, local_label(Ref)}], Content}]
+% end.
+
+
+%% <!ELEMENT function (args, typespec?, equiv?, description?, since?,
+%% deprecated?, see*)>
+%% <!ATTLIST function
+%% name CDATA #REQUIRED
+%% arity CDATA #REQUIRED
+%% exported NMTOKEN(yes | no) #REQUIRED
+%% label CDATA #IMPLIED>
+%% <!ELEMENT args (arg*)>
+%% <!ELEMENT arg description?>
+%% <!ATTLIST arg name CDATA #REQUIRED>
+
+
+%% <!ELEMENT equiv (expr, see?)>
+%% <!ELEMENT expr (#PCDATA)>
+
+% functions(Fs) ->
+% Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs),
+% if Es == [] -> [];
+% true ->
+% [?NL,
+% {h2, [{a, [{name, ?FUNCTIONS_LABEL}], [?FUNCTIONS_TITLE]}]},
+% ?NL | Es]
+% end.
+
+functions(Fs) ->
+ Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs),
+ if Es == [] -> [];
+ true ->
+ {funcs, Es}
+ end.
+
+% is_exported(E) ->
+% case get_attrval(exported, E) of
+% "yes" -> true;
+% _ -> false
+% end.
+
+% function(Name, E=#xmlElement{content = Es}) ->
+% ([?NL, {h3, label_anchor([Name], E)}, ?NL]
+% ++ case typespec(get_content(typespec, Es)) of
+% [] ->
+% signature(get_content(arguments, Es),
+% get_text(functionName, Es));
+% Spec -> Spec
+% end
+% ++ equiv(Es)
+% ++ deprecated(Es, "function")
+% ++ fulldesc(Es)
+% ++ since(Es)
+% ++ sees(Es)).
+
+function(_Name, E=#xmlElement{content = Es}) ->
+ TypeSpec = get_content(typespec, Es),
+ [?NL,{func, [ ?NL,
+ {name,
+% case typespec(get_content(typespec, Es)) of
+ case funcheader(TypeSpec) of
+ [] ->
+ signature(get_content(args, Es),
+ get_attrval(name, E));
+ Spec -> Spec
+ end
+ },
+ ?NL,{fsummary, fsummary(Es)},
+% ?NL,{type, local_types(TypeSpec)},
+ ?NL,local_types(TypeSpec),
+ ?NL,{desc, label_anchor(E)++fulldesc(Es)++sees(Es)}
+ ]}].
+
+fsummary([]) -> ["\s"];
+fsummary(Es) ->
+ Desc = get_content(description, Es),
+ case get_content(briefDescription, Desc) of
+ [] ->
+ fsummary_equiv(Es); % no description at all if no equiv
+ ShortDesc ->
+ ShortDesc
+ end.
+
+
+fsummary_equiv(Es) ->
+ case get_content(equiv, Es) of
+ [] -> ["\s"];
+ Es1 ->
+ case get_content(expr, Es1) of
+ [] -> ["\s"];
+ [Expr] ->
+ ["Equivalent to ", Expr, ".",?NL]
+ end
+ end.
+
+
+function_name(E) ->
+ get_attrval(name, E) ++ "/" ++ get_attrval(arity, E).
+
+label_anchor(E) ->
+ case get_attrval(label, E) of
+ "" -> [];
+ Ref -> [{marker, [{id, Ref}],[]},?NL]
+ end.
+
+label_anchor(Content, E) ->
+ case get_attrval(label, E) of
+ "" -> Content;
+ Ref -> {p,[{marker, [{id, Ref}],[]},
+ {em, Content}]}
+ end.
+
+%% <!ELEMENT args (arg*)>
+%% <!ELEMENT arg (argName, description?)>
+%% <!ELEMENT argName (#PCDATA)>
+
+%% This is currently only done for functions without type spec.
+
+signature(Es, Name) ->
+% [{tt, [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()", ?NL]}].
+ [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()", ?NL].
+
+arg(#xmlElement{content = Es}) ->
+ [get_text(argName, Es)].
+
+%% <!ELEMENT typespec (erlangName, type, localdef*)>
+
+% typespec([]) -> [];
+% typespec(Es) ->
+% [{p, ([{tt, ([t_name(get_elem(qualifiedName, Es))]
+% ++ t_type(get_content(type, Es)))}]
+% ++ local_defs(get_elem(definition, Es)))},
+% ?NL].
+
+funcheader([]) -> [];
+funcheader(Es) ->
+ [t_name(get_elem(erlangName, Es))] ++ t_utype(get_elem(type, Es)).
+
+local_types([]) -> [];
+local_types(Es) ->
+ local_defs2(get_elem(localdef, Es)).
+
+local_defs2([]) -> [];
+local_defs2(Es) ->
+ {type,[?NL | [{v, localdef(E)} || E <- Es]]}.
+
+%% <!ELEMENT typedecl (typedef, description?)>
+%% <!ELEMENT typedef (erlangName, argtypes, type?, localdef*)>
+
+types([]) -> [];
+types(Ts) ->
+ Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E) end, Ts),
+ [?NL,
+% {h2, [{a, [{name, ?DATA_TYPES_LABEL}],
+% [?DATA_TYPES_TITLE]}]},
+% ?NL | Es]
+ {p,[{marker, [{id, ?DATA_TYPES_LABEL}],[]},
+ {em,[?DATA_TYPES_TITLE]}]},
+ ?NL, {taglist,[?NL|Es]}].
+
+%%type(Name, E=#xmlElement{content = Es}) ->
+%% ([?NL, {h3, label_anchor([Name, "()"], E)}, ?NL]
+%% ++ [{p, typedef(get_content(typedef, Es))}, ?NL]
+%% ++ fulldesc(Es)).
+typedecl(_Name, #xmlElement{content = Es}) ->
+ [{tag, typedef(get_content(typedef, Es))},?NL,{item,fulldesc(Es)},?NL].
+
+
+type_name(#xmlElement{content = Es}) ->
+ t_name(get_elem(erlangName, get_content(typedef, Es))).
+
+typedef(Es) ->
+ Name = ([t_name(get_elem(erlangName, Es)), "("]
+ ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
+ (case get_elem(type, Es) of
+ [] -> [{b, ["abstract datatype"]}, ": ", {tt, Name}];
+ Type ->
+ [{tt, Name ++ [" = "] ++ t_utype(Type)}]
+ end
+ ++ local_defs(get_elem(localdef, Es))).
+
+local_defs([]) -> [];
+local_defs(Es) ->
+ [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}].
+
+localdef(E = #xmlElement{content = Es}) ->
+ (case get_elem(typevar, Es) of
+ [] ->
+ label_anchor(t_abstype(get_content(abstype, Es)), E);
+ [V] ->
+ t_var(V)
+ end
+ ++ [" = "] ++ t_utype(get_elem(type, Es))).
+
+fulldesc(Es) ->
+ case get_content(fullDescription, get_content(description, Es)) of
+% [] -> [?NL];
+ [] -> index_desc(Es);
+% Desc -> [{p, Desc}, ?NL]
+ Desc ->
+ {Short,Long} = find_first_p(Desc,[]),
+ [?NL,{p,Short}|Long] ++[?NL]
+ end.
+
+find_first_p([#xmlElement{name=p}|_]=Long,Short) ->
+ {lists:reverse(Short),Long};
+find_first_p([H|T],Short) ->
+ find_first_p(T,[H|Short]);
+find_first_p([],Short) ->
+ {lists:reverse(Short),[]}.
+
+
+sees1(Es) ->
+ case get_elem(see, Es) of
+ [] -> [];
+ Es1 ->
+ {section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]}
+ end.
+
+sees(Es) ->
+ case get_elem(see, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{em, ["See also:"]}, " "] ++ seq(fun see/1, Es1, ["."])},
+ ?NL]
+ end.
+
+see(E=#xmlElement{content = Es}) ->
+ see(E,Es).
+
+see(E, Es) ->
+ case get_attrval(href, E) of
+ "" -> Es;
+ Ref ->
+ case lists:reverse(Ref) of
+ "lmgs.ppa_"++Ppa ->
+ App = lists:reverse(Ppa),
+ [{seealso, [{marker, App++"_app"}], [App]},"(6)"];
+ "lmgs."++Dom ->
+ Mod = lists:reverse(Dom),
+ [{seealso, [{marker, Mod}], [Mod]},"(3)"];
+ _ ->
+ [{seealso, [{marker, Ref}], Es}]
+ end
+ end.
+
+equiv(Es) ->
+ case get_content(equiv, Es) of
+ [] -> ["\s"];
+ Es1 ->
+ case get_content(expr, Es1) of
+ [] -> [];
+ [Expr] ->
+% Expr1 = {tt, [Expr]},
+% Expr1 = {c, [Expr]},
+ Expr1 = [Expr],
+ Expr2 = case get_elem(see, Es1) of
+ [] ->
+ {c,Expr1};
+ [E=#xmlElement{}] ->
+% see(E,Expr1)
+ case get_attrval(href, E) of
+ "" ->
+ {c,Expr1};
+ Ref ->
+ {seealso, [{marker, Ref}], Expr1}
+ end
+ end,
+ [{p, ["Equivalent to ", Expr2, "."]}, ?NL]
+ end
+ end.
+
+% replace_minus_with_percent([$-|T]) ->
+% [$%|T];
+% replace_minus_with_percent([H|T]) ->
+% [H|replace_minus_with_percent(T)].
+
+copyright(Es) ->
+ case get_content(copyright, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, ["Copyright \251 " | Es1]}, ?NL]
+ end.
+
+version(Es) ->
+ case get_content(version, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["Version:"]}, " " | Es1]}, ?NL]
+ end.
+
+since(Es) ->
+ case get_content(since, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["Introduced in:"]}, " " | Es1]}, ?NL]
+ end.
+
+deprecated(Es, S) ->
+ Es1 = get_content(description, get_content(deprecated, Es)),
+ case get_content(fullDescription, Es1) of
+ [] -> [];
+ Es2 ->
+ [{p, [{b, ["This " ++ S ++ " is deprecated:"]}, " " | Es2]},
+ ?NL]
+ end.
+
+% behaviours(Es) ->
+% case get_elem(behaviour, Es) of
+% [] -> [];
+% Es1 ->
+% [{p, [{b, ["Behaviour:"]}, " "] ++ seq(fun behaviour/1, Es1, ["."])},
+% ?NL]
+% end.
+
+% behaviour(E=#xmlElement{content = Es}) ->
+% case get_attrval(href, E) of
+% "" -> [{tt, Es}];
+% Ref -> [{a, [{href, Ref}], [{tt, Es}]}]
+% end.
+
+authors(Es) ->
+ case get_elem(author, Es) of
+ [] -> [?NL,{aname,["\s"]},?NL,{email,["\s"]}];
+ Es1 -> [?NL|seq(fun author/1, Es1, [])]
+%
+% [{p, [{b, ["Authors:"]}, " "] ++ seq(fun author/1, Es1, ["."])},
+% ?NL]
+ end.
+
+
+%% <!ATTLIST author
+%% name CDATA #REQUIRED
+%% email CDATA #IMPLIED
+%% website CDATA #IMPLIED>
+
+author(E=#xmlElement{}) ->
+ Name = case get_attrval(name, E) of
+ [] -> "\s";
+ N -> N
+ end,
+ Mail = case get_attrval(email, E) of
+ [] -> "\s";
+ M -> M
+ end,
+ [?NL,{aname,[Name]},?NL,{email,[Mail]}].
+
+% author(E=#xmlElement{}) ->
+% Name = get_attrval(name, E),
+% Mail = get_attrval(email, E),
+% URI = get_attrval(website, E),
+% (if Name == Mail ->
+% [{a, [{href, "mailto:" ++ Mail}],[{tt, [Mail]}]}];
+% true ->
+% if Mail == "" -> [Name];
+% true -> [Name, " (", {a, [{href, "mailto:" ++ Mail}],
+% [{tt, [Mail]}]}, ")"]
+% end
+% end
+% ++ if URI == "" -> [];
+% true -> [" [", {em, ["web site:"]}, " ",
+% {tt, [{a, [{href, URI}], [URI]}]}, "]"]
+% end).
+
+references(Es) ->
+ case get_elem(reference, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["References"]},
+ {ul, [{li, C} || #xmlElement{content = C} <- Es1]}]},
+ ?NL]
+ end.
+
+t_name([E]) ->
+ N = get_attrval(name, E),
+ case get_attrval(module, E) of
+ "" -> N;
+ M ->
+ S = M ++ ":" ++ N,
+ case get_attrval(app, E) of
+ "" -> S;
+ A -> "//" ++ A ++ "/" ++ S
+ end
+ end.
+
+t_utype([E]) ->
+ t_utype_elem(E).
+
+t_utype_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> t_type(Es);
+ Name ->
+ T = t_type(Es),
+ case T of
+ [Name] -> T; % avoid generating "Foo::Foo"
+ T -> [Name] ++ ["::"] ++ T
+ end
+ end.
+
+t_type([E=#xmlElement{name = typevar}]) ->
+ t_var(E);
+t_type([E=#xmlElement{name = atom}]) ->
+ t_atom(E);
+t_type([E=#xmlElement{name = integer}]) ->
+ t_integer(E);
+t_type([E=#xmlElement{name = float}]) ->
+ t_float(E);
+t_type([#xmlElement{name = nil}]) ->
+ t_nil();
+t_type([#xmlElement{name = list, content = Es}]) ->
+ t_list(Es);
+t_type([#xmlElement{name = tuple, content = Es}]) ->
+ t_tuple(Es);
+t_type([#xmlElement{name = 'fun', content = Es}]) ->
+ t_fun(Es);
+t_type([E = #xmlElement{name = abstype, content = Es}]) ->
+ T = t_abstype(Es),
+% see(E,T);
+ case get_attrval(href, E) of
+ "" -> T;
+ % Ref -> [{seealso, [{marker, Ref}], T}]
+ _Ref -> T
+ end;
+t_type([#xmlElement{name = union, content = Es}]) ->
+ t_union(Es).
+
+t_var(E) ->
+ [get_attrval(name, E)].
+
+
+t_atom(E) ->
+ [get_attrval(value, E)].
+
+t_integer(E) ->
+ [get_attrval(value, E)].
+
+t_float(E) ->
+ [get_attrval(value, E)].
+
+t_nil() ->
+ ["[]"].
+
+t_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"].
+
+t_tuple(Es) ->
+ ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+
+t_fun(Es) ->
+ ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
+ [") -> "] ++ t_utype(get_elem(type, Es))).
+
+t_abstype(Es) ->
+% ([t_name(get_elem(qualifiedName, Es)), "("]
+% ++ seq(fun t_type_elem/1, get_elem(type, Es), [")"])).
+ case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of
+ {Mod,Type} ->
+ [Type, "("] ++
+ seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) ++
+ [" (see module ", Mod, ")"];
+ Type ->
+ [Type, "("] ++
+ seq(fun t_utype_elem/1, get_elem(type, Es), [")"])
+ end.
+
+%% Split at one colon, but not at two (or more)
+split_at_colon([$:,$:|_]=Rest,Acc) ->
+ lists:reverse(Acc)++Rest;
+split_at_colon([$:|Type],Acc) ->
+ {lists:reverse(Acc),Type};
+split_at_colon([Char|Rest],Acc) ->
+ split_at_colon(Rest,[Char|Acc]);
+split_at_colon([],Acc) ->
+ lists:reverse(Acc).
+
+% t_par(Es) ->
+% T = t_type(get_content(type, Es)),
+% case get_elem(variable, Es) of
+% [] -> T;
+% [V0] -> case t_variable(V0) of
+% T -> T;
+% V -> V ++ ["::"] ++ T
+% end
+% end.
+
+% t_par_elem(#xmlElement{content = Es}) -> t_par(Es).
+
+t_union(Es) ->
+ seq(fun t_utype_elem/1, Es, " | ", []).
+
+seq(F, Es) ->
+ seq(F, Es, []).
+
+seq(F, Es, Tail) ->
+ seq(F, Es, ", ", Tail).
+
+seq(F, [E], _Sep, Tail) ->
+ F(E) ++ Tail;
+seq(F, [E | Es], Sep, Tail) ->
+ F(E) ++ [Sep] ++ seq(F, Es, Sep, Tail);
+seq(_F, [], _Sep, Tail) ->
+ Tail.
+
+get_elem(Name, [#xmlElement{name = Name} = E | Es]) ->
+ [E | get_elem(Name, Es)];
+get_elem(Name, [_ | Es]) ->
+ get_elem(Name, Es);
+get_elem(_, []) ->
+ [].
+
+get_attr(Name, [#xmlAttribute{name = Name} = A | As]) ->
+ [A | get_attr(Name, As)];
+get_attr(Name, [_ | As]) ->
+ get_attr(Name, As);
+get_attr(_, []) ->
+ [].
+
+get_attrval(Name, #xmlElement{attributes = As}) ->
+ case get_attr(Name, As) of
+ [#xmlAttribute{value = V}] ->
+ V;
+ [] -> ""
+ end.
+
+get_content(Name, Es) ->
+ case get_elem(Name, Es) of
+ [#xmlElement{content = Es1}] ->
+ Es1;
+ [] -> []
+ end.
+
+get_text(Name, Es) ->
+ case get_content(Name, Es) of
+ [#xmlText{value = Text}] ->
+ Text;
+ [] -> ""
+ end.
+
+% local_label(R) ->
+% "#" ++ R.
+
+xml(Title, CSS, Body) ->
+ {html, [?NL,
+ {head, [?NL,
+ {title, [Title]},
+ ?NL] ++ CSS},
+ ?NL,
+ {body, [{bgcolor, "white"}], Body},
+ ?NL]
+ }.
+
+%% ---------------------------------------------------------------------
+
+ type(E) ->
+ type(E, []).
+
+% type(E, Ds) ->
+% xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds),
+% ?HTML_EXPORT).
+ type(E, Ds) ->
+ xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds),
+ ?SGML_EXPORT).
+
+
+package(E=#xmlElement{name = package, content = Es}, Options) ->
+ Opts = init_opts(E, Options),
+ Name = get_text(packageName, Es),
+ Title = io_lib:fwrite("Package ~s", [Name]),
+ Desc = get_content(description, Es),
+% ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Body = ([?NL, {h1, [Title]}, ?NL]
+% ++ ShortDesc
+ ++ copyright(Es)
+ ++ deprecated(Es, "package")
+ ++ version(Es)
+ ++ since(Es)
+ ++ authors(Es)
+ ++ references(Es)
+ ++ sees(Es)
+ ++ FullDesc),
+ XML = xml(Title, stylesheet(Opts), Body),
+ xmerl:export_simple([XML], ?SGML_EXPORT, []).
+
+overview(E=#xmlElement{name = overview, content = Es}, Options) ->
+ Opts = init_opts(E, Options),
+ Title = get_text(title, Es),
+ Desc = get_content(description, Es),
+% ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Body = ([?NL, {h1, [Title]}, ?NL]
+% ++ ShortDesc
+ ++ copyright(Es)
+ ++ version(Es)
+ ++ since(Es)
+ ++ authors(Es)
+ ++ references(Es)
+ ++ sees(Es)
+ ++ FullDesc),
+ XML = xml(Title, stylesheet(Opts), Body),
+ xmerl:export_simple([XML], ?SGML_EXPORT, []).
diff --git a/lib/edoc/test/Makefile b/lib/edoc/test/Makefile
new file mode 100644
index 0000000000..4ce9799f6d
--- /dev/null
+++ b/lib/edoc/test/Makefile
@@ -0,0 +1,66 @@
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ edoc_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/edoc_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \
+ >> $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES) $(GEN_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) edoc.spec $(RELSYSDIR)
+ chmod -f -R u+w $(RELSYSDIR)
+ @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+
+release_docs_spec:
diff --git a/lib/edoc/test/edoc.spec b/lib/edoc/test/edoc.spec
new file mode 100644
index 0000000000..8443a28028
--- /dev/null
+++ b/lib/edoc/test/edoc.spec
@@ -0,0 +1 @@
+{topcase, {dir, "../edoc_test"}}.
diff --git a/lib/edoc/test/edoc_SUITE.erl b/lib/edoc/test/edoc_SUITE.erl
new file mode 100644
index 0000000000..ea833f89b2
--- /dev/null
+++ b/lib/edoc/test/edoc_SUITE.erl
@@ -0,0 +1,52 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(edoc_SUITE).
+
+-include("test_server.hrl").
+
+%% Test server specific exports
+-export([all/1]).
+
+%% Test cases
+-export([build_std/1]).
+
+all(suite) ->
+ [build_std].
+
+build_std(suite) ->
+ [];
+build_std(doc) ->
+ ["Build some documentation using standard EDoc layout"];
+build_std(Config) when is_list(Config) ->
+
+ ?line DataDir = ?config(data_dir, Config),
+ ?line Overview1 = filename:join(DataDir, "overview.edoc"),
+ ?line Overview2 = filename:join(DataDir, "overview.syntax_tools"),
+ ?line PrivDir = ?config(priv_dir, Config),
+
+ ?line ok = edoc:application(edoc, [{overview, Overview1},
+ {def, {vsn,"TEST"}},
+ {dir, PrivDir}]),
+
+ ?line ok = edoc:application(syntax_tools, [{overview, Overview2},
+ {def, {vsn,"TEST"}},
+ {dir, PrivDir}]),
+
+ ?line ok = edoc:application(xmerl, [{dir, PrivDir}]),
+
+ ok.
diff --git a/lib/edoc/test/edoc_SUITE_data/overview.edoc b/lib/edoc/test/edoc_SUITE_data/overview.edoc
new file mode 100644
index 0000000000..0da5a21759
--- /dev/null
+++ b/lib/edoc/test/edoc_SUITE_data/overview.edoc
@@ -0,0 +1,910 @@
+ -*- html -*-
+
+ EDoc overview page
+
+
+@author Richard Carlsson <[email protected]>
+@copyright 2003-2006 Richard Carlsson
+@version {@vsn}
+@title Welcome to EDoc
+
+@doc EDoc is the Erlang program documentation generator. Inspired by the
+Javadoc<sup><font size="-3">TM</font></sup> tool for the Java<sup><font
+size="-3">TM</font></sup> programming language, EDoc is adapted to the
+conventions of the Erlang world, and has several features not found in
+Javadoc.
+
+== Contents ==
+
+<ol>
+ <li>{@section Introduction}</li>
+ <li>{@section Running EDoc}</li>
+ <li>{@section Generic tags}</li>
+ <li>{@section Overview tags}</li>
+ <li>{@section Module tags}</li>
+ <li>{@section Function tags}</li>
+ <li>{@section References}</li>
+ <li>{@section Notes on XHTML}</li>
+ <li>{@section Wiki notation}</li>
+ <li>{@section Macro expansion}</li>
+ <li>{@section Type specifications}</li>
+ <li>{@section Acknowledgements}</li>
+</ol>
+
+== Introduction ==
+
+EDoc lets you write the documentation of an Erlang program as
+comments in the source code itself, using <em>tags</em> on the form
+"`@Name ...'". A source file does not have to contain tags
+for EDoc to generate its documentation, but without tags the result will
+only contain the basic available information that can be extracted from
+the module.
+
+A tag must be the first thing on a comment line, except for leading
+'`%'' characters and whitespace. The comment must be between
+program declarations, and not on the same line as any program text. All
+the following text - including consecutive comment lines - up until the
+end of the comment or the next tagged line, is taken as the
+<em>content</em> of the tag.
+
+Tags are associated with the nearest following program construct "of
+significance" (the module name declaration and function
+definitions). Other constructs are ignored; e.g., in:
+```
+ %% @doc Prints the value X.
+
+ -record(foo, {x, y, z}).
+
+ print(X) -> ...
+'''
+the `@doc' tag is associated with the function `print/1'.
+
+Note that in a comment such as:
+```% % @doc ...'''
+the tag is ignored, because only the first '`%'' character is
+considered "leading". This allows tags to be "commented out".
+
+Some tags, such as `@type', do not need to be associated
+with any program construct. These may be placed at the end of the file,
+in the "footer".
+
+
+== Running EDoc ==
+
+The following are the main functions for running EDoc:
+ <ul>
+ <li>{@link edoc:application/2}: Creates documentation for a
+ typical Erlang application.</li>
+ <li>{@link edoc:packages/2}: Creates documentation for one or
+ more packages, automatically locating source files.</li>
+ <li>{@link edoc:files/2}: Creates documentation for a
+ specified set of source files.</li>
+ <li>{@link edoc:run/3}: General interface function; the common
+ back-end for the above functions. Options are documented here.</li>
+ </ul>
+
+Note that the function {@link edoc:file/2} belongs to the old, deprecated
+interface (from EDoc version 0.1), and should not be used.
+
+
+== Generic tags ==
+
+The following tags can be used anywhere within a module:
+<dl>
+ <dt><a name="gtag-clear">`@clear'</a></dt>
+
+ <dd>This tag causes all tags above it (up to the previous program
+ construct), to be discarded, including the `@clear'
+ tag itself. The text following the tag
+ is also ignored. <em>This is typically only useful in code
+ containing conditional compilation, when preprocessing is turned
+ on.</em> (Preprocessing is turned off by default.) E.g., in
+```-ifdef(DEBUG).
+ %% @doc ...
+ foo(...) -> ...
+ -endif.
+ %% @clear
+
+ %% @doc ...
+ bar(...) -> ...'''
+ the `@clear' tag makes sure that EDoc does not see
+ two `@doc' tags before the function `bar',
+ even if the code for function `foo' is removed by
+ preprocessing. (There is no way for EDoc to see what the first
+ `@doc' tag "really" belongs to, since preprocessing
+ strips away all such information.)</dd>
+
+ <dt><a name="gtag-end">`@end'</a></dt>
+ <dd>The text following this tag is always ignored. Use this to
+ mark the end of the previous tag, when necessary, as e.g. in:
+```%% ----------------------------------
+ %% ...
+ %% @doc ...
+ %% ...
+ %% @end
+ %% ----------------------------------'''
+ to avoid including the last "ruler" line in the
+ `@doc' tag.
+
+ <em>Note: using some other "dummy" `@'-tag for
+ the same purpose might work in a particular implementation of
+ EDoc, but is not guaranteed to. Always use `@end'
+ to ensure future compatibility.</em></dd>
+
+ <dt><a name="gtag-todo">`@todo' (or `@TODO')</a></dt>
+ <dd>Attaches a To-Do note to a function, module, package, or
+ overview-page. The content can be any XHTML text describing
+ the issue, e.g.:
+```%% @TODO Finish writing the documentation.'''
+ or
+```%% @todo Implement <a href="http://www.ietf.org/rfc/rfc2549.txt">RFC 2549</a>.'''
+ To-Do notes are normally not shown unless the `todo' option is
+ turned on (see {@link edoc:get_doc/2}).</dd>
+
+ <dt><a name="gtag-type">`@type'</a></dt>
+ <dd>Documents an abstract data type or type alias. The content
+ consists of a type declaration or definition, optionally
+ followed by a period ('`.'') separator and XHTML
+ text describing the type (i.e., its purpose, use, etc.). There
+ must be at least one whitespace character between the
+ '`.'' and the text. See {@section Type specifications} below
+ for syntax and examples.
+ All data type descriptions are placed in a separate section of
+ the documentation, regardless of where the tags occur.</dd>
+
+</dl>
+
+
+
+== Overview tags ==
+
+The following tags can only be used in an overview file:
+<dl>
+ <dt><a name="otag-title">`@title'</a></dt>
+ <dd>Specifies a title for the overview page. The content
+ can be arbitrary text.</dd>
+</dl>
+
+
+== Module tags ==
+
+The following tags can be used before a module declaration:
+<dl>
+ <dt><a name="mtag-author">`@author'</a></dt>
+ <dd>Specifies the name of an author, along with contact
+ information. An e-mail address can be given within `<...>'
+ delimiters, and a URI within `[...]' delimiters. Both e-mail and
+ URI are optional, and any surrounding whitespace is stripped from
+ all strings.
+
+ The name is the first nonempty string that is not within `<...>'
+ or `[...]', and does not contain only whitespace. (In other words,
+ the name can come before, between, or after the e-mail and URI,
+ but cannot be split up; any sections after the first are ignored.)
+ If an e-mail address is given, but no name, the e-mail string will
+ be used also for the name. If no `<...>' section is present, but
+ the name string contains an '`@'' character, it is assumed to be
+ an e-mail address. Not both name and e-mail may be left out.
+
+ Examples:
+```%% @author Richard Carlsson'''
+
+```%% @author Richard Carlsson <[email protected]>
+ %% [http://user.it.uu.se/~richardc/]'''
+
+```%% @author <[email protected]>'''
+
+```%% @author [email protected] [http://user.it.uu.se/~richardc/]'''
+ </dd>
+
+<dt><a name="mtag-copyright">`@copyright'</a></dt>
+ <dd>Specifies the module copyrights. The content can be
+ arbitrary text; for example:
+```
+ %% @copyright 2001-2003 Richard Carlsson'''
+ </dd>
+
+ <dt><a name="mtag-deprecated">`@deprecated'</a></dt>
+ <dd>Mark the module as deprecated, indicating that it should no
+ longer be used. The content must be well-formed XHTML, and should
+ preferably include a `@{@link}' reference to a
+ replacement; as in:
+```
+ %% @deprecated Please use the module @{@link foo} instead.'''
+ </dd>
+
+ <dt><a name="mtag-doc">`@doc'</a></dt>
+ <dd>Describes the module, using well-formed XHTML text. The
+ first sentence is used as a summary (see the
+ <a href="#ftag-doc">`@doc' function tag</a> below
+ for details). For example.:
+```%% @doc This is a <em>very</em> useful module. It is ...'''</dd>
+
+ <dt><a name="mtag-hidden">`@hidden'</a></dt>
+ <dd>Marks the module so that it will not appear in the
+ documentation (even if "private" documentation is generated).
+ Useful for sample code, test modules, etc. The content can be
+ used as a comment; it is ignored by EDoc.</dd>
+
+ <dt><a name="mtag-private">`@private'</a></dt>
+ <dd>Marks the module as private (i.e., not part of the public
+ interface), so that it will not appear in the normal
+ documentation. (If "private" documentation is generated, the
+ module will be included.) The content can be used as a comment; it
+ is ignored by EDoc.</dd>
+
+ <dt><a name="mtag-reference">`@reference'</a></dt>
+ <dd>Specifies a reference to some arbitrary external resource,
+ such as an article, book, or web site. The content must be
+ well-formed XHTML text. Examples:
+```%% @reference Pratchett, T., <em>Interesting Times</em>,
+ %% Victor Gollancz Ltd, 1994.'''
+
+```%% @reference See <a href="www.google.com">Google</a> for
+ %% more information.'''
+ </dd>
+
+ <dt><a name="mtag-see">`@see'</a></dt>
+ <dd>See the <a href="#ftag-see">`@see' function tag</a>
+ below for details.</dd>
+
+ <dt><a name="mtag-since">`@since'</a></dt>
+ <dd>Specifies when the module was introduced, with respect to
+ the application, package, release or distribution it is part
+ of. The content can be arbitrary text.</dd>
+
+ <dt><a name="mtag-version">`@version'</a></dt>
+ <dd>Specifies the module version. The content can be arbitrary
+ text.</dd>
+
+</dl>
+
+
+
+== Function tags ==
+
+The following tags can be used before a function definition:
+<dl>
+ <dt><a name="ftag-deprecated">`@deprecated'</a></dt>
+ <dd>See the <a href="#mtag-deprecated">`@deprecated'
+ module tag</a> for details.</dd>
+
+ <dt><a name="ftag-doc">`@doc'</a></dt>
+ <dd>XHTML text describing the function. The first
+ sentence of the text is used as a quick summary; this ends at
+ the first period character ('`.'') or exclamation mark
+ ('`!'') that is followed by a whitespace character, a
+ line break, or the end of the tag text, and is not within XML
+ markup. (As an exception, the first sentence may be within an
+ initial paragraph element)</dd>
+
+ <dt><a name="ftag-equiv">`@equiv'</a></dt>
+ <dd>Specify equivalence to another function call/expression.
+ The content must be a proper Erlang expression. If the
+ expression is a function call, a cross-reference to the called
+ function is created automatically. Typically, this tag is used
+ instead of `@doc'. </dd>
+
+ <dt><a name="ftag-hidden">`@hidden'</a></dt>
+ <dd>Marks the function so that it will not appear in the
+ documentation (even if "private" documentation is generated).
+ Useful for debug/test functions, etc. The content can be
+ used as a comment; it is ignored by EDoc.</dd>
+
+ <dt><a name="ftag-private">`@private'</a></dt>
+ <dd>Marks the function as private (i.e., not part of the public
+ interface), so that it will not appear in the normal
+ documentation. (If "private" documentation is generated, the
+ function will be included.) Only useful for exported functions,
+ e.g. entry points for `spawn'. (Non-exported functions are
+ always "private".) The content can be used as a comment; it is
+ ignored by EDoc.</dd>
+
+ <dt><a name="ftag-see">`@see'</a></dt>
+ <dd>Make a reference to a module, function, datatype, or
+ application. (See {@section References} below.)
+ The content consists of a reference, optionally followed by a
+ period ('`.''), one or more whitespace characters, and
+ XHTML text to be used for the label; for example "`@see edoc'" or
+ "`@see edoc. <b>EDoc</b>'". If no label text is specified, the
+ reference itself is used as the label.</dd>
+
+ <dt><a name="ftag-since">`@since'</a></dt>
+ <dd>Specifies in what version of the module the function was
+ introduced; cf. the
+ <a href="#mtag-version">`@version'
+ module tag</a>. The content can be arbitrary text.</dd>
+
+ <dt><a name="ftag-spec">`@spec'</a></dt>
+ <dd>Used to specify the function type; see {@section Type
+ specifications} below for syntax. If the function name is
+ included in the specification, it must match the name in the
+ actual code. When parameter names are not given in the
+ specification, suitable names will be taken from the source
+ code if possible, and otherwise synthesized.</dd>
+
+ <dt><a name="ftag-throws">`@throws'</a></dt>
+ <dd>Specifies which types of terms may be thrown by the
+ function, if its execution terminates abruptly due to a call to
+ `erlang:throw(Term)'. The content is a type expression (see {@section
+ Type specifications}), and can be a union type.
+
+ Note that exceptions of type `exit' (as caused by calls to
+ `erlang:exit(Term)') and `error' (run-time errors such as `badarg'
+ or `badarith') are not viewed as part of the normal interface of
+ the function, and cannot be documented with the `@throws' tag.</dd>
+
+ <dt><a name="ftag-type">`@type'</a></dt>
+ <dd>See the <a href="#gtag-type">`@type' generic tag</a>
+ for details. Placing a `@type' tag by a function
+ definition may be convenient, but does not affect where the
+ description is placed in the generated documentation.</dd>
+</dl>
+
+
+
+== References ==
+
+In several contexts (`@see' tags, `@link' macros, etc.), EDoc lets
+you refer to the generated documentation for modules, functions,
+datatypes, and applications, using a simple and compact syntax. The
+possible formats for references are:
+<table border="1" summary="reference syntax">
+ <tr><th>Reference syntax</th><th>Example</th><th>Scope</th></tr>
+ <tr><td>`Module'</td><td>{@link edoc_run}, `erl.lang.list'</td><td>Global</td></tr>
+ <tr><td>`Package.*'</td><td>`erl.lang.*'</td><td>Global</td></tr>
+ <tr><td>`Function/Arity'</td><td>`file/2'</td><td>Within module</td></tr>
+ <tr><td>`Module:Function/Arity'</td><td>{@link edoc:application/2}</td><td>Global</td></tr>
+ <tr><td>`Type()'</td><td>`filename()'</td><td>Within module</td></tr>
+ <tr><td>`Module:Type()'</td><td>{@link edoc:edoc_module()}</td><td>Global</td></tr>
+ <tr><td>`//Application'</td><td>{@link //edoc}</td><td>Global</td></tr>
+ <tr><td>`//Application/Module'</td><td>{@link //edoc/edoc_doclet}</td><td>Global</td></tr>
+ <tr><td>`//Application/Module:Function/Arity'</td><td>{@link //edoc/edoc_run:file/1}</td><td>Global</td></tr>
+ <tr><td>`//Application/Module:Type()'</td><td>{@link //edoc/edoc:edoc_module()}</td><td>Global</td></tr>
+</table>
+
+
+EDoc will resolve references using the information it finds in
+`edoc-info'-files at the locations specified with the `doc_path'
+option. EDoc will automatically (and somewhat intelligently) try to find
+any local `edoc-info'-files using the current code path, and add them to
+the end of the `doc_path' list. The target doc-directory is also
+searched for an existing info file; this allows documentation to be
+built incrementally. (Use the `new' option to ignore any old info
+file.)
+
+Note that if the name of a module, function or datatype is explicitly
+qualified with an application (as in "`//edoc/edoc_run'"), this
+overrides any other information about that name, and the reference will
+be made relative to the location of the application (if it can be
+found). This makes it possible to refer to e.g. a module "`fred'" as
+"`//foo/fred'" without accidentally getting a reference to
+"`//bar/fred'". You should not use this form of explicit references for
+names that are local to the application you are currently creating -
+they will always be resolved correctly.
+
+Note that module-local references such as `file/2' only work properly
+within a module. In an overview-page like this (i.e., the one you are
+currently reading), no module context is available.
+
+== Notes on XHTML ==
+
+In several places, XHTML markup can be used in the documentation
+text, in particular in `@doc' tags. The main differences from
+HTML are the following:
+<ul>
+ <li>All elements must have explicit start and end tags, and be
+ correctly nested. This means that you cannot e.g. write a
+ `<li>' tag without also writing a corresponding `</li>'
+ tag in the right place. This could be an annoyance
+ at times, but has the great advantage that EDoc can report all
+ malformed XHTML in your source code, rather than propagate the
+ errors to the generated documentation.</li>
+ <li>XHTML tag and attribute names should always be lower-case.</li>
+ <li>Attributes must be quoted, as in e.g. `<a
+ name="top">'.</li>
+</ul>
+To write an element like the HTML `<br>', which has no actual content,
+you can write either the full `<br></br>', or better, use the XHTML
+abbreviated form `<br/>'.
+
+Since the purpose of EDoc is to document programs, there is also a
+limited form of "wiki"-syntax available for making program code easier
+to write inline (and to make the doc-comments easier to read).
+See {@section Wiki notation} below for details.
+
+The HTML heading tags `h1' and `h2' are reserved for use by EDoc.
+Headings in documentation source code should start at `h3'. There is
+however a special syntax for writing headings which avoids using
+specific level numbers altogether; see {@section Headings} below for details.
+
+EDoc uses {@link //xmerl. XMerL} to parse and export XML markup.
+
+
+== Wiki notation ==
+
+When EDoc parses XHTML, it does additional pre- and post-processing of
+the text in order to expand certain notation specific to EDoc into
+proper XHTML markup. This "wiki" ([http://en.wikipedia.org/wiki/Wiki])
+notation is intended to make it easier to write source code
+documentation.
+
+ === Empty lines separate paragraphs ===
+
+Leaving an empty line in XHTML text (i.e., a line which except for
+any leading start-of-comment '<tt>%</tt>' characters contains only
+whitespace), will make EDoc split the text before and
+after the empty line into separate paragraphs. For example:
+```%% @doc This will all be part of the first paragraph.
+ %% It can stretch over several lines and contain <em>any
+ %% XHTML markup</em>.
+ %%
+ %% This is the second paragraph. The above line is
+ %% regarded as "empty" by EDoc, even though it ends with
+ %% a space.'''
+will generate the following text:
+<blockquote><p>This will all be part of the first paragraph. It can
+stretch over several lines and contain <em>any XHTML markup</em>.</p>
+This is the second paragraph. The above line is regarded as "empty" by
+EDoc, even though it ends with a space.</blockquote>
+
+Paragraph splitting takes place after the actual XHTML parsing. It only
+affects block-level text, and not e.g., text within `<pre>' markup, or
+text that is already within `<p>' markup.
+
+ === Headings ===
+
+Section headings, sub-headings, and sub-sub-headings, can be written
+using the following notation:
+```== Heading ==
+ === Sub-heading ===
+ ==== Sub-sub-heading ===='''
+Such a heading must be alone on a line, except for whitespace, and
+cannot be split over several lines. A link target is automatically
+created for the heading, by replacing any whitespace within the text by
+a single underscore character. E.g.,
+```== Concerning Hobbits =='''
+is equivalent to
+```<h3><a name="Concerning_Hobbits">Concerning Hobbits</a></h3>'''
+Thus, headings using this notation should not contain characters that
+may not be part of URL labels, except for whitespace. If you need to
+create such headings, you have to use the explicit XHTML markup.
+
+A hypertext link to a heading written this way can be created using the
+`@section' macro, which transforms the argument text into a label as
+described above. E.g.,
+```@{@section Concerning Hobbits}'''
+is equivalent to writing
+```<a href="#Concerning_Hobbits">Concerning Hobbits</a>'''
+
+The above expansions take place before XML parsing.
+
+ === External links ===
+
+Writing a URL within brackets, as in "`[http://www.w3c.org/]'", will
+generate a hyperlink such as [http://www.w3c.org/], using the URL both
+for the destination and the label of the reference, equivalent to writing
+"`<a href="http://www.w3c.org/"><tt>http://www.w3c.org/</tt></a>'". This
+short-hand keeps external URL references short and readable. The
+recognized protocols are `http', `ftp', and `file'. This expansion takes
+place before XML parsing.
+
+ === Verbatim quoting ===
+
+In XHTML text, the '<code>&#x60;</code>' character (Unicode `000060',
+known as "grave accent" or "back-quote") can be used for verbatim
+quoting. This expansion takes place before XML parsing.
+
+<ul>
+ <li>A character sequence "<code>&#x60;...'</code>" or
+ "<code>&#x60;&#x60;...''</code>" will be expanded to
+ "`<code>...</code>'", where all occurrences of the special XML
+ characters '`<'' and '`&'' (and for completeness, also '`>'') in
+ the quoted text have been escaped to "`&lt;'", "`&amp;'", and
+ "`&gt;'", respectively.
+ All whitespace is stripped from the beginning and end of the
+ quoted text.
+
+ Double back-quotes "<code>&#x60;&#x60;...''</code>" can be used
+ to quote text containing single '`` ' ''' characters. The automatic
+ stripping of any surrounding whitespace makes it possible to write
+ things like "<code>&#x60;&#x60; 'foo@bar' ''</code>".
+
+ To quote text containing "<code>''</code>" verbatim,
+ explicit `<code>' markup or similar must be used.</li>
+
+ <li>A character sequence "<code>&#x60;&#x60;&#x60;...'''</code>"
+ will be expanded to "`<pre><![CDATA[...]]></pre>'", which disables
+ all XML markup within the quoted text, and displays the result in
+ fixed-font with preserved indentation. Whitespace is stripped from
+ the end of the quoted text, but not from the beginning, except for
+ whole leading lines of whitespace. This is
+ useful for multi-line code examples, or displayed
+ one-liners.</li>
+
+ <li>To produce a single '<code>&#x60;</code>'-character in XML
+ without beginning a new quote, you can write "<code>&#x60;'</code>"
+ (no space between the '<code>&#x60;</code>' and the '<code>'</code>').
+ You can of course also use the XML character entity
+ "`&#x60;'".</li>
+</ul>
+
+Examples:
+ ```%% @doc ...where the variable `Foo' refers to... '''
+
+ ```%% @doc ...returns the atom `` '[email protected]' ''... '''
+
+ <pre>
+ %% @doc ...use the command &#x60;&#x60;&#x60;erl -name foo''' to...</pre>
+
+ <pre>
+ %% @doc ...as in the following code:
+ %% &#x60;&#x60;&#x60;f(X) ->
+ %% case X of
+ %% ...
+ %% end'''</pre>
+
+ <pre>
+ %% @doc ...or in the following:
+ %% &#x60;&#x60;&#x60;
+ %% g(X) ->
+ %% fun () -> ... end
+ %% '''</pre>
+
+
+== Macro expansion ==
+
+Before the content of a tag is parsed, the text undergoes <em>macro
+expansion</em>. The syntax for macro calls is:
+<pre>
+ @{@<em>name</em>}</pre>
+or
+<pre>
+ @{@<em>name</em> <em>argument</em>}</pre>
+where <em>name</em> and <em>argument</em> are separated by one or more
+whitespace characters. The argument can be any text, which may contain
+other macro calls. The number of non-escaped "<code>@{@</code>" and
+"`}'" delimiters must be balanced.
+
+ The argument text is first expanded in the current environment, and
+the result is bound to the <em>macro parameter</em>, written
+<code>@{@?}</code>. (If no argument is given, <code>@{@?}</code> is
+bound to the empty string.) The macro definition is then substituted
+for the call, and expansion continues over the resulting text. Recursive
+macro expansions are not allowed.
+
+ === User-defined macros ===
+
+Users can define their own macros by using the `def' EDoc
+option; see {@link edoc:file/2} and {@link edoc:get_doc/2} for more
+information.
+
+ === Predefined macros ===
+
+<dl>
+ <dt><a name="predefmacro-date"><code>@{@date}</code></a></dt>
+ <dd>Expands to the current date, as "<tt>Month Day Year</tt>",
+ e.g. "{@date}".</dd>
+
+ <dt><a name="predefmacro-docRoot"><code>@{@docRoot}</code></a></dt>
+ <dd>Expands to the relative URL path (such as
+ `"../../.."') from the current page to the root
+ directory of the generated documentation. This can be used to
+ create XHTML references such as `<img
+ src="@{@docRoot}/images/logo.jpeg">' that are independent of how
+ deep down in a package structure they occur. If packages are not
+ used (i.e., if all modules are in the "empty" package),
+ <code>@{@docRoot}</code> will always resolve to the empty
+ string.</dd>
+
+ <dt><a name="predefmacro-link"><code>@{@link <em>reference</em>.
+ <em>description</em>}</code></a></dt>
+ <dd>This creates a hypertext link; cf. the
+ <a href="#ftag-see">`@see' function tag</a> above for
+ details. The description text (including the period separator)
+ is optional; if no text is given, the reference itself is
+ used. For example, <code>@{@link edoc:file/2}</code> creates the
+ link {@link edoc:file/2}, and `@{@link edoc:file/2. <em>this link</em>}'
+ creates {@link edoc:file/2. <em>this link</em>}.</dd>
+
+ <dt><a name="predefmacro-module"><code>@{@module}</code></a></dt>
+ <dd>Expands to the name of the current module. Only defined when a
+ module is being processed.</dd>
+
+ <dt><a name="predefmacro-package"><code>@{@package}</code></a></dt>
+ <dd>Expands to the name of the current package.</dd>
+
+ <dt><a name="predefmacro-section"><code>@{@section
+ <em>heading</em>}</code></a></dt>
+ <dd>Expands to a hypertext link to the specified section heading;
+ see {@section Headings} for more information.</dd>
+
+ <dt><a name="predefmacro-type"><code>@{@type
+ <em>type-expression</em>}</code></a></dt>
+ <dd>Formats a type expression within `<code>...</code>'
+ markup and with hypertext links for data types. For example,
+ <code>@{@type {options, List::edoc:option_list()@@}}</code>
+ generates "{@type {options, List::edoc:option_list()@}}". (Cf.
+ {@section Escape sequences} below.)</dd>
+
+ <dt><a name="predefmacro-time"><code>@{@time}</code></a></dt>
+ <dd>Expands to the current time, as "<tt>Hr:Min:Sec</tt>",
+ e.g. "{@time}".</dd>
+</dl>
+
+ === Escape sequences ===
+
+To prevent certain characters from being interpreted as delimiters,
+for example to produce the text "<code>@{@</code>" in the output, or use a
+'`}'' character in the argument text of a macro call, the
+following escape sequences may be used: <dl>
+ <dt><code>@@{</code></dt>
+ <dd>Expands to "`{'". Example:
+```
+ %% @doc A macro call starts with the sequence "@@@{@".'''
+ </dd>
+ <dt><code>@@}</code></dt>
+ <dd>Expands to "`}'". Example:
+```
+ %% @doc ...@{@foo ...{Key, Value@@}...}...'''
+ </dd>
+ <dt><code>@@@@</code></dt>
+ <dd>Expands to "`@'". Example:
+```
+ %% @doc Contact us at support@@@@@{@hostname}'''
+ Will generate the text "Contact us at [email protected]"
+ if the macro `hostname' is bound to
+ "`vaporware.acme.com'". Also:
+```
+ %% @doc You might want to write something like
+ %% @@@@foo that will expand to @@foo and does not start
+ %% a new tag even if it appears first in a line.'''
+ </dd>
+</dl>
+
+
+== Type specifications ==
+
+ === Function specifications ===
+
+The following grammar describes the form of the specifications
+following a `@spec' tag:
+
+<table summary="specification syntax grammar">
+ <tr>
+ <td><code>Spec</code></td>
+ <td>::=</td>
+ <td><code>FunType Def*
+ <br/>| FunctionName FunType Def*</code></td>
+ </tr>
+ <tr>
+ <td><code>FunctionName</code></td>
+ <td>::=</td>
+ <td><code>Atom</code></td>
+ </tr>
+ <tr>
+ <td><code>FunType</code></td>
+ <td>::=</td>
+ <td><code>"(" UnionTypes? ")" "->" UnionType</code></td>
+ </tr>
+ <tr>
+ <td><code>UnionTypes</code></td>
+ <td>::=</td>
+ <td><code>UnionType
+ <br/>| UnionType "," UnionTypes</code></td>
+ </tr>
+ <tr>
+ <td><code>UnionType</code></td>
+ <td>::=</td>
+ <td><code>UnionList
+ <br/>| Name "::" UnionList</code></td>
+ </tr>
+ <tr>
+ <td><code>Name</code></td>
+ <td>::=</td>
+ <td><code>Variable</code></td>
+ </tr>
+ <tr>
+ <td><code>UnionList</code></td>
+ <td>::=</td>
+ <td><code>Type
+ <br/>| Type "+" UnionList
+ <br/>| Type "|" UnionList</code></td>
+ </tr>
+ <tr>
+ <td><code>Type</code></td>
+ <td>::=</td>
+ <td><code>TypeVariable
+ <br/>| Atom
+ <br/>| Integer
+ <br/>| Float
+ <br/>| FunType
+ <br/>| "{" UnionTypes? "}"
+ <br/>| "[" "]"
+ <br/>| "[" UnionType "]"
+ <br/>| TypeName "(" UnionTypes? ")"
+ <br/>| ModuleName ":" TypeName "(" UnionTypes? ")"
+ <br/>| "//" AppName "/" ModuleName ":" TypeName "(" UnionTypes? ")"</code></td>
+ </tr>
+ <tr>
+ <td><code>TypeVariable</code></td>
+ <td>::=</td>
+ <td><code>Variable</code></td>
+ </tr>
+ <tr>
+ <td><code>TypeName</code></td>
+ <td>::=</td>
+ <td><code>Atom</code></td>
+ </tr>
+ <tr>
+ <td><code>ModuleName</code></td>
+ <td>::=</td>
+ <td><code>Atom
+ <br/>| ModuleName "." Atom</code></td>
+ </tr>
+ <tr>
+ <td><code>AppName</code></td>
+ <td>::=</td>
+ <td><code>Atom</code></td>
+ </tr>
+ <tr>
+ <td><code>Def</code></td>
+ <td>::=</td>
+ <td><code>TypeVariable "=" UnionType
+ <br/>| TypeName "(" TypeVariables? ")" "=" UnionType</code></td>
+ </tr>
+ <tr>
+ <td><code>TypeVariables</code></td>
+ <td>::=</td>
+ <td><code>TypeVariable
+ <br/>| TypeVariable "," TypeVariables</code></td>
+ </tr>
+</table>
+
+
+Examples:
+```
+ %% @spec my_function(X::integer()) -> integer()'''
+```
+ %% @spec (X::integer()) -> integer()'''
+```
+ %% @spec sqrt(float()) -> float()'''
+```
+ %% @spec pair(S, T) -> {S, T}'''
+```
+ %% @spec append(List, List) -> List
+ %% List = [term()]'''
+```
+ %% @spec append(A::List, B::List) -> List
+ %% List = [term()]'''
+```
+ %% @spec open(File::filename()) -> file_descriptor()
+ %% filename() = string() + atom()'''
+```
+ %% @spec close(graphics:window()) -> ok'''
+
+In the above examples, `X', `A', `B',
+and `File' are parameter names, used for referring to the
+parameters from the documentation text. The <em>type variables</em>
+`S', `T' and `List' are used to
+simplify the type specifications, and may be supplied with
+definitions. It is also possible to give definitions for named types,
+which means that the name is simply an alias. (Use the
+`@type' tag to document abstract data types.) If a named type
+is defined in another module, it can be referred to as
+`Module:TypeName(...)'.
+
+Both the '`|'' and the '`+'' character may be
+used to separate alternatives in union types; there is no semantic
+difference. Note that the notation `[Type]' means "proper
+(nil-terminated) list whose elements all belong to `Type'";
+For example, `[atom()|integer()]' means the same thing as
+`[atom()+integer()]', i.e., a proper list of atoms and/or
+integers.
+
+If only a type variable is given for a parameter, as in
+"`pair(S, T) -> ...'", the same variable name may implicitly
+be used as the parameter name; there is no need to write
+"`pair(S::S, T::T) -> ...'".
+
+EDoc automatically extracts possible parameter names from the source
+code, to be used if no parameter name is given in the specification (or
+if the specification is missing altogether). If this fails, EDoc will
+generate a dummy parameter name, such as `X1'. This way, EDoc
+can often produce helpful documentation even for code that does not
+contain any annotations at all.
+
+ === Type definitions ===
+
+The following grammar (see above for auxiliary definitions) describes
+the form of the definitions that may follow a `@type' tag:
+
+<table summary="type definition grammar">
+ <tr>
+ <td><code>Typedef</code></td>
+ <td>::=</td>
+ <td><code>TypeName "(" TypeVariables? ")" Def*
+ <br/>| TypeName "(" TypeVariables? ")" "=" UnionType Def*</code></td>
+ </tr>
+</table>
+
+(For a truly abstract data type, no equivalence is specified.) The main
+definition may be followed by additional local definitions. Examples:
+```
+ %% @type myList(X). A special kind of lists ...'''
+```
+ %% @type filename() = string(). Atoms not allowed!'''
+```
+ %% @type thing(A) = {thong, A}
+ %% A = term().
+ %% A kind of wrapper type thingy.'''
+
+
+ === Pre-defined data types ===
+
+The following data types are predefined by EDoc, and may not be
+redefined:
+```
+ any()
+ atom()
+ binary()
+ bool()
+ char()
+ cons()
+ deep_string()
+ float()
+ function()
+ integer()
+ list()
+ nil()
+ none()
+ number()
+ pid()
+ port()
+ reference()
+ string()
+ term()
+ tuple()
+'''
+Details:
+<ul>
+ <li>`any()' means "any Erlang data type".
+ `term()' is simply an alias for `any()'.</li>
+ <li>`atom()', `binary()',
+ `float()', `function()',
+ `integer()', `pid()', `port()'
+ and `reference()' are primitive data types of
+ the Erlang programming language.</li>
+ <li>`bool()' is the subset of `atom()' consisting
+ of the atoms `true' and `false'.</li>
+ <li>`char()' is a subset of
+ `integer()' representing character codes.</li>
+ <li>`tuple()' is the set of all tuples `{...}'.</li>
+ <li>`list(T)' is just an alias for `[T]'.</li>
+ <li>`nil()' is an alias for the empty list `[]'.</li>
+ <li>`cons(H,T)' is the list constructor. This is usually not
+ used directly. It is possible to recursively define `list(T)
+ := nil()+cons(T,list(T))'.</li>
+ <li>`string()' is an alias for `[char()]'.</li>
+ <li>`deep_string()' is recursively defined as
+ `[char()+deep_string()]'.</li>
+ <li>`none()' means "no data type". E.g., a function
+ that never returns has type `(...) -> none()'</li>
+</ul>
+
+
+== Acknowledgements ==
+
+Since the first version of EDoc, several people have come up with
+suggestions (Luke Gorrie, Joe Armstrong, Erik Stenman, Sean Hinde, Ulf
+Wiger, ...), and some have even submitted code to demonstrate their
+ideas (Vlad Dumitrescu, Johan Blom, Vijay Hirani, ...). None of that
+code was actually included in the Great Rewriting that followed the
+initial public release (EDoc version 0.1), but most of the central
+points were addressed in the new system, such as better modularization
+and possibility to plug in different layout engines, and making EDoc
+understand the application directory layout.
+
+It is now getting too hard to keep track of all the people who have made
+further suggestions or submitted bug reports, but your input is always
+appreciated. Thank you.
diff --git a/lib/edoc/test/edoc_SUITE_data/overview.syntax_tools b/lib/edoc/test/edoc_SUITE_data/overview.syntax_tools
new file mode 100644
index 0000000000..a63c8b26b2
--- /dev/null
+++ b/lib/edoc/test/edoc_SUITE_data/overview.syntax_tools
@@ -0,0 +1,76 @@
+
+@author Richard Carlsson <[email protected]>
+@copyright 1997-2004 Richard Carlsson
+@version {@vsn}
+@title Erlang Syntax Tools
+
+@doc This package contains modules for handling abstract Erlang syntax
+trees, in a way that is compatible with the "parse trees" of the
+standard library module `erl_parse', together with utilities for reading
+source files in unusual ways and pretty-printing syntax trees. Also
+included is an amazing module merger and renamer called Igor, as well as
+an automatic code-cleaner.
+
+<p>The abstract layer (defined in {@link erl_syntax}) is nicely
+structured and the node types are context-independent. The layer makes
+it possible to transparently attach source-code comments and user
+annotations to nodes of the tree. Using the abstract layer makes
+applications less sensitive to changes in the {@link //stdlib/erl_parse}
+data structures, only requiring the {@link erl_syntax} module to be
+up-to-date.</p>
+
+<p>The pretty printer {@link erl_prettypr} is implemented on top of the
+library module {@link prettypr}: this is a powerful and flexible generic
+pretty printing library, which is also distributed separately.</p>
+
+<p>For a short demonstration of parsing and pretty-printing, simply
+compile the included module <a
+href="../examples/demo.erl"><code>demo.erl</code></a>, and execute
+<code>demo:run()</code> from the Erlang shell. It will compile the
+remaining modules and give you further instructions.</p>
+
+<p>Also try the {@link erl_tidy} module, as follows:
+<pre>
+ erl_tidy:dir("any-erlang-source-dir", [test, old_guard_tests]).</pre>
+("<code>test</code>" assures that no files are modified).</p>
+
+<p>News in 1.4:
+<ul>
+ <li>Added support for {@link erl_syntax:cond_expr/1. cond-expressions},
+ {@link erl_syntax:try_expr/4. try-expressions} and
+ {@link erl_syntax:class_qualifier/2. class-qualifier patterns}.</li>
+ <li>Added support for parameterized modules.</li>
+ <li>{@link igor. Igor} is officially included.</li>
+ <li>Quick-parse functionality added to {@link epp_dodger}.</li>
+</ul>
+</p>
+
+<p>News in 1.3:
+<ul>
+ <li>Added support for qualified names (as used by "packages").</li>
+ <li>Various internal changes.</li>
+</ul>
+</p>
+
+<p>News in 1.2:
+<ul>
+ <li>HTML Documentation (generated with EDoc).</li>
+ <li>A few bug fixes and some minor interface changes (sorry for any
+ inconvenience).</li>
+</ul>
+</p>
+
+<p>News in 1.1:
+<ul>
+ <li>Module {@link erl_tidy}: check or tidy either a single module, or a
+ whole directory tree recursively. Rewrites and reformats the code
+ without losing comments or expanding macros. Safe mode allows
+ generating reports without modifying files.</li>
+ <li>Module {@link erl_syntax_lib}: contains support functions for easier
+ analysis of the source code structure.</li>
+ <li>Module {@link epp_dodger}: Bypasses the Erlang preprocessor - avoids
+ macro expansion, file inclusion, conditional compilation, etc.
+ Allows you to find/modify particular definitions/applications of
+ macros, and other things previously not possible.</li>
+</ul>
+</p>
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
new file mode 100644
index 0000000000..7c7ba58cc9
--- /dev/null
+++ b/lib/edoc/vsn.mk
@@ -0,0 +1 @@
+EDOC_VSN = 0.7.6.5