From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- support/latexdiff/COPYING | 623 +++ support/latexdiff/Makefile | 64 + support/latexdiff/README | 118 + support/latexdiff/contrib/README.latexchanges | 13 + support/latexdiff/contrib/latexchanges.py | 67 + support/latexdiff/contrib/latexdiff-wrap | 192 + support/latexdiff/contrib/latexdiff.spec | 58 + support/latexdiff/doc/example-diff.tex | 107 + support/latexdiff/doc/latexdiff-man.pdf | Bin 0 -> 274601 bytes support/latexdiff/doc/latexdiff-man.tex | 367 ++ support/latexdiff/example/example-draft.tex | 59 + support/latexdiff/example/example-rev.tex | 60 + support/latexdiff/latexdiff | 5030 ++++++++++++++++++++++ support/latexdiff/latexdiff-fast | 5591 +++++++++++++++++++++++++ support/latexdiff/latexdiff-so | 5487 ++++++++++++++++++++++++ support/latexdiff/latexdiff-vc | 827 ++++ support/latexdiff/latexdiff-vc.1 | 309 ++ support/latexdiff/latexdiff.1 | 924 ++++ support/latexdiff/latexrevise | 538 +++ support/latexdiff/latexrevise.1 | 243 ++ 20 files changed, 20677 insertions(+) create mode 100644 support/latexdiff/COPYING create mode 100644 support/latexdiff/Makefile create mode 100644 support/latexdiff/README create mode 100644 support/latexdiff/contrib/README.latexchanges create mode 100644 support/latexdiff/contrib/latexchanges.py create mode 100755 support/latexdiff/contrib/latexdiff-wrap create mode 100644 support/latexdiff/contrib/latexdiff.spec create mode 100644 support/latexdiff/doc/example-diff.tex create mode 100644 support/latexdiff/doc/latexdiff-man.pdf create mode 100644 support/latexdiff/doc/latexdiff-man.tex create mode 100644 support/latexdiff/example/example-draft.tex create mode 100644 support/latexdiff/example/example-rev.tex create mode 100755 support/latexdiff/latexdiff create mode 100755 support/latexdiff/latexdiff-fast create mode 100755 support/latexdiff/latexdiff-so create mode 100755 support/latexdiff/latexdiff-vc create mode 100644 support/latexdiff/latexdiff-vc.1 create mode 100644 support/latexdiff/latexdiff.1 create mode 100755 support/latexdiff/latexrevise create mode 100644 support/latexdiff/latexrevise.1 (limited to 'support/latexdiff') diff --git a/support/latexdiff/COPYING b/support/latexdiff/COPYING new file mode 100644 index 0000000000..d6fa9152e8 --- /dev/null +++ b/support/latexdiff/COPYING @@ -0,0 +1,623 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + diff --git a/support/latexdiff/Makefile b/support/latexdiff/Makefile new file mode 100644 index 0000000000..143ddd213c --- /dev/null +++ b/support/latexdiff/Makefile @@ -0,0 +1,64 @@ +# Modify these paths to the requirements of your own system +# For the current setting you will need root permission but +# it is perfectly acceptable to choose user directories +# +INSTALLPATH = /usr/local +INSTALLMANPATH = $(INSTALLPATH)/man +INSTALLEXECPATH = $(INSTALLPATH)/bin + +default: + @echo "To install stand-alone version type: make install" + @echo " (Note the standard version requires prior installation" + @echo " of the PERL package Algorithm::Diff available from " + @echo " the PERL archive www.cpan.org)" + @echo " " + @echo "To install fast version (using UNIX diff) type: make install fast " + @echo " " + @echo "To install the version which uses the system Algorithm::Diff package type: make install-ext" + @echo " " + +install: install-so + +install-ext: install-latexdiff install-latexrevise install-latexdiff-vc install-man + +install-so: install-latexdiff-so install-latexrevise install-latexdiff-vc install-man + +install-fast: install-latexdiff-fast install-latexrevise install-latexdiff-vc install-man + +install-man: + install -m 644 -D -t $(INSTALLMANPATH)/man1 latexrevise.1 latexdiff.1 latexdiff-vc.1 + +install-latexdiff: + install -D -t $(INSTALLEXECPATH) latexdiff + +install-latexdiff-so: + if [ -e $(INSTALLEXECPATH)/latexdiff ]; then rm $(INSTALLEXECPATH)/latexdiff; fi + install -D -t $(INSTALLEXECPATH) latexdiff-so + cd $(INSTALLEXECPATH); ln -s latexdiff-so latexdiff + +install-latexdiff-fast: + if [ -e $(INSTALLEXECPATH)/latexdiff ]; then rm $(INSTALLEXECPATH)/latexdiff; fi + install -D -t $(INSTALLEXECPATH) latexdiff-fast + cd $(INSTALLEXECPATH); ln -s latexdiff-fast latexdiff + +install-latexrevise: + install -D -t $(INSTALLEXECPATH) latexrevise + +install-latexdiff-vc: + install -D -t $(INSTALLEXECPATH) latexdiff-vc + cd $(INSTALLEXECPATH); for vcs in cvs rcs svn git hg ; do if [ -e latexdiff-$$vcs ]; then rm latexdiff-$$vcs; fi; ln -s latexdiff-vc latexdiff-$$vcs ; done + +test-ext: + @echo "latexdiff example/example-draft.tex example/example-rev.tex (system Algorithm::Diff)" + ./latexdiff -V example/example-draft.tex example/example-rev.tex > example/example-diff.tex + @echo "Difference file created: example/example-diff.tex" + +test-so: + @echo "latexdiff example/example-draft.tex example/example-rev.tex (stand-alone version)" + ./latexdiff-so -V example/example-draft.tex example/example-rev.tex > example/example-diff.tex + @echo "Difference file created: example/example-diff.tex" + +test-fast: + @echo "latexdiff example/example-draft.tex example/example-rev.tex (stand-alone version)" + ./latexdiff-fast -V example/example-draft.tex example/example-rev.tex > example/example-diff.tex + @echo "Difference file created: example/example-diff.tex" diff --git a/support/latexdiff/README b/support/latexdiff/README new file mode 100644 index 0000000000..b75fa841bc --- /dev/null +++ b/support/latexdiff/README @@ -0,0 +1,118 @@ +INTRODUCTION + +latexdiff is a Perl script, which compares two latex files and marks +up significant differences between them (i.e. a diff for latex files). + Various options are available for visual markup using standard latex +packages such as "color.sty". Changes not directly affecting visible +text, for example in formatting commands, are still marked in +the latex source. Note that only files conforming to latex syntax will +be processed correctly, not generic TeX files. Some further +minor restrictions apply, see documentation. + +A rudimentary revision facilility is provided by another Perl script, +latexrevise, which accepts or rejects all changes. Manual +editing of the difference file can be used to override this default +behaviour and accept or reject selected changes only. + +The author is F Tilmann. + +Project webpage: https://github.com/ftilmann/latexdiff/ +CTAN page: http://www.ctan.org/pkg/latexdiff + + +REQUIREMENTS + +Perl 5.8 or higher must be installed. + The latexdiff script makes use of the Perl package Algorithm::Diff (available +from www.cpan.org, current version 1.19). You can either install this package, or +use the standalone version of latexdiff, latexdiff-so, which has version 1.15 of +this package inlined and does not require external installation of +the package. Because latexdiff uses internal functions of Algorithm:Diff whose +calling format or availability can change without notice, the preferred method is +now to use the standalone version. + +As an alternative, latexdiff-fast has a modified version of Algorithm::Diff inlined, +which internally uses the UNIX diff command. This version is much faster but is dependent +on an external "diff" command. Subtle differences in the algorithm of Algorithm::Diff and +UNIX-diff mean that the resulting set of differences will generally not be the same as +for the standard latexdiff. In most practical cases, these differences are minor, though. + +INSTALLATION UNIX/LINUX + +The basic installation procedure is almost trivial: + +1. Copy latexdiff, latexrevise and latexdiff-vc into a directory which + is in the search path and make them executable. If the Algorithm::Diff + package is not installed, use latexdiff-so instead of latexdiff. + +2. Copy latexdiff.1 and latexrevise.1 into the correct man directory + +3. Optionally create soft links latexdiff-cvs latexdiff-rcs, latexdiff-git + latexdiff-svn and latexdiff-hg for latexdiff-vc. + +The attached trivial Makefile contains example commands to carry out above +steps as root for a typical UNIX installation. Type + + make install (for the stand alone version) +or + make install-ext (for the version using the external Algorithm::Diff) +or + make install-fast (for the version using the UNIX 'diff' function for fast differencing) + +to get it rolling. You can type + + make test +or + make test-ext +or + make test-fast + +to test the respective versions on a brief example before installation. It will often be +as easy to carry out these steps manually instead of using the Makefile. + + +DOCUMENTATION: + +Usage instructions are in the manual latexdiff-man.pdf as well as the +man pages. + +CHANGELOGS: + +Check out the comment lines at the beginning of the perl scripts (latexdiff, latexdiff-vc, latexrevise) + +CONTRIBUTIONS + +The directory contrib contains code written by others relating to latexdiff. +Currently this directory contains: + +latexdiff-wrap (Author: V. Kuhlmann) An alternative wrapper script which can be used + instead of latexdiff-vc. Its main use is as a template for customised wrapper scripts. + +latexdiff.spec (Author: T. Doerges) spec file for RPM generation + +latexchanges (Author: Jan-Ake Larsson) Wrapper script for applying latexdiff with numbered documen version +(see contrib/README.latexchanges for a more detailed description) + +Contributions by the following authors were incorporated into the latexdiff code, or inspired me to +extend latexdiff in a similar way: J. Paisley, N. Becker, K. Huebner + +EXTERNAL LATEXDIFF SUPPORT PROGRAMS + +LATEXDIFFCITE (Author: Christer van der Meeren) is a wrapper around latexdiff to make citations diff properly. It works by expanding \cite type commands using the bbl or bib file, such that citations are treated just like normal text rather than as atomic in the plain latexdiff. +https://latexdiffcite.readthedocs.org + +GIT-LATEXDIFF (lead author: Matthieu Moy) is a wrapper (bash scipt) around latexdiff that allows using it to diff two revisions of a LaTeX file under git revision control (similar functionality is provided by latexdiff-vc --git with --flatten option included with this distribution but git-latexdiff allows more fine-grained control on (not to be confused with latexdiff-git, which is normally installed as a soft link to latexdiff-vc) +https://gitorious.org/git-latexdiff/ + +LICENSE (also see file COPYING) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License Version 3 as published by +the Free Software Foundation. + +This program 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 General Public License for more details (file LICENSE in the +distribution). + diff --git a/support/latexdiff/contrib/README.latexchanges b/support/latexdiff/contrib/README.latexchanges new file mode 100644 index 0000000000..4a02765844 --- /dev/null +++ b/support/latexdiff/contrib/README.latexchanges @@ -0,0 +1,13 @@ +latexchanges.py (Jan-Ake Larsson): +Here's a wrapper I wrote for latexdiff, intended as a drop-in +replacement for latex, when you have several numbered (or dated) +versions of a manuscript. My coauthors don't as a rule know what CVS or +SVN is, they simply use a number or date for the different versions. + +latexchanges replaces the current DVI with one that includes a +latexdiff to the last version. The last version is selected as the +TEX file in the same directory with the same prefix (up to a number +or a dot), that has an mtime immediately preceding the given TEX +file. + + diff --git a/support/latexdiff/contrib/latexchanges.py b/support/latexdiff/contrib/latexchanges.py new file mode 100644 index 0000000000..de7acbe825 --- /dev/null +++ b/support/latexdiff/contrib/latexchanges.py @@ -0,0 +1,67 @@ +#! /bin/env python +# latexchanges +# +# Wrapper for latexdiff, intended as a drop-in replacement for latex, +# when you have several numbered (or dated) versions of a manuscript. +# My coauthors don't as a rule know what CVS or SVN is, they simply +# use a number or date for the different versions. +# +# latexchanges replaces the current DVI with one that includes a +# latexdiff to the last version. The last version is selected as the +# TEX file in the same directory with the same prefix (up to a number +# or a dot), that has an mtime immediately preceding the given TEX +# file. +# +# (I should probably add CVS version numbering too, at some point.) +# +# Copyright (C) 2009 by Jan-\AA{}ke Larsson +# Released under the terms of the GNU General Public License (GPL) +# Version 2. See http://www.gnu.org/ for details. +# +# Please do provide patches and bug reports, but remember: if it +# breaks, you get to keep the pieces. +# +# Jan-\AA{}ke Larsson +# Sept 16 2009 + +from os import listdir,system,stat +from sys import argv +from re import split + +name="" +newarg=[] + +# Find filename argument +for i in range(1,len(argv)): + if argv[i][-4:]==".tex": + basename=split('[0-9.]',argv[i])[0] + name=argv[i][:-4] + newarg.append(name+".changes.tex") + else: + newarg.append(argv[i]) + +if name: + print "Filename",name+".tex" + print "Prefix is",basename + # Find last archived version + mtime=stat(name+".tex").st_mtime + old_mtime=0 + ls=listdir(".") + for j in ls: + if j.startswith(basename) and j.endswith(".tex")\ + and not j.endswith(".changes.tex"): + tmptime=stat(j).st_mtime + if mtime>tmptime and old_mtime0: + print "Comparing with",oldname + system ("/bin/cp "+name+".aux "+name+".changes.aux") + system ("/bin/cp "+name+".bbl "+name+".changes.bbl") + system ("latexdiff "+oldname+" "+name+".tex > "+name+".changes.tex") + system ("latex "+" ".join(newarg)) + system ("cp "+name+".changes.dvi "+name+".dvi") + else: + system ("latex "+" ".join(argv[1:])) diff --git a/support/latexdiff/contrib/latexdiff-wrap b/support/latexdiff/contrib/latexdiff-wrap new file mode 100755 index 0000000000..894b424de3 --- /dev/null +++ b/support/latexdiff/contrib/latexdiff-wrap @@ -0,0 +1,192 @@ +#!/bin/bash +# +# latexdiff-wrap +# +# Wrapper for latexdiff, to +# * provide support for documents consiting of more than 1 latex file +# * provide my common arguments +# +# Copyright (C) by Volker Kuhlmann +# Released under the terms of the GNU General Public License (GPL) Version 2. +# See http://www.gnu.org/ for details. +# +# Volker Kuhlmann +# 5, 6, 7, 12, 16, 17 Oct 2005 +# 31 Jan; 5, 7, 13, 15 Feb 2006 +# + +VERSION="0.6, 15 Feb 2006" +AUTHOR="Volker Kuhlmann " +COPYRIGHT="Copyright (C) 2005-2006" + + +#### +#### Constants and initialised variables +# +diffcmd="latexdiff" +diffrc="$HOME/texmf/latexdiff" +#diffargs="-e latin1 --ignore-warnings -p latexdiff-preamble.sty" +diffargs="-e latin1 --ignore-warnings" +diffargs="$diffargs --append-safecmd $diffrc/safe-cmds" +diffargs="$diffargs --append-textcmd $diffrc/text-cmds" +# Note: Can't use multiple --append-safecmd +# show current command lists: +#diffcmd="$diffcmd --show-safecmd --show-textcmd --show-config" + + +#### +#### Version, Usage, Help +# +show_version() { + echo "${0##*/} version $VERSION +$COPYRIGHT by $AUTHOR" +} + +show_usage() { + echo " +Usage: ${0##*/} OLDDIR NEWDIR DIFFDIR [DIFFARGS --] FILE.tex [...] + ${0##*/} --show [DIFFARGS] +Version $VERSION +$COPYRIGHT by $AUTHOR +" +} + +show_help() { + show_usage + echo "\ +For each FILE.tex, build a new file DIFFDIR/FILE.tex with markup of the changes +which were made from OLDDIR/FILE.tex to NEWDIR/FILE.tex. +Any path given with FILE.tex is stripped off. +Any DIFFARGS are added to the latexdiff call, if present (remember to follow +them with a double-hyphen on its own before the FILE arguments). + +With --show, shows the settings latexdiff would be running with, including the +changes applied by the user. +" +} + +# For scripts not using function library only: +Version() { show_version; exitwith ErrVersion; } +Usage() { show_help; exitwith ErrUsage; } +Help() { test "$1" && exitwith ErrHelp show_help; show_help; exitwith ErrOK; } + + +#### +#### Error/Exit codes +# +exitwith() { + exec 1>&2 # write stdout on stderr instead + case "$1" in + ErrOK) + exit 0;; + ErrVersion|ErrUsage|ErrHelp) + # Output generated by function (program) $2, if given + test -n "$2" && "$2" + exit 1;; + # more codes in here + # more codes in here + ErrBadoption) + echo "Bad option '$2'." + echo "Call with -h for help." + exit 9;; + ErrMissingParameter) + echo "A required parameter for option $2 is missing." + echo "Call with -h for help." + exit 9;; + *) + echo "Internal error: exitwith() called with illegal error code '$1'." + exit 19;; + esac +} + + +#### +#### Parse command line parameters +# + +# If the next arg starts with a "-", collect additional argument for latexdiff +# until "--". +scanextraargs() { + addargs=() + case "$1" in -*) + while [ $# -gt 0 -a "$1" != "--" ]; do + addargs=( "${addargs[@]}" "$1" ) + shift + done + test "$1" == "--" && shift + ;; esac + fileargs=( "$@" ) +} + +case "$1" in + --version) Version;; + --usage) Usage;; + --help|-h|-help) Help;; + --show) + shift + scanextraargs "$@" + (set -x + $diffcmd $diffargs "${addargs[@]}" \ + --show-safecmd --show-textcmd --show-config + ) | fmt + exit $? ;; +esac + +olddir="${1%/}" +newdir="${2%/}" +diffdir="${3%/}" + +if ! [ -d "$olddir" -a -d "$newdir" -a -d "$diffdir" ]; then + Help 1>&2 err +fi + +shift 3 + +scanextraargs "$@" +set -- "${fileargs[@]}" + + + +#### +#### Functions +# +#set -x +Log() { echo 1>&2 "+ $@"; "$@"; } + + +#### +#### Main +# + +# Create output directory, just in case. +(set -x +mkdir -p "$diffdir" +) +while [ $# -gt 0 ]; do + file="${1##*/}" + echo Examining: "$file" + # No point running latexdiff if both files are identical, + # but run latexdiff on top-level LaTeX file in any case. + if cmp --quiet "$olddir/$file" "$newdir/$file" \ + && ! grep -lq '\\begin.*{document}' "$newdir/$file"; then + (set -x + cp -p "$olddir/$file" "$diffdir" + ) + else + # Delete file, to make sure it's not clobbered by redirecting stdout + # in case it's a symlink to te original. + test -f "$diffdir/$file" && (set -x + rm "$diffdir/$file" + ) + # Run latexdiff if both input files are present. + run=1 + test -f "$olddir/$file" || { echo 1>&2 "No file: $olddir/$file"; run=; } + test -f "$newdir/$file" || { echo 1>&2 "No file: $newdir/$file"; run=; } + test -n "$run" && \ + (set -x + $diffcmd $diffargs "${addargs[@]}" \ + "$olddir/$file" "$newdir/$file" > "$diffdir/$file" + ) + fi + shift +done diff --git a/support/latexdiff/contrib/latexdiff.spec b/support/latexdiff/contrib/latexdiff.spec new file mode 100644 index 0000000000..9255a69fea --- /dev/null +++ b/support/latexdiff/contrib/latexdiff.spec @@ -0,0 +1,58 @@ +Summary: Diff for LaTeX files +Name: latexdiff +Version: 0.5 +Release: 1 +License: GPL +Group: Productivity/Publishing/TeX/Utilities +URL: http://www.tug.org/tex-archive/help/Catalogue/entries/latexdiff.html +Source0: %{name}.zip +BuildArch: noarch +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root +# only required for 'make install-ext' +# Requires: perl-Algorithm-Diff + + +%description +latexdiff is a Perl script, which compares two latex files and marks +up significant differences between them (i.e. a diff for latex files). + Various options are available for visual markup using standard latex +packages such as "color.sty". Changes not directly affecting visible +text, for example in formatting commands, are still marked in +the latex source. + +(C) 2004 Frederik Tilmann + + +%prep +%setup -n %{name} + + +%build +# quick had to adapt the Makefile +%{__mv} Makefile Makefile.old +%{__sed} \ + -e "s;INSTALLPATH = /usr/local;INSTALLPATH = \${DESTDIR}%{_prefix};" \ + -e "s;INSTALLMANPATH = \$(INSTALLPATH)/man;INSTALLMANPATH = \${DESTDIR}%{_mandir};" \ + Makefile.old > Makefile + + +%install +%{__mkdir_p} $RPM_BUILD_ROOT%{_bindir} +%{__mkdir_p} $RPM_BUILD_ROOT%{_mandir}/man1 + +%makeinstall + + +%clean +[ "${RPM_BUILD_ROOT}" != "/" ] && [ -d "${RPM_BUILD_ROOT}" ] && %{__rm} -rf "${RPM_BUILD_ROOT}" + + +%files +%defattr(-,root,root) +%doc example CHANGES LICENSE README +%{_bindir}/* +%{_mandir}/man*/* + +%changelog +* Thu Jan 4 2007 Till Dörges - 0.5-1 +- Initial build. diff --git a/support/latexdiff/doc/example-diff.tex b/support/latexdiff/doc/example-diff.tex new file mode 100644 index 0000000000..9724038884 --- /dev/null +++ b/support/latexdiff/doc/example-diff.tex @@ -0,0 +1,107 @@ +\documentclass[12pt,a4paper]{article} +%DIF LATEXDIFF DIFFERENCE FILE +%DIF DEL example-draft.tex Thu Jun 12 00:01:26 2014 +%DIF ADD example-rev.tex Thu Jun 12 00:01:26 2014 + +\setlength{\topmargin}{-0.2in} +\setlength{\textheight}{9.5in} +\setlength{\oddsidemargin}{0.0in} + +%DIF 7c7 +%DIF < \setlength{\textwidth}{6.5in} +%DIF ------- +\setlength{\textwidth}{6in} %DIF > +%DIF ------- + +\title{latexdiff Example - \DIFdelbegin \DIFdel{Draft }\DIFdelend \DIFaddbegin \DIFadd{Revised }\DIFaddend version} +\author{F Tilmann} +% Note how in the preamble visual markup is never used (even %DIF > +% if some preamble might eventually end up as visible text.) %DIF > +%DIF PREAMBLE EXTENSION ADDED BY LATEXDIFF +%DIF UNDERLINE PREAMBLE %DIF PREAMBLE +\RequirePackage[normalem]{ulem} %DIF PREAMBLE +\RequirePackage{color}\definecolor{RED}{rgb}{1,0,0}\definecolor{BLUE}{rgb}{0,0,1} %DIF PREAMBLE +\providecommand{\DIFadd}[1]{{\protect\color{blue}\uwave{#1}}} %DIF PREAMBLE +\providecommand{\DIFdel}[1]{{\protect\color{red}\sout{#1}}} %DIF PREAMBLE +%DIF SAFE PREAMBLE %DIF PREAMBLE +\providecommand{\DIFaddbegin}{} %DIF PREAMBLE +\providecommand{\DIFaddend}{} %DIF PREAMBLE +\providecommand{\DIFdelbegin}{} %DIF PREAMBLE +\providecommand{\DIFdelend}{} %DIF PREAMBLE +\providecommand{\DIFmodbegin}{} %DIF PREAMBLE +\providecommand{\DIFmodend}{} %DIF PREAMBLE +%DIF FLOATSAFE PREAMBLE %DIF PREAMBLE +\providecommand{\DIFaddFL}[1]{\DIFadd{#1}} %DIF PREAMBLE +\providecommand{\DIFdelFL}[1]{\DIFdel{#1}} %DIF PREAMBLE +\providecommand{\DIFaddbeginFL}{} %DIF PREAMBLE +\providecommand{\DIFaddendFL}{} %DIF PREAMBLE +\providecommand{\DIFdelbeginFL}{} %DIF PREAMBLE +\providecommand{\DIFdelendFL}{} %DIF PREAMBLE +%DIF LISTINGS PREAMBLE %DIF PREAMBLE +\RequirePackage{listings} %DIF PREAMBLE +\RequirePackage{color} %DIF PREAMBLE +\lstdefinelanguage{DIFcode}{ %DIF PREAMBLE +%DIF DIFCODE_UNDERLINE %DIF PREAMBLE + moredelim=[il][\color{red}\sout]{\%DIF\ <\ }, %DIF PREAMBLE + moredelim=[il][\color{blue}\uwave]{\%DIF\ >\ } %DIF PREAMBLE +} %DIF PREAMBLE +\lstdefinestyle{DIFverbatimstyle}{ %DIF PREAMBLE + language=DIFcode, %DIF PREAMBLE + basicstyle=\ttfamily, %DIF PREAMBLE + columns=fullflexible, %DIF PREAMBLE + keepspaces=true %DIF PREAMBLE +} %DIF PREAMBLE +\lstnewenvironment{DIFverbatim}{\lstset{style=DIFverbatimstyle}}{} %DIF PREAMBLE +\lstnewenvironment{DIFverbatim*}{\lstset{style=DIFverbatimstyle,showspaces=true}}{} %DIF PREAMBLE +%DIF END PREAMBLE EXTENSION ADDED BY LATEXDIFF + +\begin{document} +\maketitle + +\section*{Introduction} + +This is an extremely simple document that showcases some of \DIFaddbegin \DIFadd{the }\DIFaddend latexdiff features. +Type +\begin{verbatim} +latexdiff -t UNDERLINE example-draft.tex example-rev.tex > example-diff.tex +\end{verbatim} +to create the difference file. You can inspect this file directly. Then run either +\begin{verbatim} +pdflatex example-diff.tex +xpdf example-diff.pdf +\end{verbatim} +or +\begin{verbatim} +latex example-diff.tex +dvips -o example-diff.ps example-diff.dvi +gv example-diff.ps +\end{verbatim} +to display the markup. + +\section*{\DIFdelbegin \DIFdel{Another }\DIFdelend \DIFaddbegin \DIFadd{Yet another }\DIFaddend section title} + + \DIFdelbegin \DIFdel{A paragraph with a line only in the draft document. }\DIFdelend More things could be said were it not for the constraints of time and space. + +\DIFaddbegin \DIFadd{A paragraph with a line only in the revised document. }\DIFaddend More things could be +said were it not for the constraints of time and space. + +And here is a \DIFdelbegin \DIFdel{tipo}\DIFdelend \DIFaddbegin \DIFadd{typo}\DIFaddend . + +Here is a table: + +\begin{tabular}{ll} +Name & Description \\ +\hline +Gandalf & \DIFdelbegin \DIFdel{Grey }\DIFdelend \DIFaddbegin \DIFadd{White }\DIFaddend \\ +Saruman & \DIFdelbegin \DIFdel{White +}\DIFdelend \DIFaddbegin \DIFadd{Evil +}\DIFaddend \end{tabular} + +And \DIFdelbegin \DIFdel{sometimes a whole paragraph gets completely rewritten. In this +case latexdiff marks up the whole paragraph even if some words in it +are identical}\DIFdelend \DIFaddbegin \DIFadd{now for something completely different, with not a paragraph in sight}\DIFaddend . +No change, +no markup! +\end{document} + + diff --git a/support/latexdiff/doc/latexdiff-man.pdf b/support/latexdiff/doc/latexdiff-man.pdf new file mode 100644 index 0000000000..32bfa06e2b Binary files /dev/null and b/support/latexdiff/doc/latexdiff-man.pdf differ diff --git a/support/latexdiff/doc/latexdiff-man.tex b/support/latexdiff/doc/latexdiff-man.tex new file mode 100644 index 0000000000..1059eb25a3 --- /dev/null +++ b/support/latexdiff/doc/latexdiff-man.tex @@ -0,0 +1,367 @@ +\documentclass[a4]{article} +\usepackage{graphicx} +%\def\C++{{\rm C\kern-.05em\raise.3ex\hbox{\footnotesize ++}}} +%\def\underscore{\leavevmode\kern.04em\vbox{\hrule width 0.4em height 0.3pt}} +\setlength{\parindent}{0pt} +%\setlength{\textwidth}{6.5in} +%\setlength{\oddsidemargin}{0.0in} +\title{Marking up differences between latex files with {\em latexdiff}} +\author{F.J. Tilmann\thanks{tilmann@gfz-potsdam.de}} +\date{\today} + +\begin{document} +\maketitle + +\section*{Preamble} + +{\em latexdiff} is a Perl script, which compares two +latex files and marks up significant differences between them. Various options are available for visual markup using standard +latex packages such as {\em color.sty}. Changes not directly affecting visible +text, for example in formatting commands, are still marked in the +latex source. + +A rudimentary revision facilility is provided by another Perl script, +{\em latexrevise}, which accepts or rejects all changes. Manual editing +of the difference file can be used to override this default behaviour +and accept or reject selected changes only. + +There is no explicit support for annotations as these are trivial to implement. +For example, I include the following command definition in the preamble +\begin{verbatim} +\newcommand{\remark}[1]{{ \bf [ \footnotesize #1 ]}} +\end{verbatim} +and mark up annotations as follows +\begin{verbatim} +... The roadrunner is the fastest running bird \remark{Check this +again with a zoologist!}. The most famous roadrunner ... +\end{verbatim} +Alternatively, instead of a command like \verb#\remark# in the example just given, an +equivalent annotation environment could be defined. +{\em latexrevise} can remove such comments or +environments from the text body. + +%It is planned that the revision capabilities of this system will be +%further expanded, dependent on the amount of feedback received. + +On the following pages you find the {\em man} pages for {\em + latexdiff} and {\em latexrevise} and a simple example. + +\include{latexdiff} +\setcounter{section}{0} + +\include{latexrevise} +\setcounter{section}{0} + +\include{latexdiff-vc} +\setcounter{section}{0} + +\section*{A simple example} + +We start with a draft text, \verb|example-draft.tex|, listed here in +full but also included in the distribution (except that the ``verbatim'' environment had +to be renamed to ``Verbatim'' for the listing). + +{\scriptsize +\begin{verbatim} +\documentclass[12pt,a4paper]{article} + +\setlength{\topmargin}{-0.2in} +\setlength{\textheight}{9.5in} +\setlength{\oddsidemargin}{0.0in} + +\setlength{\textwidth}{6.5in} + +\title{latexdiff Example - Draft version} +\author{F Tilmann} + +\begin{document} +\maketitle + +\section*{Introduction} + +This is an extremely simple document that showcases some of latexdiff features. +Type +\begin{Verbatim} +latexdiff -t UNDERLINE example-draft.tex example-rev.tex > example-diff.tex +\end{Verbatim} +to create the difference file. You can inspect this file directly. Then run either +\begin{Verbatim} +pdflatex example-diff.tex +xpdf example-diff.pdf +\end{Verbatim} +or +\begin{Verbatim} +latex example-diff.tex +dvips -o example-diff.ps example-diff.dvi +gv example-diff.ps +\end{Verbatim} +to display the markup. Of course, instead of \verb|xpdf| you can use +\verb|okular, evince, acroread| or any other pdf or postscript viewer. + +\section*{Another section title} + +A paragraph with a line only in the draft document. More things +could be said were it not for the constraints of time and space. + +More things could be said were it not for the constraints of time and space. + +And here is a tipo. + +Here is a table: + +\begin{tabular}{ll} +Name & Description \\ +\hline +Gandalf & Grey \\ +Saruman & White +\end{tabular} + +And sometimes a whole paragraph gets completely rewritten. In this +case latexdiff marks up the whole paragraph even if some words in it +are identical. +No change, no markup! +\end{document} +\end{verbatim} +} + +We can now edit +this text as we would do with any other latex file to create +a new revision of the text, \verb|example-rev.tex|. We should run +\begin{verbatim} +latex example-rev.tex +\end{verbatim} +and look at the resulting \verb|.dvi| file to make sure that all +changes are valid. An example revision is listed here: + +{\scriptsize +\begin{verbatim} +\documentclass[12pt,a4paper]{article} + +\setlength{\topmargin}{-0.2in} +\setlength{\textheight}{9.5in} +\setlength{\oddsidemargin}{0.0in} + +\setlength{\textwidth}{6in} + +\title{latexdiff Example - Revised version} +\author{F Tilmann} +% Note how in the preamble visual markup is never used (even +% if some preamble might eventually end up as visible text.) + +\begin{document} +\maketitle + +\section*{Introduction} + +This is an extremely simple document that showcases some of the latexdiff features. +Type +\begin{Verbatim} +latexdiff -t UNDERLINE example-draft.tex example-rev.tex > example-diff.tex +\end{Verbatim} +to create the difference file. You can inspect this file directly. Then run either +\begin{Verbatim} +pdflatex example-diff.tex +xpdf example-diff.pdf +\end{Verbatim} +or +\begin{Verbatim} +latex example-diff.tex +dvips -o example-diff.ps example-diff.dvi +gv example-diff.ps +\end{Verbatim} +to display the markup. + +\section*{Yet another section title} + + More things could be said were it not for the constraints of time and space. + +A paragraph with a line only in the revised document. +More things could be said were it not for the constraints of time and space. + +And here is a typo. + +Here is a table: + +\begin{tabular}{ll} +Name & Description \\ +\hline +Gandalf & White \\ +Saruman & Evil +\end{tabular} + +And now for something completely different, with not a paragraph in sight. +No change, +no markup! +\end{document} +\end{verbatim} +} + +To compare both revisions, type +\begin{verbatim} +latexdiff -t UNDERLINE example-draft.tex example-rev.tex > example-diff.tex +\end{verbatim} +This results in the following difference file (a few newlines have been +added in this listing for legibility reasosn): +{\scriptsize +\begin{verbatim} +\documentclass[12pt,a4paper]{article} + +\setlength{\topmargin}{-0.2in} +\setlength{\textheight}{9.5in} +\setlength{\oddsidemargin}{0.0in} + +%DIF 7c7 +%DIF < \setlength{\textwidth}{6.5in} +%DIF ------- +\setlength{\textwidth}{6in} %DIF > +%DIF ------- + +%DIF 9c9 +%DIF < \title{latexdiff Example - Draft version} +%DIF ------- +\title{latexdiff Example - Revised version} %DIF > +%DIF ------- +\author{F Tilmann} +% Note how in the preamble visual markup is never used (even %DIF > +% if some preamble might eventually end up as visible text.) %DIF > +%DIF PREAMBLE EXTENSION ADDED BY LATEXDIFF +%DIF UNDERLINE PREAMBLE %DIF PREAMBLE +\RequirePackage[normalem]{ulem} %DIF PREAMBLE +\RequirePackage{color} %DIF PREAMBLE +\providecommand{\DIFadd}[1]{{\color{blue}\uline{#1}}} %DIF PREAMBLE +\providecommand{\DIFdel}[1]{{\color{red}\sout{#1}}} %DIF PREAMBLE +%DIF SAFE PREAMBLE %DIF PREAMBLE +\providecommand{\DIFaddbegin}{} %DIF PREAMBLE +\providecommand{\DIFaddend}{} %DIF PREAMBLE +\providecommand{\DIFdelbegin}{} %DIF PREAMBLE +\providecommand{\DIFdelend}{} %DIF PREAMBLE +%DIF FLOATSAFE PREAMBLE %DIF PREAMBLE +\providecommand{\DIFaddFL}[1]{\DIFadd{#1}} %DIF PREAMBLE +\providecommand{\DIFdelFL}[1]{\DIFdel{#1}} %DIF PREAMBLE +\providecommand{\DIFaddbeginFL}{} %DIF PREAMBLE +\providecommand{\DIFaddendFL}{} %DIF PREAMBLE +\providecommand{\DIFdelbeginFL}{} %DIF PREAMBLE +\providecommand{\DIFdelendFL}{} %DIF PREAMBLE +%DIF END PREAMBLE EXTENSION ADDED BY LATEXDIFF + +\begin{document} +\maketitle + +\section*{Introduction} + +This is an extremely simple document that showcases some of latexdiff features. +Type +\begin{Verbatim} +latexdiff -t UNDERLINE example-draft.tex example-rev.tex > example-diff.tex +\end{Verbatim} +to create the difference file. You can inspect this file directly. Then run either +\begin{Verbatim} +pdflatex example-diff.tex +xpdf example-diff.pdf +\end{Verbatim} +or +\begin{Verbatim} +latex example-diff.tex +dvips -o example-diff.ps example-diff.dvi +gv example-diff.ps +\end{Verbatim} +to display the markup. + +\section*{\DIFaddbegin \DIFadd{Yet another }\DIFaddend \DIFdelbegin +\DIFdel{Another }\DIFdelend section title} + + \DIFdelbegin \DIFdel{A paragraph with a line only in the draft + document. }\DIFdelend More things could + be said were it not for the constraints of time and space. + +\DIFaddbegin \DIFadd{A paragraph with a line only in the revised + document. }\DIFaddend More things could be said +were it not for the constraints of time and space. + +And here is a \DIFaddbegin \DIFadd{typo}\DIFaddend \DIFdelbegin +\DIFdel{tipo}\DIFdelend . + +Here is a table: + +\begin{tabular}{ll} +Name & Description \\ +\hline +Gandalf & \DIFaddbegin \DIFadd{White }\DIFaddend \DIFdelbegin +\DIFdel{Grey }\DIFdelend \\ +Saruman & \DIFaddbegin \DIFadd{Evil +}\DIFaddend \DIFdelbegin \DIFdel{White +}\DIFdelend \end{tabular} + +And \DIFaddbegin \DIFadd{now for something completely different, with not + a paragraph in sight}\DIFaddend \DIFdelbegin \DIFdel{sometimes a whole + paragraph gets completely rewritten. In this +case latexdiff marks up the whole paragraph even if some words in it +are identical}\DIFdelend . +No change, +no markup! +\end{document} +\end{verbatim} +} +Type +\begin{verbatim} +pdflatex example-diff.tex +xpdf example-diff.pdf +\end{verbatim} +to make the markup visible. This is what it looks like: + +\vspace{1cm} +\framebox[\textwidth]{\includegraphics[width=\textwidth]{example-diff}} +\vspace{1cm} + +If you approve of all the changes in the revision, just continue with +\verb|example-rev.tex| for the next revision. If you like to adopt +most but not all changes you can use \verb|latexrevise| in the +following manner. Simply edit \verb|example-diff.tex| to remove the \verb|\DIFdelbegin| and +\verb|\DIFdelend| tags around the text you would like to keep and +simply remove the text between \verb|\DIFaddbegin| and +\verb|\DIFaddend| tags, if you do not wish to keep them. Say you are happy with all proposed changes for the +example above except in +the last paragraph where you prefer the original draft. You have +to change + +{\scriptsize +\begin{verbatim} +... +And \DIFaddbegin \DIFadd{now for something completely different, with not + a paragraph in sight}\DIFaddend \DIFdelbegin \DIFdel{sometimes a whole + paragraph gets completely rewritten. In this +case latexdiff marks up the whole paragraph even if some words in it +are identical}\DIFdelend . +... +\end{verbatim} +} +into +{\scriptsize +\begin{verbatim} +... +And \DIFdel{sometimes a whole + paragraph gets completely rewritten. In this +case latexdiff marks up the whole paragraph even if some words in it +are identical}. +... +\end{verbatim} +} +and run +\begin{verbatim} +latexrevise -a example-diff.tex > example-final.tex +\end{verbatim} +\verb|example-final.tex| is then almost identical to +\verb|example-rev.tex| except for the last paragraph. + +\section*{External tools} +The following is an incomplete list of wrappers written by others providing some added functionality. These are not included with the distribution but need to be downloaded and installed separately. + +\begin{description} + \item[latexdiffcite] (Author: Christer van der Meeren) is a wrapper around latexdiff to make citations diff properly. It works by expanding \verb|\cite| type commands using the bbl or bib file, such that citations are treated just like normal text rather than as atomic in the plain latexdiff. \\ +\verb|https://latexdiffcite.readthedocs.org| +\item[git-latexdiff] (lead author: Matthieu Moy) is a wrapper (bash scipt) around latexdiff that allows using it to diff two revisions of a LaTeX file under git revision control Similar functionality is provided by \verb|latexdiff-vc --git| with \verb|--flatten| option included with this distribution but git-latexdiff allows more fine-grained control on varous aspects. (Not to be confused with latexdiff-git, which is normally installed as a soft link to latexdiff-vc) \\ +\verb|https://gitorious.org/git-latexdiff/| + +\end{description} + +\end{document} diff --git a/support/latexdiff/example/example-draft.tex b/support/latexdiff/example/example-draft.tex new file mode 100644 index 0000000000..593a170712 --- /dev/null +++ b/support/latexdiff/example/example-draft.tex @@ -0,0 +1,59 @@ +\documentclass[12pt,a4paper]{article} + +\setlength{\topmargin}{-0.2in} +\setlength{\textheight}{9.5in} +\setlength{\oddsidemargin}{0.0in} + +\setlength{\textwidth}{6.5in} + +\title{latexdiff Example - Draft version} +\author{F Tilmann} + +\begin{document} +\maketitle + +\section*{Introduction} + +This is an extremely simple document that showcases some of latexdiff features. +Type +\begin{verbatim} +latexdiff -t UNDERLINE example-draft.tex example-rev.tex > example-diff.tex +\end{verbatim} +to create the difference file. You can inspect this file directly. Then run either +\begin{verbatim} +pdflatex example-diff.tex +xpdf example-diff.pdf +\end{verbatim} +or +\begin{verbatim} +latex example-diff.tex +dvips -o example-diff.ps example-diff.dvi +gv example-diff.ps +\end{verbatim} +to display the markup. + +\section*{Another section title} + +A paragraph with a line only in the draft document. More things could be said +were it not for the constraints of time and space. + +More things could be said were it not for the constraints of time and space. + +And here is a tipo. + +Here is a table: + +\begin{tabular}{ll} +Name & Description \\ +\hline +Gandalf & Grey \\ +Saruman & White +\end{tabular} + +And sometimes a whole paragraph gets completely rewritten. In this +case latexdiff marks up the whole paragraph even if some words in it +are identical. +No change, no markup! +\end{document} + + diff --git a/support/latexdiff/example/example-rev.tex b/support/latexdiff/example/example-rev.tex new file mode 100644 index 0000000000..4bcaf15627 --- /dev/null +++ b/support/latexdiff/example/example-rev.tex @@ -0,0 +1,60 @@ +\documentclass[12pt,a4paper]{article} + +\setlength{\topmargin}{-0.2in} +\setlength{\textheight}{9.5in} +\setlength{\oddsidemargin}{0.0in} + +\setlength{\textwidth}{6in} + +\title{latexdiff Example - Revised version} +\author{F Tilmann} +% Note how in the preamble visual markup is never used (even +% if some preamble might eventually end up as visible text.) + +\begin{document} +\maketitle + +\section*{Introduction} + +This is an extremely simple document that showcases some of the latexdiff features. +Type +\begin{verbatim} +latexdiff -t UNDERLINE example-draft.tex example-rev.tex > example-diff.tex +\end{verbatim} +to create the difference file. You can inspect this file directly. Then run either +\begin{verbatim} +pdflatex example-diff.tex +xpdf example-diff.pdf +\end{verbatim} +or +\begin{verbatim} +latex example-diff.tex +dvips -o example-diff.ps example-diff.dvi +gv example-diff.ps +\end{verbatim} +to display the markup. + +\section*{Yet another section title} + + More things could be said were it not for the constraints of time and space. + +A paragraph with a line only in the revised document. More things could be +said were it not for the constraints of time and space. + +And here is a typo. + +Here is a table: + +\begin{tabular}{ll} +Name & Description \\ +\hline +Gandalf & White \\ +Saruman & Evil +\end{tabular} + +And now for something completely different, with not a paragraph in sight. +No change, +no markup! +\end{document} + + diff --git a/support/latexdiff/latexdiff b/support/latexdiff/latexdiff new file mode 100755 index 0000000000..227cc2c45e --- /dev/null +++ b/support/latexdiff/latexdiff @@ -0,0 +1,5030 @@ +#!/usr/bin/env perl +##!/usr/bin/perl -w +# latexdiff - differences two latex files on the word level +# and produces a latex file with the differences marked up. +# +# Copyright (C) 2004-16 F J Tilmann (tilmann@gfz-potsdam.de) +# +# Repository/issue tracker: https://github.com/ftilmann/latexdiff +# CTAN page: http://www.ctan.org/pkg/latexdiff +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Detailed usage information at the end of the file +# + +# Version 1.3.0 (7 October 2018): +# - treat options to \documentclass as potential package names (some packages allow implicit loading of or imply selected packages +# - improved pattern matching: now allows nested angular brackets, and is no longer confused by escaped curly braces +# - improved pattern matching in COARSE mode: occasionally, the closing bracket or some other elements would be matched in an 'unnatural' way due to another sequence being more minimal in the computational sense, sometimes even causing errors due to tokens moving in or out of the scope of math environments. This is now discouraged by adding internal \DIFANCHOR commands (which are removed again in post-processing) (fixes issues reported via email by li_ruomeng . +# - verbatim and lstlisting environments are marked-up with line-by-line in a similar style to non-verbatim text (requires the listing package to be installed) +# (see new configuration variable VERBATIMLINEENV) (several issues and pull requests by jprotze) +# - --flatten: now supports \verbatiminput and \lstlistinput +# - --flatten: if file is not found, do not fail, simply warn and leave command unexpanded (inspired by issue #112). Don't warn if file name contains #[0-9] as it is then most likely an argument within a command definition rather than an actual file (applies to \input, \subfile, \include commands) +# - added to textcmds: \intertext +# - new config variable CUSTOMDIFCMD to allow defining special versions of commands in added or deleted blocks (Pull request by github user jprotze) +# - added option -no-links (mostly for use by latexdiff-vc in only-changes modes) (Pull request by github user jprotze) +# Bug fixes: +# - pattern matching of \verb and \lstinline commands had an error which meant they would trigger on commands beginning with \verb. +# - In description environments, mark up item descriptions by effectively reating the insides of item commannds as text commands (fixes #161) +# +# +# Version 1.2.1 (22 June 2017) +# - add "DeclareOldFontCommand" to styles using \bf or \sf old style font commands (fixies issue #92 ) +# - improved markup: process lstinline commands in listings package correctly +# for styles using colour, \verb and \lstinline arguments are marked up with colour (blue for added, red for deleted) +# - bug fix: protecting inline math expressions for mbox did not work as intended (see stack exchange question: http://tex.stackexchange.com/questions/359412/compiling-the-latexdiff-when-adding-a-subscript-before-a-pmatrix-environment-cau) +# - bug fix: when deleted \item commands are followed immediately by unsafe commands, they were not restored properly +# (thanks to J. Protze for pull request) (pull request #89) +# - treat lstlisting and comment as equivalent to verbatim environment +# make environments that are treated like verbatim environments configurable (config variable VERBATIMENV) +# treat lstinlne as equivalent to verb command +# partially addresses issue #38 +# - refactoring: set default configuration variables in a hash, and those that correspond to lists +# - feature: option --add-to-config used to amend configuration variables, which are regex pattern lists +# - bug fix: deleted figures when endfloat package is activated +# - bug fix: alignat environment now always processed correctly (fix issues #65) +# - bug fix: avoid processing of commands as potential files in routine init_regex_arr (fix issue #70 ) +# - minimal feature enhancement: treat '@' as allowed character in commands (strictly speaking requires prior \makeatletter statement, but always assuming it to be +# @ a letter if it is part of a command name will usually lead to the correct behaviour (see http://tex.stackexchange.com/questions/346651/latexdiff-and-let) +# - new feature/bug fix: --flatten option \endinput in included files now respected but only if \endinput stands right at the beginning of the line (issue #77) +# - bug fix: flatten would incorrectly attempt to process commented out \include commands (from discussion in issue #77 ) +# - introduce an invisible space (\hspace{0pt} after \mbox{..} auxiliary commands (not in math mode), to allow line breaks between added and deleted citations (change should not cause adverse behaviour otherwise) +# +# Version 1.2.0: +# - highlight new and deleted figures +# - bug fix in title mark-up. Previously deleted commands in title (such as \title, \author or \date) were marked up erroneously +# - (minor) bug fixes in new 1.1.1 features: disabled label was commented out twice, additional spaces were introduced before list environment begin and end commands +# - depracation fix: left brace in RegEx now needs to be escaped +# - add type PDFCOMMENT based on issue #49 submitted by github user peci1 (Martin Pecka) +# - make utf8 the default encoding +# +# Version 1.1.1 +# - patch mhchem: allow ce in equations +# - flatten now also expands \input etc. in the preamble (but not \usepackage!) +# - Better support for Japanese ( contributed by github user kshramt ) +# - prevent duplicated verbatim hashes (patch contributed by github user therussianjig, issue #36) +# - disable deleted label commands (fixes issue #31) +# - introduce post-processing to reinstate most deleted environments and all needed item commands (fixes issue #1) +# +# Version 1.1.0 +# - treat diacritics (\",\', etc) as safe commands +# - treat \_ and \& correctly as safe commands, even if used without spacing to the next word +# - Add a BOLD markup type that sets added text in bold face (Contribution by Victor Zabalza via pull request ) +# - add append-mboxsafecmd list option to be able to specify special safe commands which need to be surrounded by mbox to avoid breaking (mostly this is needed with ulem package) +# - support for siunitx and cleveref packages: protect \SI command in siunitx package and \cref,\Cref{range}{*} in cleveref packages (thanks to Stefan Pinnow for testing) +# - experimental support for chemformula, mhchem packages: define \ch and \ce in packages as safe (but not \ch,\cee in equation array environments) - these unfortunately will not be marked up (thanks to Stefan Pinnow for testing) +# - bug fix: packages identified correctly even if \usepackage command options extend over several lines (previously \usepackage command needed to be fully contained in one line) +# - new subtype ONLYCHANGEDPAGE outputs only changed pages (might not work well for floating material) +# - new subtype ZLABEL operates similarly to LABEL but uses absolute page numbers (needs zref package) +# - undocumented option --debug/--nodebug to override default setting for debug mode (Default: 0 for release version, 1: for development version +# +# Version 1.0.4 +# - introduce list UNSAFEMATHCMD, which holds list of commands which cannot be marked up with \DIFadd or \DIFdel commands (only relevant for WHOLE and COARSE math markup modes) +# - new subtype LABEL which gives each change a label. This can later be used to only display pages where changes +# have been made (instructions for that are put as comments into the diff'ed file) inspired by answer on http://tex.stackexchange.com/questions/166049/invisible-markers-in-pdfs-using-pdflatex +# - Configuration variables take into accout some commands from additional packages: +# tikzpicture environment now treated as PICTUREENV, and \smallmatrix in ARRENV (amsmath) +# - --flatten: support for \subfile command (subfiles package) (in response to http://tex.stackexchange.com/questions/167620/latexdiff-with-subfiles ) +# - --flatten: \bibliography commands expand if corresponding bbl file present +# - angled bracket optional commands now parsed correctly (patch #3570) submitted by Dave Kleinschmidt (thanks) +# - \RequirePackage now treated as synonym of \usepackage with respect to setting packages +# - special rules for apacite package (redefine citation commands) +# - recognise /dev/null as 'file-like' arguments for --preamble and --config options +# - fix units package incompatibility with ulem for text maths statements $ ..$ (thanks to Stuart Prescott for reporting this) +# - amsmath environment cases treated correctly (Bug fix #19029) (thanks to Jalar) +# - {,} in comments no longer confuse latexdiff (Bug fix #19146) +# - \% in one-letter sub/Superscripts was not converted correctly +# +# Version 1.0.3 +# - fix bug in add_safe_commands that made latexdiff hang on DeclareMathOperator +# command in preamble +# - \(..\) inline math expressions were not parsed correctly, if they contained a linebreak +# - applied patch contributed by tomflannaghan via Berlios: [ Patch #3431 ] Adds correct handling of \left< and \right> +# - \$ is treated correctly as a literal dollar sign (thanks to Reed Cartwright and Joshua Miller for reporting this bug +# and sketching out the solution) +# - \^ and \_ are correctly interpreted as accent and underlined space, respectively, not as superscript of subscript +# (thanks to Wail Yahyaoui for pointing out this bug) +# +# Version 1.0.1 - treat \big,\bigg etc. equivalently to \left and +# \right - include starred version in MATHENV - apply +# - flatten recursively and --flatten expansion is now +# aware of comments (thanks to Tim Connors for patch) +# - Change to post-processing for more reliability for +# deleted math environments +# - On linux systems, recognise and remove DOS style newlines +# - Provide markup for some special preamble commands (\title, +# \author,\date, +# - configurable by setting context2cmd +# - for styles using ulem package, remove \emph and \text.. from list of +# safe commands in order to allow linebreaks within the +# highlighted sections. +# - for ulem style, now show citations by enclosing them in \mbox commands. +# This unfortunately implies linebreaks within citations no longer function, +# so this functionality can be turned off (Option --disable-citation-markup). +# With --enable-citation-markup, the mbox markup is forced for other styles) +# - new substyle COLOR. This is particularly useful for marking up citations +# and some special post-processing is implemented to retain cite +# commands in deleted blocks. +# - four different levels of math-markup +# - Option --driver for choosing driver for modes employing changebar package +# - accept \\* as valid command (and other commands of form \.*). Also accept +# \ (backslashed newline) +# - some typo fixes, include commands defined in preamble as safe commands +# (Sebastian Gouezel) +# - include compared filenames as comments as line 2 and 3 of +# the preamble (can be modified with option --label, and suppressed with +# --no-label), option --visible-label to show files in generated pdf or dvi +# at the beginning of main document +# +# Version 0.5 A number of minor improvements based on feedback +# Deleted blocks are now shown before added blocks +# Package specific processing +# +# Version 0.43 unreleased typo in list of styles at the end +# Add protect to all \cbstart, \cbend commands +# More robust substitution of deleted math commands +# +# Version 0.42 November 06 Bug fixes only +# +# Version 0.4 March 06 option for fast differencing using UNIX diff command, several minor bug fixes (\par bug, improved highlighting of textcmds) +# +# Version 0.3 August 05 improved parsing of displayed math, --allow-spaces +# option, several minor bug fixes +# +# Version 0.25 October 04 Fix bug with deleted equations, add math mode commands to safecmd, add | to allowed interpunctuation signs +# Version 0.2 September 04 extension to utf-8 and variable encodings +# Version 0.1 August 04 First public release + +use Algorithm::Diff qw(traverse_sequences); + +use Getopt::Long ; +use strict ; +use warnings; +use utf8 ; + +my ($algodiffversion)=split(/ /,$Algorithm::Diff::VERSION); + + +my ($versionstring)=< 3, # minimum number of tokens to form an independent block + # shorter identical blocks will be merged to the previous word + SCALEDELGRAPHICS => 0.5, # factor with which deleted figures will be scaled down (i.e. 0.5 implies they are shown at half linear size) + # this is only used for --graphics-markup=BOTH option + FLOATENV => undef , # Environments in which FL variants of defined commands are used + PICTUREENV => undef , # Environments in which all change markup is removed + MATHENV => undef , # Environments turning on display math mode (code also knows about \[ and \]) + MATHREPL => 'displaymath', # Environment introducing deleted maths blocks + MATHARRENV => undef , # Environments turning on eqnarray math mode + MATHARRREPL => 'eqnarray*', # Environment introducing deleted maths blocks + ARRENV => undef , # Environments making arrays in math mode. The underlining style does not cope well with those - as a result in-text math environments are surrounded by \mbox{ } if any of these commands is used in an inline math block + COUNTERCMD => undef, + # COUNTERCMD textcmds which are associated with a counter + # If any of these commands occur in a deleted block + # they will be succeeded by an \addtocounter{...}{-1} + # for the associated counter such that the overall numbers + # should be the same as in the new file + LISTENV => undef , # list making environments - they will generally be kept + VERBATIMENV => undef, # Environments whose content should be treated as verbatim text and not be touched + VERBATIMLINEENV => undef, # Environments whose content should be treated as verbatim text and processed in line diff mode + CUSTOMDIFCMD => undef,# Custom dif command. Is defined in the document as a \DELcommand and \ADDcommand version to be replaced by the diff + ITEMCMD => 'item' # command marking item in a list environment +); +# Configuration variables: these have to be visible from the subroutines +my ($ARRENV, + $COUNTERCMD, + $FLOATENV, + $ITEMCMD, + $LISTENV, + $MATHARRENV, + $MATHARRREPL, + $MATHENV, + $MATHREPL, + $MINWORDSBLOCK, + $PICTUREENV, + $SCALEDELGRAPHICS, + $VERBATIMENV, + $VERBATIMLINEENV, + $CUSTOMDIFCMD + ); + +# my $MINWORDSBLOCK=3; # minimum number of tokens to form an independent block +# # shorter identical blocks will be merged to the previous word +# my $SCALEDELGRAPHICS=0.5; # factor with which deleted figures will be scaled down (i.e. 0.5 implies they are shown at half linear size) +# # this is only used for --graphics-markup=BOTH option +# my $FLOATENV='(?:figure|table|plate)[\w\d*@]*' ; # Environments in which FL variants of defined commands are used +# my $PICTUREENV='(?:picture|tikzpicture|DIFnomarkup)[\w\d*@]*' ; # Environments in which all change markup is removed +# my $MATHENV='(?:equation[*]?|displaymath|DOLLARDOLLAR)[*]?' ; # Environments turning on display math mode (code also knows about \[ and \]) +# my $MATHREPL='displaymath'; # Environment introducing deleted maths blocks +# my $MATHARRENV='(?:eqnarray|align|alignat|gather|multline|flalign)[*]?' ; # Environments turning on eqnarray math mode +# my $MATHARRREPL='eqnarray*'; # Environment introducing deleted maths blocks +# my $ARRENV='(?:aligned|gathered|array|[pbvBV]?matrix|smallmatrix|cases|split)'; # Environments making arrays in math mode. The underlining style does not cope well with those - as a result in-text math environments are surrounded by \mbox{ } if any of these commands is used in an inline math block +# my $COUNTERCMD='(?:footnote|part|chapter|section|subsection|subsubsection|paragraph|subparagraph)'; # textcmds which are associated with a counter +# # If any of these commands occur in a deleted block +# # they will be succeeded by an \addtocounter{...}{-1} +# # for the associated counter such that the overall numbers +# # should be the same as in the new file +# my $LISTENV='(?:itemize|description|enumerate)'; # list making environments - they will generally be kept +# my $ITEMCMD='item'; # command marking item in a list environment + + + +my $LABELCMD='(?:label)'; # matching commands are disabled within deleted blocks - mostly useful for maths mode, as otherwise it would be fine to just not add those to SAFECMDLIST +my @UNSAFEMATHCMD=('qedhere'); # Commands which are definitely unsafe for marking up in math mode (amsmath qedhere only tested to not work with UNDERLINE markup) (only affects WHOLE and COARSE math markup modes). Note that unlike text mode (or FINE math mode0 deleted unsafe commands are not deleted but simply taken outside \DIFdel +my $MBOXINLINEMATH=0; # if set to 1 then surround marked-up inline maths expression with \mbox ( to get around compatibility + # problems between some maths packages and ulem package + + +# Markup strings +# If at all possible, do not change these as parts of the program +# depend on the actual name (particularly post-processing) +# At the very least adapt subroutine postprocess to new tokens. +my $ADDMARKOPEN='\DIFaddbegin '; # Token to mark begin of appended text +my $ADDMARKCLOSE='\DIFaddend '; # Token to mark end of appended text +my $ADDOPEN='\DIFadd{'; # To mark begin of added text passage +my $ADDCLOSE='}'; # To mark end of added text passage +my $ADDCOMMENT='DIF > '; # To mark added comment line +my $DELMARKOPEN='\DIFdelbegin '; # Token to mark begin of deleted text +my $DELMARKCLOSE='\DIFdelend '; # Token to mark end of deleted text +my $DELOPEN='\DIFdel{'; # To mark begin of deleted text passage +my $DELCLOSE='}'; # To mark end of deleted text passage +my $DELCMDOPEN='%DIFDELCMD < '; # To mark begin of deleted commands (must begin with %, i.e., be a comment +my $DELCMDCLOSE="%%%\n"; # To mark end of deleted commands (must end with a new line) +my $AUXCMD='%DIFAUXCMD' ; # follows auxiliary commands put in by latexdiff to make difference file legal + # auxiliary commands must be on a line of their own + # Note that for verbatim environment openings the %DIFAUXCMD cannot be placed in + # the same line as this would mean they are shown + # so the special form "%DIFAUXCMD NEXT" is used to indicate that the next line + # is an auxiliary command + # Similarly "%DIFAUXCMD LAST" would indicate the auxiliary command is in previous line (not currently used) +my $DELCOMMENT='DIF < '; # To mark deleted comment line +my $VERBCOMMENT='DIFVRB '; # to mark lines which are within a verbatim environment + +# main local variables: +my @TEXTCMDLIST=(); # array containing patterns of commands with text arguments +my @TEXTCMDEXCL=(); # array containing patterns of commands without text arguments (if a pattern + # matches both TEXTCMDLIST and TEXTCMDEXCL it is excluded) +my @CONTEXT1CMDLIST=(); # array containing patterns of commands with text arguments (subset of text commands), + # but which cause confusion if used out of context (e.g. \caption). + # In deleted passages, the command will be disabled but its argument is marked up + # Otherwise they behave exactly like TEXTCMD's +my @CONTEXT1CMDEXCL=(); # exclude list for above, but always empty +my @CONTEXT2CMDLIST=(); # array containing patterns of commands with text arguments, but which fail or cause confusion + # if used out of context (e.g. \title). They and their arguments will be disabled in deleted + # passages +my @CONTEXT2CMDEXCL=(); # exclude list for above, but always empty +my @MATHTEXTCMDLIST=(); # treat like textcmd. If a textcmd is in deleted or added block, just wrap the + # whole content with \DIFadd or \DIFdel irrespective of content. This functionality + # is useful for pseudo commands \MATHBLOCK.. into which math environments are being + # transformed +my @MATHTEXTCMDEXCL=(); # + +# Note I need to declare this with "our" instead of "my" because later in the code I have to "local"ise these +our @SAFECMDLIST=(); # array containing patterns of safe commands (which do not break when in the argument of DIFadd or DIFDEL) +our @SAFECMDEXCL=(); +my @MBOXCMDLIST=(); # patterns for commands which are in principle safe but which need to be surrounded by an \mbox +my @MBOXCMDEXCL=(); # all the patterns in MBOXCMDLIST will be appended to SAFECMDLIST + + +my ($i,$j,$l); +my ($old,$new); +my ($line,$key); +my (@dumlist); +my ($newpreamble,$oldpreamble); +my (@newpreamble,@oldpreamble,@diffpreamble,@diffbody); +my ($latexdiffpreamble); +my ($oldbody, $newbody, $diffbo); +my ($oldpost, $newpost); +my ($diffall); +# Option names +my ($type,$subtype,$floattype,$config,$preamblefile,$encoding,$nolabel,$visiblelabel, + $showpreamble,$showsafe,$showtext,$showconfig,$showall, + $replacesafe,$appendsafe,$excludesafe, + $replacetext,$appendtext,$excludetext, + $replacecontext1,$appendcontext1, + $replacecontext2,$appendcontext2, + $help,$verbose,$driver,$version,$ignorewarnings, + $enablecitmark,$disablecitmark,$allowspaces,$flatten,$nolinks,$debug,$earlylatexdiffpreamble); ###$disablemathmark, +my ($mboxsafe); +# MNEMNONICS for mathmarkup +my $mathmarkup; +use constant { + OFF => 0, + WHOLE => 1, + COARSE => 2, + FINE => 3 +}; +# MNEMNONICS for graphicsmarkup +my $graphicsmarkup; +use constant { + NONE => 0, + NEWONLY => 1, + BOTH => 2 +}; + +my ($mboxcmd); + +my (@configlist,@addtoconfiglist,@labels, + @appendsafelist,@excludesafelist, + @appendmboxsafelist,@excludemboxsafelist, + @appendtextlist,@excludetextlist, + @appendcontext1list,@appendcontext2list, + @packagelist); +my ($assign,@config); +# Hash where keys corresponds to the names of all included packages (including the documentclass as another package +# the optional arguments to the package are the values of the hash elements +my ($pkg,%packages); + +# Defaults +$mathmarkup=COARSE; +$verbose=0; +# output debug and intermediate files, set to 0 in final distribution +$debug=0; +# insert preamble directly after documentclass - experimental feature, set to 0 in final distribution +# Note that this failed with mini example (or other files, where packages used in latexdiff preamble +# are called again with incompatible options in preamble of resulting file) +$earlylatexdiffpreamble=0; + + +# define character properties +sub IsNonAsciiPunct { return <<'END' # Unicode punctuation but excluding ASCII punctuation ++utf8::IsPunct +-utf8::IsASCII +END +} +sub IsNonAsciiS { return <<'END' # Unicode symbol but excluding ASCII ++utf8::IsS +-utf8::IsASCII +END +} + + +my %verbhash; + +Getopt::Long::Configure('bundling'); +GetOptions('type|t=s' => \$type, + 'subtype|s=s' => \$subtype, + 'floattype|f=s' => \$floattype, + 'config|c=s' => \@configlist, + 'add-to-config=s' => \@addtoconfiglist, + 'preamble|p=s' => \$preamblefile, + 'encoding|e=s' => \$encoding, + 'label|L=s' => \@labels, + 'no-label' => \$nolabel, + 'visible-label' => \$visiblelabel, + 'exclude-safecmd|A=s' => \@excludesafelist, + 'replace-safecmd=s' => \$replacesafe, + 'append-safecmd|a=s' => \@appendsafelist, + 'exclude-textcmd|X=s' => \@excludetextlist, + 'replace-textcmd=s' => \$replacetext, + 'append-textcmd|x=s' => \@appendtextlist, + 'replace-context1cmd=s' => \$replacecontext1, + 'append-context1cmd=s' => \@appendcontext1list, + 'replace-context2cmd=s' => \$replacecontext2, + 'append-context2cmd=s' => \@appendcontext2list, + 'exclude-mboxsafecmd=s' => \@excludemboxsafelist, + 'append-mboxsafecmd=s' => \@appendmboxsafelist, + 'show-preamble' => \$showpreamble, + 'show-safecmd' => \$showsafe, + 'show-textcmd' => \$showtext, + 'show-config' => \$showconfig, + 'show-all' => \$showall, + 'packages=s' => \@packagelist, + 'allow-spaces' => \$allowspaces, + 'math-markup=s' => \$mathmarkup, + 'graphics-markup=s' => \$graphicsmarkup, + 'enable-citation-markup|enforce-auto-mbox' => \$enablecitmark, + 'disable-citation-markup|disable-auto-mbox' => \$disablecitmark, + 'verbose|V' => \$verbose, + 'ignore-warnings' => \$ignorewarnings, + 'driver=s'=> \$driver, + 'flatten' => \$flatten, + 'no-links' => \$nolinks, + 'version' => \$version, + 'help|h' => \$help, + 'debug!' => \$debug ) or die "Use latexdiff -h to get help.\n" ; + +if ( $help ) { + usage() ; +} + + +if ( $version ) { + die $versionstring ; +} + +print STDERR $versionstring if $verbose; + +if (defined($showall)){ + $showpreamble=$showsafe=$showtext=$showconfig=1; +} +# Default types +$type='UNDERLINE' unless defined($type); +$subtype='SAFE' unless defined($subtype); +# set floattype to IDENTICAL for LABEL and ONLYCHANGEDPAGE subtype, unless it has been set explicitly on the command line +$floattype=($subtype eq 'LABEL' || $subtype eq 'ONLYCHANGEDPAGE') ? 'IDENTICAL' : 'FLOATSAFE' unless defined($floattype); +if ( $subtype eq 'LABEL' ) { + print STDERR "Note that LABEL subtype is deprecated. If possible, use ZLABEL instead (requires zref package)"; +} + +if (defined($mathmarkup)) { + $mathmarkup=~tr/a-z/A-Z/; + if ( $mathmarkup eq 'OFF' ){ + $mathmarkup=OFF; + } elsif ( $mathmarkup eq 'WHOLE' ){ + $mathmarkup=WHOLE; + } elsif ( $mathmarkup eq 'COARSE' ){ + $mathmarkup=COARSE; + } elsif ( $mathmarkup eq 'FINE' ){ + $mathmarkup=FINE; + } elsif ( $mathmarkup !~ m/^[0123]$/ ) { + die "latexdiff Illegal value: ($mathmarkup) for option--math-markup. Possible values: OFF,WHOLE,COARSE,FINE,0-3\n"; + } + # else use numerical value +} + + + +# setting extra preamble commands +if (defined($preamblefile)) { + $latexdiffpreamble=join "\n",(extrapream($preamblefile),""); +} else { + $latexdiffpreamble=join "\n",(extrapream($type,$subtype,$floattype),""); +} + +if ( defined($driver) ) { + # for changebar only + $latexdiffpreamble=~s/\[dvips\]/[$driver]/sg; +} +# setting up @SAFECMDLIST and @SAFECMDEXCL +if (defined($replacesafe)) { + init_regex_arr_ext(\@SAFECMDLIST,$replacesafe); +} else { + init_regex_arr_data(\@SAFECMDLIST, "SAFE COMMANDS"); +} +foreach $appendsafe ( @appendsafelist ) { + init_regex_arr_ext(\@SAFECMDLIST, $appendsafe); +} +foreach $excludesafe ( @excludesafelist ) { + init_regex_arr_ext(\@SAFECMDEXCL, $excludesafe); +} +# setting up @MBOXCMDLIST and @MBOXCMDEXCL +foreach $mboxsafe ( @appendmboxsafelist ) { + init_regex_arr_ext(\@MBOXCMDLIST, $mboxsafe); +} +foreach $mboxsafe ( @excludemboxsafelist ) { + init_regex_arr_ext(\@MBOXCMDEXCL, $mboxsafe); +} + + + +# setting up @TEXTCMDLIST and @TEXTCMDEXCL +if (defined($replacetext)) { + init_regex_arr_ext(\@TEXTCMDLIST,$replacetext); +} else { + init_regex_arr_data(\@TEXTCMDLIST, "TEXT COMMANDS"); +} +foreach $appendtext ( @appendtextlist ) { + init_regex_arr_ext(\@TEXTCMDLIST, $appendtext); +} +foreach $excludetext ( @excludetextlist ) { + init_regex_arr_ext(\@TEXTCMDEXCL, $excludetext); +} + + +# setting up @CONTEXT1CMDLIST ( @CONTEXT1CMDEXCL exist but is always empty ) +if (defined($replacecontext1)) { + init_regex_arr_ext(\@CONTEXT1CMDLIST,$replacecontext1); +} else { + init_regex_arr_data(\@CONTEXT1CMDLIST, "CONTEXT1 COMMANDS"); +} +foreach $appendcontext1 ( @appendcontext1list ) { + init_regex_arr_ext(\@CONTEXT1CMDLIST, $appendcontext1); +} + + +# setting up @CONTEXT2CMDLIST ( @CONTEXT2CMDEXCL exist but is always empty ) +if (defined($replacecontext2)) { + init_regex_arr_ext(\@CONTEXT2CMDLIST,$replacecontext2); +} else { + init_regex_arr_data(\@CONTEXT2CMDLIST, "CONTEXT2 COMMANDS"); +} +foreach $appendcontext2 ( @appendcontext2list ) { + init_regex_arr_ext(\@CONTEXT2CMDLIST, $appendcontext2); +} + +# setting configuration variables +@config=(); +foreach $config ( @configlist ) { + if (-f $config || lc $config eq '/dev/null' ) { + open(FILE,$config) or die ("Couldn't open configuration file $config: $!"); + while () { + chomp; + next if /^\s*#/ || /^\s*%/ || /^\s*$/ ; + push (@config,$_); + } + close(FILE); + } + else { +# foreach ( split(",",$config) ) { +# push @config,$_; +# } + push @config,split(",",$config) + } +} +foreach $assign ( @config ) { + $assign=~ m/\s*(\w*)\s*=\s*(\S*)\s*$/ or die "Illegal assignment $assign in configuration list (must be variable=value)"; + exists $CONFIG{$1} or die "Unknown configuration variable $1."; + $CONFIG{$1}=$2; +} + +my @addtoconfig=(); +foreach $config ( @addtoconfiglist ) { + if (-f $config || lc $config eq '/dev/null' ) { + open(FILE,$config) or die ("Couldn't open addd-to-config file $config: $!"); + while () { + chomp; + next if /^\s*#/ || /^\s*%/ || /^\s*$/ ; + push (@addtoconfig,$_); + } + close(FILE); + } + else { +# foreach ( split(",",$config) ) { +# push @addtoconfig,$_; +# } + push @addtoconfig,split(",",$config) + } +} + +# initialise default lists from DATA +# for those configuration variables, which have not been set explicitly, initiate from list in document +foreach $key ( keys(%CONFIG) ) { + if (!defined $CONFIG{$key}) { + @dumlist=(); + init_regex_arr_data(\@dumlist,"$key CONFIG"); + $CONFIG{$key}=join(";",@dumlist) + } +} + + +foreach $assign ( @addtoconfig ) { + ###print STDERR "assign:|$assign|\n"; + $assign=~ m/\s*(\w*)\s*=\s*(\S*)\s*$/ or die "Illegal assignment $assign in configuration list (must be variable=value)"; + exists $CONFIG{$1} or die "Unknown configuration variable $1."; + $CONFIG{$1}.=";$2"; +} + +# Map from hash to variables (we do this to have more concise code later, change from comma-separated list) +foreach ( keys(%CONFIG) ) { + if ( $_ eq "MINWORDSBLOCK" ) { $MINWORDSBLOCK = $CONFIG{$_}; } + elsif ( $_ eq "FLOATENV" ) { $FLOATENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "ITEMCMD" ) { $ITEMCMD = $CONFIG{$_} ; } + elsif ( $_ eq "LISTENV" ) { $LISTENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "PICTUREENV" ) { $PICTUREENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "MATHENV" ) { $MATHENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "MATHREPL" ) { $MATHREPL = $CONFIG{$_} ; } + elsif ( $_ eq "MATHARRENV" ) { $MATHARRENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "MATHARRREPL" ) { $MATHARRREPL = $CONFIG{$_} ; } + elsif ( $_ eq "ARRENV" ) { $ARRENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "VERBATIMENV" ) { $VERBATIMENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "VERBATIMLINEENV" ) { $VERBATIMLINEENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "CUSTOMDIFCMD" ) { $CUSTOMDIFCMD = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "COUNTERCMD" ) { $COUNTERCMD = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "SCALEDELGRAPHICS" ) { $SCALEDELGRAPHICS = $CONFIG{$_} ; } + else { die "Unknown configuration variable $_.";} +} + +if ( $mathmarkup == COARSE || $mathmarkup == WHOLE ) { + push(@MATHTEXTCMDLIST,qr/^MATHBLOCK(?:$MATHENV|$MATHARRENV|SQUAREBRACKET)$/); +} + + + +foreach $pkg ( @packagelist ) { + map { $packages{$_}="" } split(/,/,$pkg) ; +} + + + +if ($showconfig || $showtext || $showsafe || $showpreamble) { + show_configuration(); + exit 0; +} + +if ( @ARGV != 2 ) { + print STDERR "2 and only 2 non-option arguments required. Write latexdiff -h to get help\n"; + exit(2); +} + +# Are extra spaces between command arguments permissible? +my $extraspace; +if ($allowspaces) { + $extraspace='\s*'; +} else { + $extraspace=''; +} + +# append context lists to text lists (as text property is implied) +push @TEXTCMDLIST, @CONTEXT1CMDLIST; +push @TEXTCMDLIST, @CONTEXT2CMDLIST; + +push @TEXTCMDLIST, @MATHTEXTCMDLIST if $mathmarkup==COARSE; + +# internal additions to SAFECMDLIST +push(@SAFECMDLIST, qr/^QLEFTBRACE$/, qr/^QRIGHTBRACE$/); + + +# Patterns. These are used by some of the subroutines, too +# I can only define them down here because value of extraspace depends on an option + + my $pat0 = '(?:[^{}])*'; + my $pat_n = $pat0; +# if you get "undefined control sequence MATHBLOCKmath" error, increase the maximum value in this loop + for (my $i_pat = 0; $i_pat < 20; ++$i_pat){ + $pat_n = '(?:[^{}]|\{'.$pat_n.'\}|\\\\\{|\\\\\})*'; + # Actually within the text body, quoted braces are replaced in pre-processing. The only place where + # the last part of the pattern matters is when processing the arguments of context2cmds in the preamble + # and these contain a \{ or \} combination, probably rare. + # It should thus be fine to use the simpler version below. + ### $pat_n = '(?:[^{}]|\{'.$pat_n.'\})*'; + } + + my $brat0 = '(?:[^\[\]]|\\\[|\\\])*'; + my $brat_n = $brat0; + for (my $i_pat = 0; $i_pat < 4; ++$i_pat){ + $brat_n = '(?:[^\[\]]|\['.$brat_n.'\]|\\\[|\\\])*'; + ### $brat_n = '(?:[^\[\]]|\['.$brat_n.'\])*'; # Version not taking into account escaped \[ and \] + } + my $abrat0 = '(?:[^<>])*'; + + my $quotemarks = '(?:\'\')|(?:\`\`)'; + my $punct='[0.,\/\'\`:;\"\?\(\)\[\]!~\p{IsNonAsciiPunct}\p{IsNonAsciiS}]'; + my $number='-?\d*\.\d*'; + my $mathpunct='[+=<>\-\|]'; + my $and = '&'; + my $coords= '[\-.,\s\d]*'; +# quoted underscore - this needs special treatment as perl treats _ as a letter (\w) but latex does not +# such that a\_b is interpreted as a{\_}b by latex but a{\_b} by perl + my $quotedunderscore='\\\\_'; +# word: sequence of letters or accents followed by letter + my $word_ja='\p{Han}+|\p{InHiragana}+|\p{InKatakana}+'; + my $word='(?:' . $word_ja . '|(?:(?:[-\w\d*]|\\\\[\"\'\`~^][A-Za-z\*])(?!(?:' . $word_ja . ')))+)'; + my $cmdleftright='\\\\(?:left|right|[Bb]igg?[lrm]?|middle)\s*(?:[<>()\[\]|\.]|\\\\(?:[|{}]|\w+))'; + my $cmdoptseq='\\\\[\w\d@\*]+'.$extraspace.'(?:(?:<'.$abrat0.'>|\['.$brat_n.'\]|\{'. $pat_n . '\}|\(' . $coords .'\))'.$extraspace.')*'; + my $defseq='\\\\def\\\\[\w\d@\*]+(?:#\d+|\[#\d+\])+(?:\{'. $pat_n . '\})?'; + my $backslashnl='\\\\\n'; + my $oneletcmd='\\\\.\*?(?:\['.$brat_n.'\]|\{'. $pat_n . '\})*'; + my $math='\$(?:[^$]|\\\$)*?\$|\\\\[(](?:.|\n)*?\\\\[)]'; +## the current maths command cannot cope with newline within the math expression + my $comment='%[^\n]*\n'; + my $pat=qr/(?:\A\s*)?(?:${and}|${quotemarks}|${number}|${word}|$quotedunderscore|${defseq}|$cmdleftright|${cmdoptseq}|${math}|${backslashnl}|${oneletcmd}|${comment}|${punct}|${mathpunct}|\{|\})\s*/ ; + + + + +# now we are done setting up and can start working +my ($oldfile, $newfile) = @ARGV; +# check for existence of input files +if ( ! -e $oldfile ) { + die "Input file $oldfile does not exist"; +} +if ( ! -e $newfile ) { + die "Input file $newfile does not exist"; +} + + +# set the labels to be included into the file +# first find out which file name is longer for correct alignment +my ($diff,$oldlabel_n_spaces,$newlabel_n_spaces); +$oldlabel_n_spaces = 0; +$newlabel_n_spaces = 0; +$diff = length($newfile) - length($oldfile); +if ($diff > 0) { + $oldlabel_n_spaces = $diff; +} +if ($diff < 0) { + $newlabel_n_spaces = abs($diff); +} + +my ($oldtime,$newtime,$oldlabel,$newlabel); +if (defined($labels[0])) { + $oldlabel=$labels[0] ; +} else { + $oldtime=localtime((stat($oldfile))[9]); + $oldlabel="$oldfile " . " "x($oldlabel_n_spaces) . $oldtime; +} +if (defined($labels[1])) { + $newlabel=$labels[1] ; +} else { + $newtime=localtime((stat($newfile))[9]); + $newlabel="$newfile " . " "x($newlabel_n_spaces) . $newtime; +} + +$encoding=guess_encoding($newfile) unless defined($encoding); + +$encoding = "utf8" if $encoding =~ m/^utf8/i ; +if (lc($encoding) eq "utf8" ) { + binmode(STDOUT, ":utf8"); + binmode(STDERR, ":utf8"); +} + +$old=read_file_with_encoding($oldfile,$encoding); +$new=read_file_with_encoding($newfile,$encoding); + + + + +# reset time +exetime(1); +($oldpreamble,$oldbody,$oldpost)=splitdoc($old,'\\\\begin\{document\}','\\\\end\{document\}'); + +($newpreamble,$newbody,$newpost)=splitdoc($new,'\\\\begin\{document\}','\\\\end\{document\}'); + + +if ($flatten) { + $oldbody=flatten($oldbody,$oldpreamble,$oldfile,$encoding); + $newbody=flatten($newbody,$newpreamble,$newfile,$encoding); + # flatten preamble + $oldpreamble=flatten($oldpreamble,$oldpreamble,$oldfile,$encoding); + $newpreamble=flatten($newpreamble,$newpreamble,$newfile,$encoding); + +} + + + + +my @auxlines; + +# boolean variab +my ($ulem)=0; + +if ( length $oldpreamble && length $newpreamble ) { + # pre-process preamble by looking for commands used in \maketitle (title, author, date etc commands) + # and marking up content with latexdiff markup + @auxlines=preprocess_preamble($oldpreamble,$newpreamble); + + @oldpreamble = split /\n/, $oldpreamble; + @newpreamble = split /\n/, $newpreamble; + + # If a command is defined in the preamble of the new file, and only uses safe commands, then it can be considered to be safe) (contribution S. Gouezel) + # Base this assessment on the new preamble + add_safe_commands($newpreamble); + + # get a list of packages from preamble if not predefined + %packages=list_packages($newpreamble) unless %packages; + if ( %packages && $debug ) { my $key ; foreach $key (keys %packages) { print STDERR "DEBUG \\usepackage[",$packages{$key},"]{",$key,"}\n" ;} } +} + +# have to return to all processing to properly add preamble additions based on packages found +if (defined($graphicsmarkup)) { + $graphicsmarkup=~tr/a-z/A-Z/; + if ( $graphicsmarkup eq 'OFF' or $graphicsmarkup eq 'NONE' ) { + $graphicsmarkup=NONE; + } elsif ( $graphicsmarkup eq 'NEWONLY' or $graphicsmarkup eq 'NEW-ONLY' ) { + $graphicsmarkup=NEWONLY; + } elsif ( $graphicsmarkup eq 'BOTH' ) { + $graphicsmarkup=BOTH; + } elsif ( $graphicsmarkup !~ m/^[012]$/ ) { + die "latexdiff Illegal value: ($graphicsmarkup) for option --highlight-graphics. Possible values: OFF,WHOLE,COARSE,FINE,0-2\n"; + } + # else use numerical value +} else { + # Default: no explicit setting in menu + if ( defined $packages{"graphicx"} or defined $packages{"graphics"} ) { + $graphicsmarkup=NEWONLY; + } else { + $graphicsmarkup=NONE; + } +} + +if (defined $packages{"hyperref"} ) { + # deleted lines should not generate or appear in link names: + print STDERR "hyperref package detected.\n" if $verbose ; + $latexdiffpreamble =~ s/\{\\DIFadd\}/{\\DIFaddtex}/g; + $latexdiffpreamble =~ s/\{\\DIFdel\}/{\\DIFdeltex}/g; + $latexdiffpreamble .= join "\n",(extrapream("HYPERREF"),""); + if($nolinks){ + $latexdiffpreamble .= "\n\\hypersetup{bookmarks=false}"; + } + ### $latexdiffpreamble .= '%DIF PREAMBLE EXTENSION ADDED BY LATEXDIFF FOR HYPERREF PACKAGE' . "\n"; + ### $latexdiffpreamble .= '\providecommand{\DIFadd}[1]{\texorpdfstring{\DIFaddtex{#1}}{#1}}' . "\n"; + ### $latexdiffpreamble .= '\providecommand{\DIFdel}[1]{\texorpdfstring{\DIFdeltex{#1}}{}}' . "\n"; + ### $latexdiffpreamble .= '%DIF END PREAMBLE EXTENSION ADDED BY LATEXDIFF FOR HYPERREF PACKAGE' . "\n"; +} + +# add commands for figure highlighting to preamble +if ($graphicsmarkup != NONE ) { + my @matches; + # Check if \DIFaddbeginFL definition calls \DIFaddbegin - if so we will issue an error message that graphics highlighting is + # is not compatible with this. + # (A more elegant solution would be to suppress the redefinitions of the \DIFaddbeginFL etc commands, but for this narrow use case + # I currently don't see this as an efficient use of time) + ### The foreach loop does not make sense here. I don't know why I put this in - (F Tilmann) + ###foreach my $cmd ( "DIFaddbegin","DIFaddend","DIFdelbegin","DIFdelend" ) { + @matches=( $latexdiffpreamble =~ m/command\{\\DIFaddbeginFL}\{($pat_n)}/sg ) ; + # we look at the last one of the list to take into account possible redefinition but almost always matches should have exactly one element + if ( $matches[$#matches] =~ m/\\DIFaddbegin/ ) { + die "Cannot combine graphics markup with float styles defining \\DIFaddbeginFL in terms of \\DIFaddbegin. Use --graphics-markup=none option or choose a different float style."; + exit 10; + } + ###} + $latexdiffpreamble .= join "\n",("\\newcommand{\\DIFscaledelfig}{$SCALEDELGRAPHICS}",extrapream("HIGHLIGHTGRAPHICS"),""); + + # only change required for highlighting both is to declare \includegraphics safe, as preamble already contains commands for deleted environment + if ( $graphicsmarkup == BOTH ) { + init_regex_arr_list(\@SAFECMDLIST,'includegraphics'); + } +} + +$ulem = ($latexdiffpreamble =~ /\\RequirePackage(?:\[$brat_n\])?\{ulem\}/ || defined $packages{"ulem"}); + + +# If listings is being used or can be found in the latexdiff search path, add to the preamble auxiliary code to enable line-by-line markup +if ( defined($packages{"listings"}) or `kpsewhich listings.sty` ne "" ) { + my @listingpreamble=extrapream("LISTINGS"); + my @listingDIFcode=(); + my $replaced; + # note that in case user supplies preamblefile the type might not reflect well the + @listingDIFcode=extrapream("-nofail","DIFCODE_" . $type) unless defined($preamblefile); + if (!(@listingDIFcode)) { + # if listingDIFcode is empty try to guess a suitable one from the preamble + if ($latexdiffpreamble =~ /\\RequirePackage(?:\[$brat_n\])?\{color\}/ and $ulem ) { + @listingDIFcode=extrapream("DIFCODE_UNDERLINE"); + } elsif ( $latexdiffpreamble =~ /\\RequirePackage(?:\[$brat_n\])?\{color\}/ ) { + # only colour used + @listingDIFcode=extrapream("DIFCODE_CFONT"); + } else { + # fall-back solution + @listingDIFcode=extrapream("DIFCODE_BOLD"); + } + } + # now splice it in + $replaced=0; + ###print STDERR "DEBUG: listingDIFcode: ",join("\n",@listingDIFcode),"|||\n" if $debug; + + @listingpreamble=grep { + # only replace if this has not been done already (use short-circuit property of and) + if (!$replaced and $_ =~ s/^.*%DIFCODE TEMPLATE.*$/join("\n",@listingDIFcode)/e ) { + ###print STDERR "DEBUG: Replaced text $_\n" if $debug; + $replaced=1; + 1; + } else { + # return false for those lines matching %DIFCODE TEMPLATE (so that they are not included in output) + not m/%DIFCODE TEMPLATE/; + } + } @listingpreamble; + ### print STDERR "DEBUG: listingpreamble @listingpreamble\n"; + $latexdiffpreamble .= join "\n",(@listingpreamble,""); +} else { + print STDERR "WARNING: listings package not detected. Disabling mark-up in verbatim environments \n" ; + # if listings does not exist disable line-by-line markup and treat all verbatim environments as opaque + $VERBATIMENV = liststringtoregex($CONFIG{VERBATIMENV}.";".$CONFIG{VERBATIMLINEENV}); + $VERBATIMLINEENV = ""; +} + +# adding begin and end marker lines to preamble +$latexdiffpreamble = "%DIF PREAMBLE EXTENSION ADDED BY LATEXDIFF\n" . $ latexdiffpreamble . "%DIF END PREAMBLE EXTENSION ADDED BY LATEXDIFF\n"; + +# and return to preamble specific processing +if ( length $oldpreamble && length $newpreamble ) { + print STDERR "Differencing preamble.\n" if $verbose; + + # insert dummy first line such that line count begins with line 1 (rather than perl's line 0) - just so that line numbers inserted by linediff are correct + unshift @newpreamble,''; + unshift @oldpreamble,''; + @diffpreamble = linediff(\@oldpreamble, \@newpreamble); + # remove dummy line again + shift @diffpreamble; + # add filenames, modification time and latexdiff mark + defined($nolabel) or splice @diffpreamble,1,0, + "%DIF LATEXDIFF DIFFERENCE FILE", + ,"%DIF DEL $oldlabel", + "%DIF ADD $newlabel"; + if ( @auxlines ) { + push @diffpreamble,"%DIF DELETED TITLE COMMANDS FOR MARKUP"; + push @diffpreamble,join("\n",@auxlines); + } + if ( $earlylatexdiffpreamble) { + # insert latexdiff command directly after documentclass at beginning of preamble + # note that grep is only run for its side effect + ( grep { s/^([^%]*\\documentclass.*)$/$1$latexdiffpreamble/ } @diffpreamble )==1 or die "Could not find documentclass statement in preamble"; + } else { + # insert latexdiff commands at the end of preamble (default behaviour) + push @diffpreamble,$latexdiffpreamble; + } + push @diffpreamble,'\begin{document}'; + if (defined $packages{"hyperref"} && $nolinks) { + push @diffpreamble, '\begin{NoHyper}'; + } +} +elsif ( !length $oldpreamble && !length $newpreamble ) { + @diffpreamble=(); +} else { + print STDERR "Either both texts must have preamble or neither text must have the preamble.\n"; + exit(2); +} + +# Special: treat all cite commands as safe except in UNDERLINE and FONTSTRIKE mode +# (there is a conflict between citation and ulem package, see +# package documentation) +# Use post-processing +# and $packages{"apacite"}!~/natbibpapa/ + + +if (defined $packages{"units"} && $ulem ) { + # protect inlined maths environments by surrounding with an \mbox + # this is done to get around an incompatibility between the ulem and units package + # where spaces in the argument to underlined or crossed-out \unit commands cause an error message + print STDERR "units package detected at the same time as style using ulem.\n" if $verbose ; + $MBOXINLINEMATH=1; +} + +if (defined $packages{"siunitx"} ) { + # protect SI command by surrounding them with an \mbox + # this is done to get around an incompatibility between the ulem and siunitx package + print STDERR "siunitx package detected.\n" if $verbose ; + my $mboxcmds='SI,ang,numlist,numrange,SIlist,SIrange'; + init_regex_arr_list(\@SAFECMDLIST,'num,si'); + if ( $enablecitmark || ( $ulem && ! $disablecitmark )) { + init_regex_arr_list(\@MBOXCMDLIST,$mboxcmds); + } else { + init_regex_arr_list(\@SAFECMDLIST,$mboxcmds); + } +} + +if (defined $packages{"cleveref"} ) { + # protect selected command by surrounding them with an \mbox + # this is done to get around an incompatibility between ulem and cleveref package + print STDERR "cleveref package detected.\n" if $verbose ; + my $mboxcmds='[Cc]ref(?:range)?\*?,labelcref,(?:lc)?name[cC]refs?' ; + if ( $enablecitmark || ( $ulem && ! $disablecitmark )) { + init_regex_arr_list(\@MBOXCMDLIST,$mboxcmds); + } else { + init_regex_arr_list(\@SAFECMDLIST,$mboxcmds); + } +} + +if (defined $packages{"glossaries"} ) { + # protect selected command by surrounding them with an \mbox + # this is done to get around an incompatibility between ulem and glossaries package + print STDERR "glossaries package detected.\n" if $verbose ; + my $mboxcmds='[gG][lL][sS](?:|pl|disp|link|first|firstplural|desc|user[iv][iv]?[iv]?),[aA][cC][rR](?:long|longpl|full|fullpl),[aA][cC][lfp]?[lfp]?'; + init_regex_arr_list(\@SAFECMDLIST,'[gG][lL][sS](?:(?:entry)?(?:text|plural|name|symbol)|displaynumberlist|entryfirst|entryfirstplural|entrydesc|entrydescplural|entrysymbolplural|entryuser[iv][iv]?[iv]?|entrynumberlist|entrydisplaynumberlist|entrylong|entrylongpl|entryshort|entryshortpl|entryfull|entryfullpl),[gG]lossentry(?:name|desc|symbol),[aA][cC][rR](?:short|shortpl),[aA]csp?'); + if ( $enablecitmark || ( $ulem && ! $disablecitmark )) { + init_regex_arr_list(\@MBOXCMDLIST,$mboxcmds); + } else { + init_regex_arr_list(\@SAFECMDLIST,$mboxcmds); + } +} + +if (defined $packages{"chemformula"} or defined $packages{"chemmacros"} ) { + print STDERR "chemformula package detected.\n" if $verbose ; + init_regex_arr_list(\@SAFECMDLIST,'ch'); + push(@UNSAFEMATHCMD,'ch'); + # The next command would be needed to allow highlighting the interior of \ch commands in math environments + # but the redefinitions in chemformula are too deep to make this viable + # push(@MATHTEXTCMDLIST,'ch'); +} + +if (defined $packages{"mhchem"} ) { + print STDERR "mhchem package detected.\n" if $verbose ; + init_regex_arr_list(\@SAFECMDLIST,'ce'); + push(@UNSAFEMATHCMD,'ce','cee'); + # The next command would be needed to allow highlighting the interior of \cee commands in math environments + # but the redefinitions in chemformula are too deep to make this viable + # push(@MATHTEXTCMDLIST,'cee'); +} + + +my ( $citpat); + +if ( defined $packages{"apacite"} ) { + print STDERR "apacite package detected.\n" if $verbose ; + $citpat='(?:mask)?(?:full|short|no)?cite(?:A|author|year|meta)?(?:NP)?'; +} else { + # citation command pattern for all other citation schemes + $citpat='(?:cite\w*|nocite)'; +}; + +if ( ! $ulem ) { + # modes not using ulem: citation is safe + push (@SAFECMDLIST, $citpat); +} else { + ### Experimental: disable text and emph commands + push(@SAFECMDEXCL, qr/^emph$/, qr/^text..$/); + # replace \cite{..} by \mbox{\cite{..}} in added or deleted blocks in post-processing + push(@MBOXCMDLIST,$citpat) unless $disablecitmark; + if ( uc($subtype) eq "COLOR" or uc($subtype) eq "DVIPSCOL" ) { + # remove \cite command again from list of safe commands + pop @MBOXCMDLIST; + # deleted cite commands + } +} +push(@MBOXCMDLIST,$citpat) if $enablecitmark ; + + +if (defined $packages{"amsmath"} or defined $packages{"amsart"} or defined $packages{"amsbook"} ) { + print STDERR "amsmath package detected.\n" if $verbose ; + $MATHARRREPL='align*'; +} + +# add commands in MBOXCMDLIST to SAFECMDLIST +foreach $mboxcmd ( @MBOXCMDLIST ) { + init_regex_arr_list(\@SAFECMDLIST, $mboxcmd); +} + +# check if \label is in SAFECMDLIST, and if yes replace "label" in $LABELCMD by something that never matches (we hope!) +if ( iscmd("label",\@SAFECMDLIST,\@SAFECMDEXCL) ) { + $LABELCMD=~ s/label/NEVERMATCHLABEL/; +} + + + +print STDERR "Preprocessing body. " if $verbose; +preprocess($oldbody,$newbody); + + +# run difference algorithm +@diffbody=bodydiff($oldbody, $newbody); +$diffbo=join("",@diffbody); +writedebugfile($diffbo,"bodydiff"); + +print STDERR "(",exetime()," s)\n","Postprocessing body. \n" if $verbose; +postprocess($diffbo); +$diffall =join("\n",@diffpreamble) ; +# add visible labels +if (defined($visiblelabel)) { + # Give information right after \begin{document} (or at the beginning of the text for files without preamble + ### if \date command is used, add information to \date argument, otherwise give right after \begin{document} + ### $diffall=~s/(\\date$extraspace(?:\[$brat0\])?$extraspace)\{($pat_n)\}/$1\{$2 \\ LATEXDIFF comparison \\ Old: $oldlabel \\ New: $newlabel \}/ or + $diffbo = "\\begin{verbatim}LATEXDIFF comparison\nOld: $oldlabel\nNew: $newlabel\\end{verbatim}\n$diffbo" ; +} + +$diffall .= "$diffbo" ; +if (defined $packages{"hyperref"} && $nolinks) { + $diffall .= "\\end{NoHyper}\n"; +} +$diffall .= "\\end{document}$newpost" if length $newpreamble ; +if ( lc($encoding) ne "utf8" && lc($encoding) ne "ascii" ) { + print STDERR "Encoding output file to $encoding\n" if $verbose; + $diffall=Encode::encode($encoding,$diffall); + binmode STDOUT; +} +print $diffall; + + +print STDERR "(",exetime()," s)\n","Done.\n" if $verbose; + + +# liststringtoregex(liststring) +# expands string with semi-colon separated list into a regular expression corresponding +# matching any of the elements +sub liststringtoregex { + my ($liststring)=@_; + my @elements=grep /\S/,split(";",$liststring); + if ( @elements) { + return('(?:(?:' . join(')|(?:',@elements) .'))'); + } else { + return ""; + } +} + +# show_configuration +# note that this is not encapsulated but uses variables from the main program +# It is provided for convenience because in the future it is planned to allow output +# to be modified based on what packages are read etc - this works only if the input files are actually read +# whether or not additional files are provided +sub show_configuration { + if ($showpreamble) { + print "\nPreamble commands:\n"; + print $latexdiffpreamble ; + } + + if ($showsafe) { + print "\nsafecmd: Commands safe within scope of $ADDOPEN $ADDCLOSE and $DELOPEN $DELCLOSE (unless excluded):\n"; + print_regex_arr(@SAFECMDLIST); + print "\nsafecmd-exlude: Commands not safe within scope of $ADDOPEN $ADDCLOSE and $DELOPEN $DELCLOSE :\n"; + print_regex_arr(@SAFECMDEXCL); + print "\nmboxsafecmd: Commands safe only if they are surrounded by \\mbox command:\n"; + print_regex_arr(@MBOXCMDLIST); + print "\nnmboxsafecmd: Commands not safe:\n"; + print_regex_arr(@MBOXCMDEXCL); + } + + if ($showtext) { + print "\nCommands with last argument textual (unless excluded) and safe in every context:\n"; + print_regex_arr(@TEXTCMDLIST); + print "\nContext1 commands (last argument textual, command will be disabled in deleted passages, last argument will be shown as plain text):\n"; + print_regex_arr(@CONTEXT1CMDLIST); + print "\nContext2 commands (last argument textual, command and its argument will be disabled in deleted passages):\n"; + print_regex_arr(@CONTEXT2CMDLIST); + print "\nExclude list of Commands with last argument not textual (overrides patterns above):\n"; + print_regex_arr(@TEXTCMDEXCL); + } + + + if ($showconfig) { + print "Configuration variables:\n"; + print "ARRENV=$ARRENV\n"; + print "COUNTERCMD=$COUNTERCMD\n"; + print "FLOATENV=$FLOATENV\n"; + print "ITEMCMD=$ITEMCMD\n"; + print "LISTENV=$LISTENV\n"; + print "MATHARRENV=$MATHARRENV\n"; + print "MATHARRREPL=$MATHARRREPL\n"; + print "MATHENV=$MATHENV\n"; + print "MATHREPL=$MATHREPL\n"; + print "MINWORDSBLOCK=$MINWORDSBLOCK\n"; + print "PICTUREENV=$PICTUREENV\n"; + print "SCALEDELGRAPHICS=$SCALEDELGRAPHICS\n"; + print "VERBATIMENV=$VERBATIMENV\n"; + print "VERBATIMLINEENV=$VERBATIMLINEENV\n"; + print "CUSTOMDIFCMD=$CUSTOMDIFCMD\n"; + } +} + + + +## guess_encoding(filename) +## reads the first 20 lines of filename and looks for call of inputenc package +## if found, return the option of this package (encoding), otherwise return utf8 +sub guess_encoding { + my ($filename)=@_; + my ($i,$enc); + open (FH, $filename) or die("Couldn't open $filename: $!"); + $i=0; + while () { + next if /^\s*%/; # skip comment lines + if (m/\\usepackage\[(\w*?)\]\{inputenc\}/) { + close(FH); + return($1); + } + last if (++$i > 20 ); # scan at most 20 non-comment lines + } + close(FH); + ### return("ascii"); + return("utf8"); +} + + +sub read_file_with_encoding { + my ($output); + my ($filename, $encoding) = @_; + + if (lc($encoding) eq "utf8" ) { + open (FILE, "<:utf8",$filename) or die("Couldn't open $filename: $!"); + local $/ ; # locally set record operator to undefined, ie. enable whole-file mode + $output=; + } elsif ( lc($encoding) eq "ascii") { + open (FILE, $filename) or die("Couldn't open $filename: $!"); + local $/ ; # locally set record operator to undefined, ie. enable whole-file mode + $output=; + } else { + require Encode; + open (FILE, "<",$filename) or die("Couldn't open $filename: $!"); + local $/ ; # locally set record operator to undefined, ie. enable whole-file mode + $output=; + print STDERR "Converting $filename from $encoding to utf8\n" if $verbose; + $output=Encode::decode($encoding,$output); + } + close FILE; + if ($^O eq "linux" ) { + $output =~ s/\r\n/\n/g ; + } + return $output; +} + +## %packages=list_packages(@preamble) +## scans the arguments for \documentclass,\RequirePackage and \usepackage statements and constructs a hash +## whose keys are the included packages, and whose values are the associated optional arguments +#sub list_packages { +# my (@preamble)=@_; +# my %packages=(); +# foreach $line ( @preamble ) { +# # get rid of comments +# $line=~s/(?catfile($dirname,$fname); + print STDERR "DEBUG Beg of line match |$1|\n" if defined($1) && $debug ; + print STDERR "Include file $fname\n" if $verbose; + print STDERR "DEBUG looking for file ",$fullfile, "\n" if $debug; + # content of file becomes replacement value (use recursion), add \newpage if the command was include + if ( -f $fullfile ) { + # If file exists, replace input or include command with expanded input + $replacement=flatten(read_file_with_encoding($fullfile, $encoding), $preamble,$filename,$encoding) or die "Could not open file ",$fullfile,": $!"; + $replacement = remove_endinput($replacement); + # \include always starts a new page; use explicit \newpage command to simulate this + $newpage=(defined($4)? " \\newpage " : "") ; + } else { + # if file does not exist, do not expand include or input command (do not warn if fname contains #[0-9] as it is then likely part of a command definition + # and is not meant to be expanded directly + print STDERR "WARNING: Could not find included file ",$fullfile,". I will continue but not expand |$2|\n" unless $fname =~ m(#[0-9]) ; + $replacement = $2 ; # i.e. just the original command again -> make no change file does not exist + $newpage=""; + } + "$begline$newpage$replacement$newpage"; + }/exgm; + + # replace bibliography with bbl file if it exists + $text=~s/(^(?:[^%\n]|\\%)*)\\bibliography\{(.*?)\}/{ + if ( -f $bblfile ){ + $replacement=read_file_with_encoding(File::Spec->catfile($bblfile), $encoding); + } else { + warn "Bibliography file $bblfile cannot be found. No flattening of \\bibliography done. Run bibtex on old and new files first"; + $replacement="\\bibliography{$2}"; + } + $begline=(defined($1)? $1 : "") ; + "$begline$replacement"; + }/exgm; + + # replace subfile with contents (subfile package) + $text=~s/(^(?:[^%\n]|\\%)*)\\subfile\{(.*?)\}/{ + $begline=(defined($1)? $1 : "") ; + $fname = $2; + # # add tex extension unless there is a three or four letter extension already + $fname .= ".tex" unless $fname =~ m|\.\w{3,4}|; + print STDERR "Include file as subfile $fname\n" if $verbose; + # content of file becomes replacement value (use recursion) + # now strip away everything outside and including \begin{document} and \end{document} pair# + # # note: no checking for comments is made + $fullfile=File::Spec->catfile($dirname,$fname); + if ( -f $fullfile) { + # if file exists, expand \subfile command by contents of file + $subfile=read_file_with_encoding($fullfile,$encoding) or die "Could not open included subfile ",$fullfile,": $!"; + ($subpreamble,$subbody,$subpost)=splitdoc($subfile,'\\\\begin\{document\}','\\\\end\{document\}'); + ### $subfile=~s|^.*\\begin{document}||s; + ### $subfile=~s|\\end{document}.*$||s; + $replacement=flatten($subbody, $preamble,$filename,$encoding); + ### $replacement = remove_endinput($replacement); + } else { + # if file does not exist, do not expand subfile + print STDERR "WARNING: Could not find subfile ",$fullfile,". I will continue but not expand |$2|\n" unless $fname =~ m(#[0-9]) ; + $replacement = "\\subfile\{$2\}" ; # i.e. just the original command again -> make no change file does not exist + } + + "$begline$replacement"; + }/exgm; + + # replace \verbatiminput and \lstlistinginput + $text=~s/(^(?:[^%\n]|\\%)*)\\(verbatiminput\*?|lstinputlisting)$extraspace(\[$brat_n\])?$extraspace\{(.*?)\}/{ + $begline=(defined($1)? $1 : "") ; + $command = $2 ; + $fname = $4 ; + $verboptions = defined($3)? $3 : "" ; + if ($command eq 'verbatiminput' ) { + $verbenv = "verbatim" ; + } elsif ($command eq 'verbatiminput*' ) { + $verbenv = "verbatim*" ; + } elsif ($command eq 'lstinputlisting' ) { + $verbenv = "lstlisting" ; + } else { + die "Internal errorL Unexpected verbatim input type $command.\n"; + } + print STDERR "DEBUG Beg of line match |$begline|\n" if $debug ; + print STDERR "Include file $fname verbatim\n" if $verbose; + print STDERR "DEBUG looking for file ",File::Spec->catfile($dirname,$fname), "\n" if $debug; + # content of file becomes replacement value (do not use recursion), add \newpage if the command was include + ###$replacement=read_file_with_encoding(File::Spec->catfile($dirname,$fname), $encoding) or die "Couldn't find file ",File::Spec->catfile($dirname,$fname),": $!"; + $replacement=read_file_with_encoding(File::Spec->catfile($dirname,$fname), $encoding) or die "Couldn't find file ",File::Spec->catfile($dirname,$fname),": $!"; + # Add a new line if it not already there (note that the matching operator needs to use different delimiters, as we are still inside an outer scope that takes precedence + $replacement .= "\n" unless $replacement =~ m(\n$) ; + "$begline\\begin{$verbenv}$verboptions\n$replacement\\end{$verbenv}\n"; + }/exgm; + + return($text); +} + + +# print_regex_arr(@arr) +# prints regex array without x-ism expansion put in by pearl to stdout +sub print_regex_arr { + my $dumstring; + $dumstring = join(" ",@_); # PERL generates string (?-xism:^ref$) for quoted refex ^ref$ + $dumstring =~ s/\(\?-xism:\^(.*?)\$\)/$1/g; # remove string and ^,$ marks before output + print $dumstring,"\n"; +} + + +# @lines=extrapream($type,...) +# reads line from appendix or external file +# (end of file after __END__ token) +# if $type is a filename, it will read the file instead of reading from the appendix +# otherwise it will screen appendix for line "%DIF $TYPE" and copy everything up to line +# '%DIF END $TYPE' (where $TYPE is upcased version of $type) +# extrapream('-nofail',$type) will---instead of failing---simply return nothing if +# it does not find the matching line in a appendix (do not use -nofail option with multiple types!) +sub extrapream { + my @types=@_; + my ($type,$arg); + my $nofail=0; + ###my @retval=("%DIF PREAMBLE EXTENSION ADDED BY LATEXDIFF") ; + my @retval=(); + my ($copy); + + foreach $arg ( @types ) { + if ( $arg eq '-nofail' ) { + $nofail=1; + next; + } + $type=$arg; + $copy=0; + if ( -f $type || lc $type eq '/dev/null' ) { + open (FILE,$type) or die "Cannot open preamble file $type: $!"; + print STDERR "Reading preamble file $type\n" if $verbose ; + while () { + chomp ; + if ( $_ =~ m/%DIF PREAMBLE/ ) { + push (@retval,"$_"); + } else { + push (@retval,"$_ %DIF PREAMBLE"); + } + } + } else { # not (-f $type) + $type=uc($type); # upcase argument + print STDERR "Preamble Internal Type $type\n" if $verbose; + # save filehandle position (before first read this points to line after __END__) + # but seek DATA,0,0 resets it to the beginning of the file + # see https://stackoverflow.com/questions/4459601/how-can-i-use-data-twice + my $data_start = tell DATA; + while () { + if ( m/^%DIF $type/ ) { + $copy=1; + } elsif ( m/^%DIF END $type/ ) { + last; + } + chomp; + push (@retval,"$_ %DIF PREAMBLE") if $copy; + } + if ( $copy == 0 ) { + unless ($nofail) { + print STDERR "\nPreamble style $type not implemented.\n"; + print STDERR "Write latexdiff -h to get help with available styles\n"; + exit(2); + } + } + seek DATA,$data_start,0; # rewind DATA handle to beginning of data record + } + } + ###push (@retval,"%DIF END PREAMBLE EXTENSION ADDED BY LATEXDIFF") ; + return @retval; +} + + +# ($part1,$part2,$part3)=splitdoc($text,$word1,$word2) +# splits $text into 3 parts at $word1 and $word2. +# if neither $word1 nor $word2 exist, $part1 and $part3 are empty, $part2 is $text +# If only $word1 or $word2 exist but not the other, output an error message. + +# NB this version avoids $` and $' for performance reason although it only makes a tiny difference +# (in one test gain a tenth of a second for a 30s run) +sub splitdoc { + my ($text,$word1,$word2)=@_; + my ($part1,$part2,$part3)=("","",""); + my ($rest,$pos); + + if ( $text =~ m/(^[^%]*)($word1)/mg ) { + $pos=pos $text; + $part1=substr($text,0,$pos-length($2)); + $rest=substr($text,$pos); + if ( $rest =~ m/(^[^%]*)($word2)/mg ) { + $pos=pos $rest; + $part2=substr($rest,0,$pos-length($2)); + $part3=substr($rest,$pos); + } + else { + die "$word1 and $word2 not in the correct order or not present as a pair." ; + } + } else { + $part2=$text; + die "$word2 present but not $word1." if ( $text =~ m/(^[^%]*)$word2/ms ); + } + return ($part1,$part2,$part3); +} + + + + + +# bodydiff($old,$new) +sub bodydiff { + my ($oldwords, $newwords) = @_; + my @retwords; + + print STDERR "(",exetime()," s)\n","Splitting into latex tokens \n" if $verbose; + print STDERR "Parsing $oldfile \n" if $verbose; + my @oldwords = splitlatex($oldwords); + print STDERR "Parsing $newfile \n" if $verbose; + my @newwords = splitlatex($newwords); + + if ( $debug ) { + open(TOKENOLD,">","latexdiff.debug.tokenold"); + print TOKENOLD join("***\n",@oldwords); + close(TOKENOLD); + open(TOKENNEW,">","latexdiff.debug.tokennew"); + print TOKENNEW join("***\n",@newwords); + close(TOKENNEW); + } + + print STDERR "(",exetime()," s)\n","Pass 1: Expanding text commands and merging isolated identities with changed blocks " if $verbose; + pass1(\@oldwords, \@newwords); + + + print STDERR "(",exetime()," s)\n","Pass 2: inserting DIF tokens and mark up. " if $verbose; + if ( $debug ) { + open(TOKENOLD,">","latexdiff.debug.tokenold2"); + print TOKENOLD join("***\n",@oldwords); + close(TOKENOLD); + open(TOKENNEW,">","latexdiff.debug.tokennew2"); + print TOKENNEW join("***\n",@newwords); + close(TOKENNEW); + } + + @retwords=pass2(\@oldwords, \@newwords); + + return(@retwords); +} + + + + +# @words=splitlatex($string) +# split string according to latex rules +# Each element of words is either +# a word (including trailing spaces and punctuation) +# a latex command +# if there is white space in the beginning return that as first token +sub splitlatex { + my ($inputstring) = @_ ; + my $string=$inputstring ; + # if input is empty, return empty list + length($string)>0 or return (); + $string=~s/^(\s*)//s; + my $leadin=$1; + length($string)>0 or return ($leadin); + + my @retval=($string =~ m/$pat/osg); + + if (length($string) != length(join("",@retval))) { + print STDERR "\nWARNING: Inconsistency in length of input string and parsed string:\n This often indicates faulty or non-standard latex code.\n In many cases you can ignore this and the following warning messages.\n Note that character numbers in the following are counted beginning after \\begin{document} and are only approximate." unless $ignorewarnings; + print STDERR "DEBUG Original length ",length($string)," Parsed length ",length(join("",@retval)),"\n" if $debug; + print STDERR "DEBUG Input string: |$string|\n" if (length($string)<500) && $debug; + print STDERR "DEBUG Token parsing: |",join("+",@retval),"|\n" if (length($string)<500) && $debug ; + @retval=(); + # slow way only do this if other m//sg method fails + my $last = 0; + while ( $string =~ m/$pat/osg ) { + my $match=$&; + if ($last + length $& != pos $string ) { + my $pos=pos($string); + my $offset=30<$last ? 30 : $last; + my $dum=substr($string,$last-$offset,$pos-$last+2*$offset); + my $dum1=$dum; + my $cnt=$#retval; + my $i; + $dum1 =~ s/\n/ /g; + unless ($ignorewarnings) { + print STDERR "\n$dum1\n"; + print STDERR " " x 30,"^" x ($pos-$last)," " x 30,"\n"; + print STDERR "Missing characters near word " . (scalar @retval) . " character index: " . $last . "-" . pos($string) . " Length: " . length($match) . " Match: |$match| (expected match marked above).\n"; + } + # put in missing characters `by hand' + push (@retval, substr($dum,$offset,$pos-$last-length($match))); +# Note: there seems to be a bug in substr with utf8 that made the following line output substr which were too long, +# using dum instead appears to work +# push (@retval, substr($string,$last, pos($string)-$last-length($match))); + } + push (@retval, $match); + $last=pos $string; + } + + } + unshift(@retval,$leadin) if (length($leadin)>0); + return @retval; +} + + +# pass1( \@seq1,\@seq2) +# Look for differences between seq1 and seq2. +# Where an common-subsequence block is flanked by deleted or appended blocks, +# and is shorter than $MINWORDSBLOCK words it is appended +# to the last deleted or appended word. If the block contains tokens other than words +# or punctuation it is not merged. +# Deleted or appended block consisting of words and safe commands only are +# also merged, to prevent break-up in pass2 (after previous isolated words have been removed) +# If there are commands with textual arguments (e.g. \caption) both in corresponding +# appended and deleted blocks split them such that the command and opening bracket +# are one token, then the rest is split up following standard rules, and the closing +# bracket is a separate token, ie. turn +# "\caption{This is a textual argument}" into +# ("\caption{","This ","is ","a ","textual ","argument","}") +# No return value. Destructively changes sequences +sub pass1 { + my $seq1 = shift ; + my $seq2 = shift ; + + my $len1 = scalar @$seq1; + my $len2 = scalar @$seq2; + my $wpat=qr/^(?:[a-zA-Z.,'`:;?()!]*)[\s~]*$/; #' + + my ($last1,$last2)=(-1,-1) ; + my $cnt=0; + my $block=[]; + my $addblock=[]; + my $delblock=[]; + my $todo=[]; + my $instruction=[]; + my $i; + my (@delmid,@addmid,@dummy); + + my ($addcmds,$delcmds,$matchindex); + my ($addtextblocks,$deltextblocks); + my ($addtokcnt,$deltokcnt,$mattokcnt)=(0,0,0); + my ($addblkcnt,$delblkcnt,$matblkcnt)=(0,0,0); + + my $adddiscard = sub { + if ($cnt > 0 ) { + $matblkcnt++; + # just after an unchanged block +# print STDERR "Unchanged block $cnt, $last1,$last2 \n"; + if ($cnt < $MINWORDSBLOCK + && $cnt==scalar ( + grep { /^$wpat/ || ( /^\\((?:[`'^"~=.]|[\w\d@*]+))((?:\[$brat_n\]|\{$pat_n\})*)/o + && iscmd($1,\@SAFECMDLIST,\@SAFECMDEXCL) + && scalar(@dummy=split(" ",$2))<3 ) } + @$block) ) { + # merge identical blocks shorter than $MINWORDSBLOCK + # and only containing ordinary words + # with preceding different word + # We cannot carry out this merging immediately as this + # would change the index numbers of seq1 and seq2 and confuse + # the algorithm, instead we store in @$todo where we have to merge + push(@$todo, [ $last1,$last2,$cnt,@$block ]); + } + $block = []; + $cnt=0; $last1=-1; $last2=-1; + } + }; + my $discard=sub { $deltokcnt++; + &$adddiscard; #($_[0],$_[1]); + push(@$delblock,[ $seq1->[$_[0]],$_[0] ]); + $last1=$_[0] }; + + my $add = sub { $addtokcnt++; + &$adddiscard; #($_[0],$_[1]); + push(@$addblock,[ $seq2->[$_[1]],$_[1] ]); + $last2=$_[1] }; + + my $match = sub { $mattokcnt++; + if ($cnt==0) { # first word of matching sequence after changed sequence or at beginning of word sequence + $deltextblocks = extracttextblocks($delblock); + $delblkcnt++ if scalar @$delblock; + $addtextblocks = extracttextblocks($addblock); + $addblkcnt++ if scalar @$addblock; + + # make a list of all TEXTCMDLIST commands in deleted and added blocks + $delcmds = extractcommands($delblock); + $addcmds = extractcommands($addblock); + # now find those text commands, which are found in both deleted and added blocks, and expand them + # keygen(third argument of _longestCommonSubsequence) implies to sort on command (0th elements of $addcmd elements) + # the calling format for longestCommonSubsequence has changed between versions of + # Algorithm::Diff so we need to check which one we are using + if ( $algodiffversion > 1.15 ) { + ### Algorithm::Diff 1.19 + $matchindex=Algorithm::Diff::_longestCommonSubsequence($delcmds,$addcmds, 0, sub { $_[0]->[0] } ); + } else { + ### Algorithm::Diff 1.15 + $matchindex=Algorithm::Diff::_longestCommonSubsequence($delcmds,$addcmds, sub { $_[0]->[0] } ); + } + + for ($i=0 ; $i<=$#$matchindex ; $i++) { + if (defined($matchindex->[$i])){ + $j=$matchindex->[$i]; + @delmid=splitlatex($delcmds->[$i][3]); + @addmid=splitlatex($addcmds->[$j][3]); + while (scalar(@$deltextblocks) && $deltextblocks->[0][0]<$delcmds->[$i][1]) { + my ($index,$block,$cnt)=@{ shift(@$deltextblocks) }; + push(@$todo, [$index,-1,$cnt,@$block]); + } + push(@$todo, [ $delcmds->[$i][1],-1,-1,$delcmds->[$i][2],@delmid,$delcmds->[$i][4]]); + + while (scalar(@$addtextblocks) && $addtextblocks->[0][0]<$addcmds->[$j][1]) { + my ($index,$block,$cnt)=@{ shift(@$addtextblocks) }; + push(@$todo, [-1,$index,$cnt,@$block]); + } + push(@$todo, [ -1,$addcmds->[$j][1],-1,$addcmds->[$j][2],@addmid,$addcmds->[$j][4]]); + } + } + # mop up remaining textblocks + while (scalar(@$deltextblocks)) { + my ($index,$block,$cnt)=@{ shift(@$deltextblocks) } ; + push(@$todo, [$index,-1,$cnt,@$block]); + } + while (scalar(@$addtextblocks)) { + my ($index,$block,$cnt)=@{ shift(@$addtextblocks) }; + push(@$todo, [-1,$index,$cnt,@$block]); + } + + $addblock=[]; + $delblock=[]; + } + push(@$block,$seq2->[$_[1]]); + $cnt++ }; + + my $keyfunc = sub { join(" ",split(" ",shift())) }; + + traverse_sequences($seq1,$seq2, { MATCH=>$match, DISCARD_A=>$discard, DISCARD_B=>$add }, $keyfunc ); + + + # now carry out the merging/splitting. Refer to elements relative from + # the end (with negative indices) as these offsets don't change before the instruction is executed + # cnt>0: merged small unchanged groups with previous changed blocks + # cnt==-1: split textual commands into components + foreach $instruction ( @$todo) { + ($last1,$last2,$cnt,@$block)=@$instruction ; + if ($cnt>=0) { + splice(@$seq1,$last1-$len1,1+$cnt,join("",$seq1->[$last1-$len1],@$block)) if $last1>=0; + splice(@$seq2,$last2-$len2,1+$cnt,join("",$seq2->[$last2-$len2],@$block)) if $last2>=0; + } else { + splice(@$seq1,$last1-$len1,1,@$block) if $last1>=0; + splice(@$seq2,$last2-$len2,1,@$block) if $last2>=0; + } + } + + if ($verbose) { + print STDERR "\n"; + print STDERR " $mattokcnt matching tokens in $matblkcnt blocks.\n"; + print STDERR " $deltokcnt discarded tokens in $delblkcnt blocks.\n"; + print STDERR " $addtokcnt appended tokens in $addblkcnt blocks.\n"; + } +} + + +# extracttextblocks(\@blockindex) +# $blockindex has the following format +# [ [ token1, index1 ], [token2, index2],.. ] +# where index refers to the index in the original old or new word sequence +# Returns: reference to an array of the form +# [[ $index, $textblock, $cnt ], .. +# where $index index of block to be merged +# $textblock contains all the words to be merged with the word at $index (but does not contain this word) +# $cnt is length of block +# +# requires: iscmd +# +sub extracttextblocks { + my $block=shift; + my ($i,$token,$index); + my $textblock=[]; + my $last=-1; + my $wpat=qr/^(?:[a-zA-Z.,'`:;?()!]*)[\s~]*$/; #' + my $retval=[]; + + for ($i=0;$i< scalar @$block;$i++) { + ($token,$index)=@{ $block->[$i] }; + # store pure text blocks + if ($token =~ /$wpat/ || ( $token =~/^\\((?:[`'^"~=.]|[\w\d@\*]+))((?:${extraspace}\[$brat_n\]${extraspace}|${extraspace}\{$pat_n\})*)/o + && iscmd($1,\@SAFECMDLIST,\@SAFECMDEXCL) + && !iscmd($1,\@TEXTCMDLIST,\@TEXTCMDEXCL))) { + # we have text or a command which can be treated as text + if ($last<0) { + # new pure-text block + $last=$index; + } else { + # add to pure-text block + push(@$textblock, $token); + } + } else { + # it is not text + if (scalar(@$textblock)) { + push(@$retval,[ $last, $textblock, scalar(@$textblock) ]); + } + $textblock=[]; + $last=-1; + } + } + # finish processing a possibly unfinished block before returning + if (scalar(@$textblock)) { + push(@$retval,[ $last, $textblock, scalar(@$textblock) ]); + } + return($retval) +} + + + +# extractcommands( \@blockindex ) +# $blockindex has the following format +# [ [ token1, index1 ], [token2, index2],.. ] +# where index refers to the index in the original old or new word sequence +# Returns: reference to an array of the form +# [ [ "\cmd1", index, "\cmd1[optarg]{arg1}{", "arg2" ,"} " ],.. +# where index is just taken from input array +# command must have a textual argument as last argument +# +# requires: iscmd +# +sub extractcommands { + my $block=shift; + my ($i,$token,$index,$cmd,$open,$mid,$closing); + my $retval=[]; + + for ($i=0;$i< scalar @$block;$i++) { + ($token,$index)=@{ $block->[$i] }; + # check if token is an alphanumeric command sequence with at least one non-optional argument + # \cmd[...]{...}{last argument} + # Capturing in the following results in these associations + # $1: \cmd[...]{...}{ + # $2: \cmd + # $3: last argument + # $4: } + trailing spaces + if ( ( $token =~ m/^(\\([\w\d\*]+)(?:${extraspace}\[$brat_n\]|${extraspace}\{$pat_n\})*${extraspace}\{)($pat_n)(\}\s*)$/so ) + && iscmd($2,\@TEXTCMDLIST,\@TEXTCMDEXCL) ) { + # push(@$retval,[ $2,$index,$1,$3,$4 ]); + ($cmd,$open,$mid,$closing) = ($2,$1,$3,$4) ; + $closing =~ s/\}/\\RIGHTBRACE/ ; + push(@$retval,[ $cmd,$index,$open,$mid,$closing ]); + } + } + return $retval; +} + +# iscmd($cmd,\@regexarray,\@regexexcl) checks +# return 1 if $cmd matches any of the patterns in the +# array $@regexarray, and none of the patterns in \@regexexcl, otherwise return 0 +sub iscmd { + my ($cmd,$regexar,$regexexcl)=@_; + my ($ret)=0; + print STDERR "DEBUG: iscmd($cmd)=" if $debug; + foreach $pat ( @$regexar ) { + if ( $cmd =~ m/^${pat}$/ ) { + $ret=1 ; + last; + } + } + print STDERR "0\n" if ($debug && !$ret) ; + return 0 unless $ret; + foreach $pat ( @$regexexcl ) { + print STDERR "0\n" if ( $debug && $cmd =~ m/^${pat}$/) ; + return 0 if ( $cmd =~ m/^${pat}$/ ); + } + print STDERR "1\n" if $debug; + return 1; +} + + +# pass2( \@seq1,\@seq2) +# Look for differences between seq1 and seq2. +# Mark begin and end of deleted and appended sequences with tags $DELOPEN and $DELCLOSE +# and $ADDOPEN and $ADDCLOSE, respectively, however exclude { } & and all comands, unless +# they match an element of the whitelist (SAFECMD) +# For words in TEXTCMD but not in SAFECMD, enclose interior with $ADDOPEN and $ADDCLOSE brackets +# Deleted comment lines are marked with %DIF < +# Added comment lines are marked with %DIF > +sub pass2 { + my $seq1 = shift ; + my $seq2 = shift ; + + my ($addtokcnt,$deltokcnt,$mattokcnt)=(0,0,0); + my ($addblkcnt,$delblkcnt,$matblkcnt)=(0,0,0); + + my $retval = []; + my $delhunk = []; + my $addhunk = []; + + my $discard = sub { $deltokcnt++; + push ( @$delhunk, $seq1->[$_[0]]) }; + + my $add = sub { $addtokcnt++; + push ( @$addhunk, $seq2->[$_[1]]) }; + + my $match = sub { $mattokcnt++; + if ( scalar @$delhunk ) { + $delblkcnt++; + # mark up changes, but comment out commands + push @$retval,marktags($DELMARKOPEN,$DELMARKCLOSE,$DELOPEN,$DELCLOSE,$DELCMDOPEN,$DELCMDCLOSE,$DELCOMMENT,$delhunk); + $delhunk = []; + } + if ( scalar @$addhunk ) { + $addblkcnt++; + # we mark up changes, but simply quote commands + push @$retval,marktags($ADDMARKOPEN,$ADDMARKCLOSE,$ADDOPEN,$ADDCLOSE,"","",$ADDCOMMENT,$addhunk); + $addhunk = []; + } + push(@$retval,$seq2->[$_[1]]) }; + + my $keyfunc = sub { join(" ",split(" ",shift())) }; + + traverse_sequences($seq1,$seq2, { MATCH=>$match, DISCARD_A=>$discard, DISCARD_B=>$add }, $keyfunc ); + # clear up unprocessed hunks + push @$retval,marktags($DELMARKOPEN,$DELMARKCLOSE,$DELOPEN,$DELCLOSE,$DELCMDOPEN,$DELCMDCLOSE,$DELCOMMENT,$delhunk) if scalar @$delhunk; + push @$retval,marktags($ADDMARKOPEN,$ADDMARKCLOSE,$ADDOPEN,$ADDCLOSE,"","",$ADDCOMMENT,$addhunk) if scalar @$addhunk; + + + if ($verbose) { + print STDERR "\n"; + print STDERR " $mattokcnt matching tokens. \n"; + print STDERR " $deltokcnt discarded tokens in $delblkcnt blocks.\n"; + print STDERR " $addtokcnt appended tokens in $addblkcnt blocks.\n"; + } + + return(@$retval); +} + +# marktags($openmark,$closemark,$open,$close,$opencmd,$closecmd,$comment,\@block) +# returns ($openmark,$open,$block,$close,$closemark) if @block contains no commands (except white-listed ones), +# braces, ampersands, or comments +# mark comments with $comment +# exclude all other exceptions from scope of open, close like this +# ($openmark, $open,...,$close, $opencmd,command, command,$closecmd, $open, ..., $close, $closemark) +# If $opencmd begins with "%" marktags assumes it is operating on a deleted block, otherwise on an added block +sub marktags { + my ($openmark,$closemark,$open,$close,$opencmd,$closecmd,$comment,$block)=@_; + my $word; + my (@argtext); + my $retval=[]; + my $noncomment=0; + my $cmd=-1; # -1 at beginning 0: last token written is a ordinary word + # 1: last token written is a command + # for keeping track whether we are just in a command sequence or in a word sequence + my $cmdcomment= ($opencmd =~ m/^%/); # Flag to indicate whether opencmd is a comment (i.e. if we intend to simply comment out changed commands) + my ($command,$commandword,$closingbracket) ; # temporary variables needed below to remember sub-pattern matches + +# split this block to flatten out sequences joined in pass1 + @$block=splitlatex(join "",@$block); + ### print STDERR "DEBUG: marktags $openmark,$closemark,$open,$close,$opencmd,$closecmd,$comment\n" if $debug; + ### print STDERR "DEBUG: marktags blocksplit ",join("|",@$block),"\n" if $debug; + foreach (@$block) { + $word=$_; + ### print STDERR "DEBUG MARKTAGS: |$word|\n" if $debug; + if ( $word =~ s/^%/%$comment/ ) { + # a comment + if ($cmd==1) { + push (@$retval,$closecmd) ; + $cmd=-1; + } + push (@$retval,$word); + next; + } + if ( $word =~ m/^\s*$/ ) { + ### print STDERR "DEBUG MARKTAGS: whitespace detected |$word| cmdcom |$cmdcomment| |$opencmd|\n" if $debug; + # a sequence of white-space characters - this should only ever happen for the first element of block. + # in deleted block, omit, otherwise just copy it in + if ( ! $cmdcomment) { # ignore in deleted blocks + push(@$retval,$word); + } + next; + } + if (! $noncomment) { + push (@$retval,$openmark); + $noncomment=1; + } + # negative lookahead pattern (?!) in second clause is put in to avoid matching \( .. \) patterns + # also note that second pattern will match \\ + print STDERR "DEBUG marktags: Considering word |$word|\n" if $debug; + if ( $word =~ /^[&{}\[\]]/ || ( $word =~ /^\\(?!\()(\\|[`'^"~=.]|[\w*@]+)/ && !iscmd($1,\@SAFECMDLIST,\@SAFECMDEXCL)) ) { + ###print STDERR "DEBUG MARKTAGS is a non-safe command ($1)\n" if $debug; + ### if ( $word =~ /^[&{}\[\]]/ || ( $word =~ /^\\([\w*@\\% ]+)/ && !iscmd($1,\@SAFECMDLIST,\@SAFECMDEXCL)) ) { + # word is a command or other significant token (not in SAFECMDLIST) + ## same conditions as in subroutine extractcommand: + # check if token is an alphanumeric command sequence with at least one non-optional argument + # \cmd[...]{...}{last argument} + # Capturing in the following results in these associations + # $1: \cmd[...]{...}{ + # $2: cmd + # $3: last argument + # $4: } + trailing spaces + ### pre-0.3 if ( ( $token =~ m/^(\\([\w\d\*]+)(?:\[$brat0\]|\{$pat_n\})*\{)($pat_n)(\}\s*)$/so ) + if ( ( $word =~ m/^(\\([\w\d\*]+)(?:${extraspace}\[$brat_n\]|${extraspace}\{$pat_n\})*${extraspace}\{)($pat_n)(\}\s*)$/so ) + && (iscmd($2,\@TEXTCMDLIST,\@TEXTCMDEXCL)|| iscmd($2,\@MATHTEXTCMDLIST,\@MATHTEXTCMDEXCL)) + && ( !$cmdcomment || !iscmd($2,\@CONTEXT2CMDLIST, \@CONTEXT2CMDEXCL) ) ) { + # Condition 1: word is a command? - if yes, $1,$2,.. will be set as above + # Condition 2: word is a text command - we mark up the interior of the word. There is a separate check for MATHTEXTCMDLIST + # because for $mathmarkup=WHOLE, the commands should not be split in pass1 (ie. math mode commands are not in + # TEXTCMDLIST, but the interior of MATHTEXT commands should be highlighted in both deleted and added blocks + # Condition 3: But if we are in a deleted block ($cmdcomment=1) and + # $2 (the command) is in context2, just treat it as an ordinary command (i.e. comment it open with $opencmd) + # Because we do not want to disable this command + # here we do not use $opencmd and $closecmd($opencmd is empty) + if ($cmd==1) { + push (@$retval,$closecmd) ; + } elsif ($cmd==0) { + push (@$retval,$close) ; + } + $command=$1; $commandword=$2; $closingbracket=$4; + @argtext=splitlatex($3); # split textual argument into tokens + # and mark it up (but we do not need openmark and closemark) + # insert command with initial arguments, marked-up final argument, and closing bracket + if ( $cmdcomment && iscmd($commandword,\@CONTEXT1CMDLIST, \@CONTEXT1CMDEXCL) ) { + # context1cmd in a deleted environment; delete command itself but keep last argument, marked up + push (@$retval,$opencmd); + $command =~ s/\n/\n${opencmd}/sg ; # repeat opencmd at the beginning of each line + # argument, note that the additional comment character is included + # to suppress linebreak after opening parentheses, which is important + # for latexrevise + push (@$retval,$command,"%\n{$AUXCMD\n",marktags("","",$open,$close,$opencmd,$closecmd,$comment,\@argtext),$closingbracket); + } elsif ( iscmd($commandword,,\@MATHTEXTCMDLIST, \@MATHTEXTCMDEXCL) ) { + # MATHBLOCK pseudo command: consider all commands safe, except & and \\ + # Keep these commands even in deleted blocks, hence set $opencmd and $closecmd (5th and 6th argument of marktags) to + # "" + local @SAFECMDLIST=(".*"); + local @SAFECMDEXCL=('\\','\\\\',@UNSAFEMATHCMD); + push(@$retval,$command,marktags("","",$open,$close,"","",$comment,\@argtext)#@argtext + ,$closingbracket); + } else { + # normal textcmd or context1cmd in an added block + push (@$retval,$command,marktags("","",$open,$close,$opencmd,$closecmd,$comment,\@argtext),$closingbracket); + } + push (@$retval,$AUXCMD,"\n") if $cmdcomment ; + $cmd=-1 ; + } else { + # ordinary command + push (@$retval,$opencmd) if $cmd==-1 ; + push (@$retval,$close,$opencmd) if $cmd==0 ; + $word =~ s/\n/\n${opencmd}/sg if $cmdcomment ; # if opencmd is a comment, repeat this at the beginning of every line + ### print STDERR "MARKTAGS: Add command |$word|\n"; + push (@$retval,$word); + $cmd=1; + } + } else { + ###print STDERR "DEBUG MARKTAGS is an ordinary word or SAFECMD command \n" if $debug; + # just an ordinary word or command in SAFECMD + push (@$retval,$open) if $cmd==-1 ; + push (@$retval,$closecmd,$open) if $cmd==1 ; + ###TODO: check here if it is a command in MBOXCMD list, and surround it with \mbox{...} + ### $word =~ /^\\(?!\()(\\|[`'^"~=.]|[\w*@]+)/ && iscmd($1,\@MBOXCMDLIST,\@MBOXCMDEXCL)) + ### but actually this check has been carried out already so can simply check if word begins with backslash + if ( $word =~ /^\\(?!\()(\\|[`'^"~=.]|[\w*@]+)(.*?)(\s*)$/s && iscmd($1,\@MBOXCMDLIST,\@MBOXCMDEXCL)) { + # $word is a safe command in MBOXCMDLIST + ###print STDERR "DEBUG Mboxsafecmd detected:$word:\n" if $debug ; + push(@$retval,"\\mbox{$AUXCMD\n\\" . $1 . $2 . $3 ."}\\hspace{0pt}$AUXCMD\n" ); + } else { + # $word is a normal word or a safe command (not in MBOXCMDLIST) + push (@$retval,$word); + } + $cmd=0; + } + } + push (@$retval,$close) if $cmd==0; + push (@$retval,$closecmd) if $cmd==1; + + push (@$retval,$closemark) if ($noncomment); + return @$retval; +} + +#used in preprocess +sub take_comments_and_enter_from_frac() { + #*************take the \n and % between frac and {}*********** + #notice all of the substitution are made none global + while( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\frac(([\s]*%[^\n]*?)*[\r\n|\r|\n])+\{(.*?)\\end\{\1}/s ) { + # if there isn't any % or \n in the pattern $2 then there should be an \\end{...} in $2 + ### print STDERR "Match the following in take_comments and_enter_from_frac(1):\n****$&****\n" if $debug; + if( $2 !~ m/\\end\{$1}/s ) { + # take out % and \n from the next match only (none global) + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\frac(([\s]*%[^\n]*?)*[\r\n|\r|\n])+\{(.*?)\\end\{\1}/\\begin{$1}$2\\frac{$5\\end{$1}/s; + } + else{ + #there are no more % and \n in $2, we want to find the next one so we clear the begin-end from the pattern + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\end\{\1}/MATHBLOCK$1\{$2\}MATHBLOCKEND/s; + } + } + ###cleaning up + while( s/MATHBLOCK($MATHENV|$MATHARRENV|SQUAREBRACKET)\{(.*?)\}MATHBLOCKEND/\\begin{$1}$2\\end{$1}/s ){} + ###*************take the \n and % between frac and {}*********** + + ###**********take the \n and % between {} and {} of the frac*************** + while( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)\\frac\{(.*?)\\end\{\1\}/s ) { + # if there isn't any more //frac before the first //end in the pattern $2 then there should be an \\end{...} in $2 + ###print STDERR "Match the following in take_comments and_enter_from_frac(2):\n****$&****\n" if $debug; + if( $2 !~ m/\\end\{$1\}/s ) { + # from now on CURRFRAC is the frac we are looking at + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)\\frac\{(.*?)\\end\{\1\}/\\begin\{$1\}$2CURRFRAC\{$3\\end\{$1\}/s; + while( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)CURRFRAC\{(.*?)\\end\{\1\}/s ) { + if( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)CURRFRAC\{($pat_n)\}([\s]*(%[^\n]*?)*[\r\n|\r|\n])+[\s]*\{(.*?)\\end\{\1}/s ) { + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)CURRFRAC\{($pat_n)\}([\s]*(%[^\n]*?)*[\r\n|\r|\n])+[\s]*\{(.*?)\\end\{\1\}/\\begin\{$1\}$2CURRFRAC\{$3\}\{$6\\end\{$1\}/s; + } + else { # there is no comment or \n between the two brackets {}{} + # change CURRFRAC to FRACSTART so we can change them all back to //frac{ when we finish + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)CURRFRAC\{(.*?)\\end\{\1}/\\begin{$1}$2FRACSTART\{$3\\end{$1}/s; + } + } + } + else{ + ###there are no more frac in $2, we want to find the next one so we clear the begin-end from the pattern + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\end\{\1}/MATHBLOCK$1\{$2\}MATHBLOCKEND/s; + } + + } + ###cleaning up + while( s/MATHBLOCK($MATHENV|$MATHARRENV|SQUAREBRACKET)\{(.*?)\}MATHBLOCKEND/\\begin{$1}$2\\end{$1}/s ){} + s/FRACSTART/\\frac/g; + ###***************take the \n and % between {} and {} of the frac********************* +} + +# preprocess($string, ..) +# carry out the following pre-processing steps for all arguments: +# 1. Remove leading white-space +# Change \{ to \QLEFTBRACE and \} to \QRIGHTBRACE and \& to \AMPERSAND +# #. Change {,} in comments to \CLEFTBRACE, \CRIGHTBRACE +# 2. mark all first empty line (in block of several) with \PAR tokens +# 3. Convert all '\%' into '\PERCENTAGE ' and all '\$' into \DOLLAR to make parsing regular expressions easier +# 4. Convert all \verb|some verbatim text| commands (where | can be an arbitrary character) +# into \verb{hash} (also lstinline) +# 5. Convert \begin{verbatim} some verbatim text \end{verbatim} into \verbatim{hash} (not only verbatim, all patterns matching VERBATIMENV) +# 6. Convert _n into \SUBSCRIPTNB{n} and _{nnn} into \SUBSCRIPT{nn} +# 7. Convert ^n into \SUPERSCRIPTNB{n} and ^{nnn} into \SUPERSCRIPT{nn} +# 8. a. Convert $$ $$ into \begin{DOLLARDOLLAR} \end{DOLLARDOLLAR} +# b. Convert \[ \] into \begin{SQUAREBRACKET} \end{SQUAREBRACKET} +# 9. Convert all picture environmentent (\begin{PICTUREENV} .. \end{PICTUREENV} \PICTUREBLOCKenv +# For math-mode COARSE,WHOLE or NONE option -convert all \begin{MATH} .. \end{MATH} +# into \MATHBLOCKmath{...} commands, where MATH/math is any valid math environment + +# 10. Add final token STOP to the very end. This is put in because the algorithm works better if the last token is identical. This is removed again in postprocessing. +# +# NB: step 6 and 7 is likely to convert some "_" inappropriately, e.g. in file +# names or labels but it does not matter because they are converted back in the postprocessing step +# Returns: leading white space removed in step 1 +sub preprocess { + for (@_) { + + + # change in \verb and similar commands - note that I introduce an extra space here so that the + # already hashed variants do not trigger again + # transform \lstinline{...} +# s/\\lstinline(\[$brat0\])?(\{(?:.*?)\})/"\\DIFlstinline". $1 ."{". tohash(\%verbhash,"$2") ."}"/esg; +# s/\\lstinline(\[$brat0\])?((\S).*?\2)/"\\DIFlstinline". $1 ."{". tohash(\%verbhash,"$2") ."}"/esg; + s/\\lstinline((?:\[$brat_n\])?)(\{(?:.*?)\})/"\\DIFlstinline". $1 ."{". tohash(\%verbhash,"$2") ."}"/esg; + s/\\lstinline((?:\[$brat_n\])?)(([^\s\w]).*?\3)/"\\DIFlstinline". $1 ."{". tohash(\%verbhash,"$2") ."}"/esg; + s/\\(verb\*?|lstinline)([^\s\w])(.*?)\2/"\\DIF${1}{". tohash(\%verbhash,"${2}${3}${2}") ."}"/esg; + + # Change \{ to \QLEFTBRACE, \} to \QRIGHTBRACE, and \& to \AMPERSAND + s/(?{$hstr}) && $string ne $hash->{$hstr}); + # else found a duplicate HASH need to repeat for a higher hash value + } + $hash->{$hstr}=$string; + ### print STDERR "Hash:$hstr: Content:$string:\n"; + return($hstr); +} + +#string=fromhash(\%hash,$fromstring) +# restores string value stored in hash +#string=fromhash(\%hash,$fromstring,$prependstring) +# additionally begins each line with prependstring +sub fromhash { + my ($hash,$hstr)=($_[0],$_[1]); + my $retstr=$hash->{$hstr}; + if ( $#_ >= 2) { + $retstr =~ s/^/$_[2]/mg; + } + return $retstr; +} + +# writedebugfile(string, label) +# if $debug set writes to file latexdiff.debug.