diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/lintex |
Initial commit
Diffstat (limited to 'support/lintex')
-rw-r--r-- | support/lintex/COPYING | 340 | ||||
-rw-r--r-- | support/lintex/Makefile | 23 | ||||
-rw-r--r-- | support/lintex/README | 93 | ||||
-rw-r--r-- | support/lintex/cxx/Makefile | 41 | ||||
-rw-r--r-- | support/lintex/cxx/cleandir.cxx | 230 | ||||
-rw-r--r-- | support/lintex/cxx/cleandir.hh | 21 | ||||
-rw-r--r-- | support/lintex/cxx/cleanup.cxx | 98 | ||||
-rw-r--r-- | support/lintex/cxx/cleanup.hh | 23 | ||||
-rw-r--r-- | support/lintex/cxx/file.cxx | 70 | ||||
-rw-r--r-- | support/lintex/cxx/file.hh | 108 | ||||
-rw-r--r-- | support/lintex/cxx/ltx.cxx | 166 | ||||
-rw-r--r-- | support/lintex/cxx/ltx.hh | 41 | ||||
-rw-r--r-- | support/lintex/lintex.1 | 136 | ||||
-rw-r--r-- | support/lintex/lintex.c | 983 | ||||
-rw-r--r-- | support/lintex/lintex.com | 90 | ||||
-rw-r--r-- | support/lintex/lintex.pdf | bin | 0 -> 8549 bytes | |||
-rw-r--r-- | support/lintex/lintexrc | 2 | ||||
-rw-r--r-- | support/lintex/mktests.sh | 13 |
18 files changed, 2478 insertions, 0 deletions
diff --git a/support/lintex/COPYING b/support/lintex/COPYING new file mode 100644 index 0000000000..2ba72d57f6 --- /dev/null +++ b/support/lintex/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE 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. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/support/lintex/Makefile b/support/lintex/Makefile new file mode 100644 index 0000000000..193da4aba9 --- /dev/null +++ b/support/lintex/Makefile @@ -0,0 +1,23 @@ +# $Id: Makefile,v 1.3 2001/12/02 11:00:05 loreti Exp $ + +CFLAGS += -ansi -pedantic -Wall `pkg-config --libs --cflags libconfig` +#CFLAGS = -ansi -Wall -g `pkg-config --libs --cflags libconfig` + +ROOT = /usr/local + +.PHONY: install clean + +lintex: lintex.c Makefile + $(CC) $(CXXFLAGS) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ lintex.c + +install: lintex + strip lintex + mv lintex $(ROOT)/bin + cp lintex.1 $(ROOT)/man/man1 + +lintex.pdf: lintex.1 + groff -man -Tps $< | ps2pdf - $@ + +clean: + -rm *~ *.o core + -rm lintex lintex.pdf diff --git a/support/lintex/README b/support/lintex/README new file mode 100644 index 0000000000..554ecb889e --- /dev/null +++ b/support/lintex/README @@ -0,0 +1,93 @@ +"lintex" is a C program that removes auxilliary (La)TeX files that are +usually not needed after a (La)TeX run (log, aux, dvi, ... files); +they are however actually removed only if their modification time is +more recent than the one of the related TeX source. The backup files +usually created by many editors are also removed. + +To remove or not remove additional extensions, lintex can parse the +configuration file $HOME/.lintexrc . See the included example and the +manpage for more details + +lintex is distributed under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +Here follows an example run: + +ryan@nu $ lintex -h +lintex version 1.13 (2012-09-11) +Usage: + lintex [OPTIONS] [DIR [DIR ...]] +Purpose: + Removes unneeded TeX auxiliary files and editor backup files from the + given directories (default: the current directory); the TeX files are + actually removed only if their modification time is more recent than + the one of the related TeX source and if they aren't readonly. + Please see the manpage for a list of extensions that get removed. +Options: + -i : asks the user before removing any file; + -r : scans recursively the subdirectories of the given + directories; + -b ext : "ext" is the trailing string identifying editor backup files + (defaults to "~"). -b "" avoids any cleanup of special + files; + -p : pretend, show what files would be removed but don't actually + remove them; + -k : keeps final document (.pdf, .ps, .dvi, etc.); + -o : permit removal of files older than their sources; + -q : quiet, only print error messages; + -v : verbose, prints which files were removed and which weren't; + -d : debug output, prints the answers to all of life's questions. +ryan@nu $ ls -lR +.: +total 4 +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 bar.aux +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 bar.dvi +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 bar.log +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 bar.pdf +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 bar.ps +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 bar.tex +drwxr-xr-x 2 ryan ryan 4096 Sep 3 18:26 dir1/ +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.aux +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.dvi +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.log +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.pdf +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.ps +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:28 foo.tex + +./dir1: +total 0 +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 baz.aux +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 baz.dvi +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 baz.log +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 baz.pdf +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 baz.ps +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 baz.tex +ryan@nu $ lintex -r README . +lintex: "README" cannot be opened (or is not a directory) +./bar.aux has been removed +./bar.dvi has been removed +./bar.log has been removed +./bar.pdf has been removed +./bar.ps has been removed +./dir1/baz.aux has been removed +./dir1/baz.dvi has been removed +./dir1/baz.log has been removed +./dir1/baz.pdf has been removed +./dir1/baz.ps has been removed +ryan@nu $ ls -lR +.: +total 4 +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 bar.tex +drwxr-xr-x 2 ryan ryan 4096 Sep 3 18:26 dir1/ +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.aux +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.dvi +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.log +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.pdf +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 foo.ps +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:28 foo.tex +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 README + +./dir1: +total 0 +-rw-r--r-- 1 ryan ryan 0 Sep 3 18:26 baz.tex diff --git a/support/lintex/cxx/Makefile b/support/lintex/cxx/Makefile new file mode 100644 index 0000000000..c53b349da6 --- /dev/null +++ b/support/lintex/cxx/Makefile @@ -0,0 +1,41 @@ +# Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +# Work: University of Padova - Department of Physics +# Via F. Marzolo, 8 - 35131 PADOVA - Italy +# Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +# EMail: loreti@pd.infn.it +# WWW: http://www.pd.infn.it/~loreti/mlo.html +# +# $Id: Makefile,v 1.9 2005/01/21 08:16:48 loreti Exp $ +# +###################################################### + +.PHONY: clean + +CXX = g++ +#CXXFLAGS = -std=c++98 -pedantic -W -Wall -g -DDEBUG +CXXFLAGS = -std=c++98 -pedantic -W -Wall -O2 + +#CXX = KCC +#CXXFLAGS = -O -DDEBUG +#CXXFLAGS = -O + +LDFLAGS = + +ltx: ltx.o cleandir.o cleanup.o file.o + $(CXX) $(LDFLAGS) -o $@ ltx.o cleandir.o cleanup.o file.o + +ltx.o: ltx.cxx ltx.hh cleandir.hh + $(CXX) $(CXXFLAGS) -o $@ -c ltx.cxx + +cleandir.o: cleandir.cxx cleandir.hh cleanup.hh + $(CXX) $(CXXFLAGS) -o $@ -c cleandir.cxx + +cleanup.o: cleanup.cxx cleanup.hh file.hh + $(CXX) $(CXXFLAGS) -o $@ -c cleanup.cxx + +file.o: file.cxx file.hh + $(CXX) $(CXXFLAGS) -o $@ -c file.cxx + +clean: + -rm *~ *.o ltx + -if [ -d ti_files ]; then rm ti_files/* && rmdir ti_files; fi diff --git a/support/lintex/cxx/cleandir.cxx b/support/lintex/cxx/cleandir.cxx new file mode 100644 index 0000000000..799f702c35 --- /dev/null +++ b/support/lintex/cxx/cleandir.cxx @@ -0,0 +1,230 @@ +// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +// Work: University of Padova - Department of Physics +// Via F. Marzolo, 8 - 35131 PADOVA - Italy +// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +// EMail: loreti@pd.infn.it +// WWW: http://www.pd.infn.it/~loreti/mlo.html +// +// ------------------------------------------------------------------- +// +// $Id: cleandir.cxx,v 1.11 2002/09/25 12:19:41 loreti Exp $ +// +// ------------------------------------------------------------------- + +#include <algorithm> +#include <iterator> +#include <vector> +#include <cstring> +#include "ltx.hh" // Includes: functional, iostream, string +#include "cleandir.hh" // Includes: string +#include "cleanup.hh" // Includes: string +#include "file.hh" // Includes: list, map, string, utility, ctime + +extern "C" { + #include <dirent.h> + #include <sys/stat.h> + #include <sys/types.h> +} + +using std::cerr; +using std::cout; +using std::strcmp; +using std::string; + +// Local variables + +namespace { + // In "re" are defined the extensions of the files relevant for + // LaTeX; ".tex" is missing, being handled separately. THIS ARRAY + // MUST BE SORTED IN ASCENDING ORDER. + + const char * re[] = { + ".aux", ".dvi", ".idx", ".ilg", ".ind", + ".lof", ".log", ".lot", ".pdf", ".ps", + ".toc" + }; + const size_t nRE = sizeof(re) / sizeof(re[0]); + std::vector<string> texExts(re, re + nRE); + + const string tex(".tex"); + const string dot("."); + + const size_t filename_max = 256; +} + +// Local functions (declarations) + +namespace { + void check_file(const string &, const time_t, currDir &); +} + +// Code + +void scan_dir( + const string & name +) { + // Scans the directory "name", building the related instantiation of + // the class "currDir" containing all the informations for the + // relevant files; then calls "clean_dir" to perform the actual + // cleanup. If the "-r" options has been specified, recurses over + // all the directories under the current one. + +#if defined(DEBUG) + static bool firstTime(true); + + if (firstTime) { + cout << "--------------------Relevant extensions (" + << nRE << ")\n"; + copy(texExts.begin(), texExts.end(), + std::ostream_iterator<string>(cout, " ")); + cout << std::endl; + firstTime = false; + } + + cout << "--------------------scan_dir called for \"" + << name << "\"\n"; +#endif // DEBUG + + DIR * pDir; + + if ((pDir = opendir( name.c_str() )) != 0) { + + string fullName(name); + if (*(fullName.rbegin()) != '/') fullName.append("/"); + + currDir thisDir(fullName); + std::list<string> subDirs; + struct dirent * pDe; + + // Reads every file: skips null inodes (already deleted + // files), and the two special files "." and ".." . + + while ((pDe = readdir(pDir)) != 0) { + if (pDe->d_ino == 0) continue; + +#if defined(DEBUG) + cout << "Next file: " << pDe->d_name << " - "; +#endif // DEBUG + + if (strcmp(pDe->d_name, ".") == 0) { +#if defined(DEBUG) + cout << "skipped\n"; +#endif // DEBUG + continue; + } + + if (strcmp(pDe->d_name, "..") == 0) { +#if defined(DEBUG) + cout << "skipped\n"; +#endif // DEBUG + continue; + } + + // Gets the file related informations with stat(2) (we need file + // type and modification time). If the call to "stat" fails, + // the file is not considered. + + char tName[filename_max]; + std::strcpy(tName, fullName.c_str()); + std::strcat(tName, pDe->d_name); + + struct stat sStat; + + if (stat(tName, &sStat) != 0) { +#if defined(DEBUG) + cout << "got error from stat()\n"; +#else + cerr << ltx::progname << ": error calling stat(" + << tName << ")\n"; +#endif // DEBUG + + } else { + if (S_ISDIR(sStat.st_mode) != 0) { +#if defined(DEBUG) + cout << "is a directory\n"; +#endif // DEBUG + + // If needed, push the subdirectory names in the dedicated + // list, for future recursion; plain files are handled by + // the local procedure check_file(). + + if (ltx::recurse) subDirs.push_back(tName); + + } else { + check_file(pDe->d_name, sStat.st_mtime, thisDir); + } + } + } + + // Looks if some cleanup has to be performed + + clean_files(thisDir); + + if (ltx::recurse) { + for_each(subDirs.begin(), subDirs.end(), std::ptr_fun(scan_dir)); + } + + closedir(pDir); + } else { + cerr << ltx::progname << ": \"" << name + << "\" could not be opened (or is not a directory)\n"; + } +} + +namespace { + void check_file( + const string & name, + const time_t mTime, + currDir & CDir + ) { + // - If the file "name" matches the trailing string identifying + // backup editor files, is removed; + // - if it matches a relevant extension, is inserted in the + // "currDir" instance. + + string::size_type where; + + if (ltx::lTrailEd > 0) { + if ((where = name.rfind(ltx::trailEd)) != string::npos) { + if ((where + ltx::lTrailEd) == name.size()) { + #if defined(DEBUG) + cout << "matches the default editor extension\n"; + #endif // DEBUG + nuke(CDir.getName(), name); + return; + } + } + } + + // Breaks the file name in "basename" and "extension"; and checks + // the latter against the known ones (".tex" files are handled + // separately). + + if ((where = name.find_last_of(dot)) != string::npos) { + string extension = name.substr(where); + string basename = name.substr(0, where); + fileFamily & fF = CDir.getFileFamily(basename); + + if (extension == tex) { + fF.addExtension(mTime, 0); + #if defined(DEBUG) + cout << "inserted\n"; + #endif // DEBUG + + } else if (binary_search(texExts.begin(), texExts.end(), extension)) { + fF.addExtension(mTime, &extension); + #if defined(DEBUG) + cout << "extension " << extension << " - inserted\n"; + } else { + cout << "extension not relevant\n"; + #endif // DEBUG + + } + + #if defined(DEBUG) + } else { + cout << "no extension\n"; + #endif // DEBUG + } + } +} diff --git a/support/lintex/cxx/cleandir.hh b/support/lintex/cxx/cleandir.hh new file mode 100644 index 0000000000..3c29d3a012 --- /dev/null +++ b/support/lintex/cxx/cleandir.hh @@ -0,0 +1,21 @@ +// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +// Work: University of Padova - Department of Physics +// Via F. Marzolo, 8 - 35131 PADOVA - Italy +// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +// EMail: loreti@pd.infn.it +// WWW: http://www.pd.infn.it/~loreti/mlo.html +// +// ------------------------------------------------------------------- +// +// $Id: cleandir.hh,v 1.1 2001/01/22 10:43:43 loreti Exp $ +// +// ------------------------------------------------------------------- + +#ifndef CLEANDIR_H_ +#define CLEANDIR_H_ + +#include <string> + +void scan_dir(const std::string &); + +#endif // CLEANDIR_H_ diff --git a/support/lintex/cxx/cleanup.cxx b/support/lintex/cxx/cleanup.cxx new file mode 100644 index 0000000000..f930490757 --- /dev/null +++ b/support/lintex/cxx/cleanup.cxx @@ -0,0 +1,98 @@ +// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +// Work: University of Padova - Department of Physics +// Via F. Marzolo, 8 - 35131 PADOVA - Italy +// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +// EMail: loreti@pd.infn.it +// WWW: http://www.pd.infn.it/~loreti/mlo.html +// +// ------------------------------------------------------------------- +// +// $Id: cleanup.cxx,v 1.8 2005/01/21 08:16:49 loreti Exp $ +// +// ------------------------------------------------------------------- + +#include <cstdio> +#include <cctype> +#include "ltx.hh" // Includes: functional, iostream, string +#include "file.hh" // Includes: list, map, string, utility, ctime +#include "cleanup.hh" // Includes: string + +using std::cin; +using std::cout; +using std::string; + +namespace { + const int answerLength(64); +} + +void clean_files( + const currDir & dir +) { + // Loops over all the file families stored in "dir", then loops over + // all the extensions in this file family; if a ".tex" file with a + // modification time former than the modification time of the target + // file exists, the file is removed. + + fileCollection::const_iterator iter, iterEnd = dir.end(); + + for (iter = dir.begin(); iter != iterEnd; iter++) { + + const fileFamily * pFF = iter->second; + std::list<extInfo>::const_iterator jter; + std::list<extInfo>::const_iterator jterEnd = pFF->end(); + + for (jter = pFF->begin(); jter != jterEnd; jter++) { + + string fullName = iter->first + jter->first; + + if (pFF->hasTex()) { + if (difftime(jter->second, pFF->texMtime()) > 0.0) { + + if (ltx::confirm) { + char answer[answerLength], c; + + do { + cout << "Remove " << dir.getName() + << fullName << " (y|n) ? "; + cin.get(answer, answerLength); + if (cin.gcount() < answerLength-1 ) { + cin.ignore(); + } + c = tolower(static_cast<unsigned char>(answer[0])); + } while (c != 'y' && c != 'n'); + if (c != 'y') continue; + } + nuke(dir.getName(), fullName); + + } else { + cout << dir.getName() << fullName + << " not removed; " << iter->first + << ".tex is newer\n"; + } + } else { + cout << dir.getName() << fullName + << " not removed; " << iter->first + << ".tex does not exist\n"; + } + } + } +} + +void nuke( + const string & dirName, + const string & fileName +) { + // Removes the file "fileName" from the directory "dirName". If the + // preprocessor symbol 'DEBUG' is defined, the file is not actually + // removed: but a message is printed on the standard output stream, + // informing that the Finger Of Death has been raised to him. + + string target = dirName + fileName; + +#if defined(DEBUG) + cout << "FOD: " << target << std::endl; +#else + remove(target.c_str()); + cout << target << " has been removed.\n"; +#endif // DEBUG +} diff --git a/support/lintex/cxx/cleanup.hh b/support/lintex/cxx/cleanup.hh new file mode 100644 index 0000000000..b692dd8f14 --- /dev/null +++ b/support/lintex/cxx/cleanup.hh @@ -0,0 +1,23 @@ +// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +// Work: University of Padova - Department of Physics +// Via F. Marzolo, 8 - 35131 PADOVA - Italy +// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +// EMail: loreti@pd.infn.it +// WWW: http://www.pd.infn.it/~loreti/mlo.html +// +// ------------------------------------------------------------------- +// +// $Id: cleanup.hh,v 1.2 2002/09/12 10:15:44 loreti Exp $ +// +// ------------------------------------------------------------------- + +#ifndef CLEANUP_H_ +#define CLEANUP_H_ + +#include <string> +#include "file.hh" + +void clean_files(const currDir &); +void nuke(const std::string &, const std::string &); + +#endif // CLEANUP_H_ diff --git a/support/lintex/cxx/file.cxx b/support/lintex/cxx/file.cxx new file mode 100644 index 0000000000..35c9362385 --- /dev/null +++ b/support/lintex/cxx/file.cxx @@ -0,0 +1,70 @@ +// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +// Work: University of Padova - Department of Physics +// Via F. Marzolo, 8 - 35131 PADOVA - Italy +// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +// EMail: loreti@pd.infn.it +// WWW: http://www.pd.infn.it/~loreti/mlo.html +// +// ------------------------------------------------------------------- +// +// $Id: file.cxx,v 1.7 2002/09/12 10:15:44 loreti Exp $ +// +// ------------------------------------------------------------------- + +#include <algorithm> +#include <functional> +#include <iostream> +#include "file.hh" // Includes: list, map, string, utility, ctime + +using std::string; + +// Methods for the class fileFamily + +void fileFamily::addExtension( + time_t mTime, + const string * pExtName +) { + // Insert the file informations in a fileFamily. "mTime" is the + // modification time, "pExtName" points to the file extension or is + // NULL for an extension of ".tex" . + + if (pExtName) { + _extInfo.push_back( std::make_pair(*pExtName, mTime) ); + } else { + _hasTex = true; + _texMtime = mTime; + } +} + +// Auxiliary function object for currDir objects + +struct releaseFileFamily + : public std::unary_function<fileCollectionElement, void> { + void operator() (fileCollectionElement & p) { + delete p.second; } +}; + +// Methods for the class currDir + +currDir::~currDir() +{ + for_each(_dirContent.begin(), _dirContent.end(), releaseFileFamily()); +} + +fileFamily & currDir::getFileFamily( + const string & base +) { + // Gets the file family related to the basename "base". If this is + // the first file found, a fileFamily instance is allocated with + // "operator new" (and will be later deleted in the currDir class + // destructor). + + fileFamily * retval = _dirContent[ base ]; + + if (retval == 0) { + retval = new fileFamily; + _dirContent[ base ] = retval; + } + + return *retval; +} diff --git a/support/lintex/cxx/file.hh b/support/lintex/cxx/file.hh new file mode 100644 index 0000000000..d395d74f2a --- /dev/null +++ b/support/lintex/cxx/file.hh @@ -0,0 +1,108 @@ +// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +// Work: University of Padova - Department of Physics +// Via F. Marzolo, 8 - 35131 PADOVA - Italy +// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +// EMail: loreti@pd.infn.it +// WWW: http://www.pd.infn.it/~loreti/mlo.html +// +// ------------------------------------------------------------------- +// +// $Id: file.hh,v 1.3 2002/09/12 10:15:44 loreti Exp $ +// +// ------------------------------------------------------------------- + +#ifndef FILE_H_ +#define FILE_H_ + +#include <list> +#include <map> +#include <string> +#include <utility> +#include <ctime> + +// Classes for the handling of directories and files. +// +// - The files are abstracted as a basename, an extension and a +// modification time: the basename being defined as all the file +// name characters up to (but not including) the last "."; and the +// extension as the remainder. A file may have an empty basename or +// an empty extension. +// +// - A "file family" is a set of files having all the same basename +// and different extensions. In this context, an extension of +// ".tex" is considered 'special' and is managed separately; the +// class "fileFamily" actually contains informations about the +// existence of a .tex member and its modification time, plus a list +// of all the related files with different extensions. +// +// - For the file families, methods are provided to test for the +// existence of a .tex; to get its modification time; to add a +// member to the family; and to retrieve the list of all the +// extensions found. + +typedef std::pair< std::string, time_t > extInfo; + +class fileFamily { +private: + bool _hasTex; + time_t _texMtime; + std::list<extInfo> _extInfo; + + // Prevents any use of the copy constructor and of the assignment + // operator + + fileFamily & operator = (const fileFamily & rhs); + fileFamily(const fileFamily & rhs); + +public: + fileFamily() : _hasTex(false), _texMtime(0) {} + ~fileFamily() {} + + bool hasTex() const { return _hasTex; } + time_t texMtime() const { return _texMtime; } + + void addExtension(time_t, const std::string *); + + // Const-iterators over all the found extensions + + std::list<extInfo>::const_iterator begin() const { + return _extInfo.begin(); } + std::list<extInfo>::const_iterator end() const { + return _extInfo.end(); } +}; + +// A directory is seen as a directory name plus a collection of file +// families; that collection is implemented as an STL map. Methods +// are provided to add a file, to retrieve the directory name, and to +// iterate over the file families. + +typedef std::pair< const std::string, fileFamily * > fileCollectionElement; +typedef std::map< const std::string, fileFamily * > fileCollection; + +class currDir { +private: + std::string _name; + fileCollection _dirContent; + + // Prevents any use of the copy constructor and of the assignment + // operator + + currDir & operator = (const currDir & rhs); + currDir(const currDir & rhs); + +public: + currDir(const std::string & dirName) : _name(dirName) { } + ~currDir(); + + const std::string & getName() const { return _name; } + fileFamily & getFileFamily(const std::string &); + + // Iterators over all the found file families + + fileCollection::const_iterator begin() const { + return _dirContent.begin(); } + fileCollection::const_iterator end() const { + return _dirContent.end(); }; +}; + +#endif // FILE_H_ diff --git a/support/lintex/cxx/ltx.cxx b/support/lintex/cxx/ltx.cxx new file mode 100644 index 0000000000..a82de99fdb --- /dev/null +++ b/support/lintex/cxx/ltx.cxx @@ -0,0 +1,166 @@ +// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +// Work: University of Padova - Department of Physics +// Via F. Marzolo, 8 - 35131 PADOVA - Italy +// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +// EMail: loreti@pd.infn.it +// WWW: http://www.pd.infn.it/~loreti/mlo.html +// +// ------------------------------------------------------------------- +// +// C++ version of the program "lintex", born as an exercise +// in programming. +// +// $Id: ltx.cxx,v 1.9 2006/09/11 12:25:41 loreti Exp $ +// +// ------------------------------------------------------------------- + +#include <algorithm> +#include <list> +#include <cstring> +#include "ltx.hh" // Includes: functional, iostream, string +#include "cleandir.hh" // Includes: string + +extern "C" { + #include <getopt.h> + #include <unistd.h> +} + +using std::cout; +using std::endl; +using std::string; + +// Global variables (definition) + +namespace ltx { + string progname; + string trailEd("~"); + string::size_type lTrailEd; + bool confirm(false); + bool recurse(false); +} + +using namespace ltx; + +// Local procedures + +namespace { + char *basename(char *); + void syntax(); +} + +int main( + int argc, + char *argv[] +) { + std::list<string> targets; + + // Gets the executable name + + progname = argv[0] = basename(argv[0]); + + // Decodes the command line options and arguments + + char shortOpts[] = "irb::"; + struct option longOpts[] = { + {"interactive", no_argument, 0, 'i'}, + {"recursive", no_argument, 0, 'r'}, + {"backup", optional_argument, 0, 'b'}, + { 0, 0, 0, 0} + }; + + int c; + while ((c = getopt_long(argc, argv, shortOpts, longOpts, 0)) != -1) { + switch (c) { + case 'i': + confirm = true; + break; + + case 'r': + recurse = true; + break; + + case 'b': + trailEd = optarg ? optarg : ""; + break; + + case 'h': + case '?': + syntax(); + return 0; + + default: + std::cerr << "Can't happen: " << __FILE__ << ' ' + << __LINE__ << endl; + return 0; + } + } + + while (optind < argc) targets.push_back( argv[optind++] ); + + // Computes the length of the extension identifying the backup + // files, and, if no target directories were explicitly given, + // scans the current one. + + lTrailEd = trailEd.size(); + + if (targets.empty()) targets.push_back("."); + +#if defined(DEBUG) + cout << "--------------------Argument analysis\n"; + cout << "Confirm = " << confirm << endl; + cout << "Recurse = " << recurse << endl; + cout << "Trailing editor extension = \"" << trailEd + << "\" (length " << lTrailEd << ")\n"; + cout << "Target directories:\n"; + + for_each(targets.begin(), targets.end(), printBefore(" ")); +#endif // DEBUG + + // Scans in turn all the wanted directories + + for_each(targets.begin(), targets.end(), std::ptr_fun(scan_dir)); + + return 0; +} + +namespace { + char *basename( + char *pc + ) { + // Strips the (eventual) path name from the full file name pointed + // to by "pc"; if no file name is given, returns an empty string. + + char *p = std::strrchr(pc, '/'); + if (p == 0) return pc; + return ++p; + } + + void syntax() + { + cout << + "\nUsage:\t " << progname << " [options] [dir [dir ... ]]\n"; + cout << + "Purpose: removes TeX auxiliary files and editor backup files from " + "the\n"; + cout + << "\t given directories (default: from the current directory). TeX\n"; + cout << + "\t auxiliary files are actually removed only if their modification\n"; + cout << + "\t time is more recent than the one of the related TeX source file.\n"; + cout << + "Options: -i | --interactive : asks before removing files;\n"; + cout << + "\t -r | --recursive : scans recursively the given directories;\n"; + cout << + "\t -b=ext | --backup=ext : \"ext\" is the trailing string " + "identifying\n"; + cout << + "\t\t\t\t editor backup files.\n"; + cout << + "Notes:\t \"ext\" defaults to \"~\"; -b \"\" avoids the unconditional " + "cleanup of\n"; + cout << + "\t any special file.\n" << endl; + } +} diff --git a/support/lintex/cxx/ltx.hh b/support/lintex/cxx/ltx.hh new file mode 100644 index 0000000000..ce41519d57 --- /dev/null +++ b/support/lintex/cxx/ltx.hh @@ -0,0 +1,41 @@ +// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO +// Work: University of Padova - Department of Physics +// Via F. Marzolo, 8 - 35131 PADOVA - Italy +// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102 +// EMail: loreti@pd.infn.it +// WWW: http://www.pd.infn.it/~loreti/mlo.html +// +// ------------------------------------------------------------------- +// +// $Id: ltx.hh,v 1.2 2002/09/12 10:15:44 loreti Exp $ +// +// ------------------------------------------------------------------- + +#include <functional> +#include <iostream> +#include <string> + +// Auxiliary function object used to print (on an output stream) a +// leading text followed by the argument string and an end-of-line. + +class printBefore : public std::unary_function< std::string, void > { +private: + const std::string & _leader; + std::ostream & _os; + +public: + printBefore(const std::string & b = "", std::ostream & o = std::cout) + : _leader(b), _os(o) {} + ~printBefore() {} + void operator() (const std::string & s) { _os << _leader << s << std::endl; } +}; + +// Global variables (declaration) + +namespace ltx { + extern std::string progname; + extern std::string trailEd; + extern std::string::size_type lTrailEd; + extern bool confirm; + extern bool recurse; +} diff --git a/support/lintex/lintex.1 b/support/lintex/lintex.1 new file mode 100644 index 0000000000..95c955e8df --- /dev/null +++ b/support/lintex/lintex.1 @@ -0,0 +1,136 @@ +.ig \"-*- nroff -*- +.. +.de TQ +.br +.ns +.TP \\$1 +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH lintex 1 "11 September 2012" "lintex version 1.13" +.SH NAME +lintex \- removes TeX-related garbage files +.SH SYNOPSIS +.BR lintex " [ " "\-i" " ] [ " "\-r" " ] [ " "\-b ext" " ] [ " "\-p" " ]" +.RB " [ " "\-k" " ] [ " "\-o" " ] [ " "\-q" " ] [ " "\-v" " ] [ " "\-d" " ]" +.RI " [ " dir " [ " dir " \|.\|.\|.\| ]]" +.SH DESCRIPTION +.B lintex +is a program that removes TeX-related auxiliary files, normally not +needed after a successful TeX run (i.e. log, aux, dvi, \.\.\. files); +they are actually removed only if their modification timestamp is more +recent than the one of the related TeX source and if they aren't readonly. +In addition, all the backup files that your favorite editor has generated +are also removed. +.SH OPTIONS +.TP +.B \-i +Asks the user permission before actually removing any file. +.TP +.B \-r +The given directories are scanned recursively; i.e., if they contain +any subdirectory structure, all the tree will be cleaned up. +.TP +.B \-b ext +.B ext +is the trailer string (the \'extension\') that identifies the backup +files generated by your editor, e.g. +.BR "\-b .bak" ";" +.B ext +defaults to ~ (the tilde character), i.e. to the +.IR emacs +convention. If +.B ext +includes characters having a special meaning for the shell, it must be +enclosed in quotes: e.g. +.B +\-b "~" +(equivalent to the default). Specifying the null string, as in +.BR "\-b """"" "," +inhibits the cleanup of any special file. +.TP +.B \-p +Pretends, show what files would be removed but don't actually remove them. +.TP +.B \-k +Keeps final documents (.pdf, .ps, .dvi). +.TP +.B \-o +Permits the removal of files older than their sources. +.TP +.B \-q +Quiet, only prints error messages. +.TP +.B \-v +Verbose, prints which files were removed and which weren't. +.TP +.B \-d +Debug, prints the answers to all of life's questions. +.SH PARAMETERS +.TP +.SM +.I dir +The name(s), separated by whitespace, of one or more directories to be +cleaned; if missing, the default is the current directory. +.SH EXTENSIONS THAT GET REMOVED +All files ending in the following extensions may be removed when appropriate: + + .aux, .bbl, .blg, .dvi, .idx, .ilg, .ind, .lof, .log, .lot, .nav, .out,\ + .pdf, .ps, .snm, .thm, .toc, .toc.old, .synctex.gz, .xyc +.SH ENVIRONMENT +The \fBHOME\fP environment variable determines the location of the configuration +file, see \fBFILES\fP below. +.SH FILES +Users can create a configuration file \fI$HOME/.lintexrc\fP which optionally +contains the keys \fBremove-keys\fP and \fBkeep-keys\fP, each followed by a +Python style list of extensions to additionally remove or keep, respectively. +Each extension \fImust\fP be preceded by a period, e.g. ".pdf" and not "pdf". +The \fBkeep-keys\fP key is only used when the keep option (\fB\-k\fP) is passed. +Note that the extensions listed in the \fBkeep-keys\fP field replace the +defaults used by the \fB\-k\fP option. The \fBkeep-keys\fP option also takes +precedence over \fBremove-keys\fP: no file ending in an extension removed by +default or listed in \fBremove-keys\fP will be deleted if its extension is +listed in \fBkeep-keys\fP and the \fB\-k\fP option was passed. For example, the +configuration file + + keep-keys = [".pdf", ".ps", ".log"]; + remove-keys = [".txt", ".1"]; + +will keep any file ending in ".pdf", ".ps" or ".log" when lintex is called with +the \fP\-k\fP option (but \fInot\fP files ending in ".dvi"), and adds ".txt" and +".1" to the list of extensions to check for removal. The following configuration +file provides lintex with its default behaviour: + + keep-keys = [".pdf", ".ps", ".dvi"]; + remove-keys = []; +.SH AUTHOR +lintex was written by Maurizio Loreti <Maurizio_Loreti\@gmail.com> between 1996 +and 2002. +.LP +As of 2010, lintex is maintained by Ryan Kavanagh <rak\@debian.org>. It +features several extensions/bug fixes. Feel free to email him with your comments +and suggestions. + +.SH COPYRIGHT + +Copyright \[co] 1996\(en2002 Maurizio Loreti <Maurizio_Loreti\@gmail.com> + +Copyright \[co] 2010\(en2012 Ryan Kavanagh <rak\@debian.org> + +.B lintex +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 2, or (at your option) any later +version. +.LP +.B lintex +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 +(here included in the file +.IR COPYING ")," +for more details. diff --git a/support/lintex/lintex.c b/support/lintex/lintex.c new file mode 100644 index 0000000000..f1b685f4e1 --- /dev/null +++ b/support/lintex/lintex.c @@ -0,0 +1,983 @@ +/* + Copyright (C) 2010-2012 Ryan Kavanagh <rak@debian.org> + Copyright (C) 1996–2002 Maurizio Loreti: + *------------------------------------------------------* + | Author: Maurizio Loreti, aka MLO or (HAM) I3NOO | + | Work: University of Padova - Department of Physics | + | Via F. Marzolo, 8 - 35131 PADOVA - Italy | + | Phone: ++39(49) 827-7216 FAX: ++39(49) 827-7102 | + | EMail: loreti@padova.infn.it | + | WWW: http://wwwcdf.pd.infn.it/~loreti/mlo.html | + *------------------------------------------------------* + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The lintex website now resides at: + http://github.com/ryanakca/lintex + + Description: the command "lintex [-i] [-r] [dir1 [dir2 ...]]" scans + the directories given as parameters (the default is the current + directory), looking for TeX-related files no more needed and to be + removed. With the option -i the user is asked before actually + removing any file; with the option -r, the given directories are + scanned recursively. + + Environment: the program has been developed under Solaris 2; but + should run on every system supporting opendir/readdir/closedir and + stat. The file names in the struct dirent (defined in <dirent.h>) + are assumed to be null terminated (this is guaranteed under Solaris + 2). + + History: + 1.00 - 1996-07-05 , first release + 1.01 - 1996-07-25 , improved (modify time in nodes) + 1.02 - 1996-10-16 , list garbage files w/out .tex + 1.03 - 1997-05-21 , call to basename; uploaded to CTAN, where lives + in /tex-archive/support/lintex . Added a man + page and a Makefile. + 1.04 - 1998-06-22 , multiple directories in the command line; -r + command option; -I and -R accepted, in addition + to -i and -r; more extensions in protoTree; code + cleanup. + 1.05 - 2001-12-02 , linked list structure optimized. + 1.06 - 2002-09-25 , added .pdf extension. + 1.07 - 2010-08-11 , don't delete read only files; delete .bbl BibTeX + files; add -k to keep final product; added + extensions for files generated by Beamer class + 1.08 - 2010-10-01 , Add support for different verbosity levels, be + relatively quiet by default; dropped support for + the DEBUG and FULLDEBUG compiler flags. It's all + taken care of with command line options now; + added a -p (pretend) flag that shows what we would + have removed, but doesn't do anything. + 1.09 - 2010-11-30 , Add support for removing files older than their + source; remove duplicate entry in usage; update + usage to not be wider than 72 characters. + 1.10 - 2011-01-30 , Also remove .thm (generated by ntheorem), .out + (generated by hyperref), .toc.old (memoir?); list + removal extensions in manpage. + 1.11 - 2011-11-07 , Also remove .synctex.gz files. + 1.12 - 2012-08-30 , Also remove xypic's .xyc files. + 1.13 - 2012-09-11 , Add support for configuration file; updated + documentation. + + ---------------------------------------------------------------------*/ + +/** + | Included files +**/ + +#include <stdio.h> /* Standard library */ +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <unistd.h> + +#include <sys/types.h> /* Unix proper */ +#include <sys/stat.h> +#include <dirent.h> + +#include <libconfig.h> /* Configuration file support */ + +/** + | Definitions: + | - LONG_ENOUGH: length of the buffer used to read the answer from the + | user, when the -i command option is specified; + | - MAX_B_EXT: maximum length of the extension for backup files (including + | the leading dot and the trailing '\0'). + | - TRUE, FALSE: guess what? + | - VERSION: lintex version + | - QUIET, WHISPER, VERBOSE and DEBUG: + | * QUIET: no output + | * WHISPER: only print actions that are taken (ie. list files that are + | removed, default starting as of 1.08) + | * VERBOSE: list files on which actions are taken as well as those which + | aren't (default up to / including 1.07) + | * DEDUG: print debug information (originally FULLDEBUG compiler flag) + | means print everything you can for those who want to debug. + | Errors will be sent to stderr regardless of the output level. +**/ + +#define LONG_ENOUGH 48 +#define MAX_B_EXT 8 +#define TRUE 1 +#define FALSE 0 +#define VERSION "1.13 (2012-09-11)" +#define QUIET 0 +#define WHISPER 1 +#define VERBOSE 2 +#define DEBUG 3 + +/** + | Type definitions: + | - Froot: the root of a linked list structure, where file names having a + | given extension (pointed to by Froot.extension) will be stored; + | these linked lists are also used to store directory names (with fake + | extension strings). + | - Fnode: an entry in the linked list of the file names; contains the + | file modification time, the file name and a pointer to the next node. + | As a side note, the so called 'struct hack', here used to store the + | file name, is not guaranteed to work by the current C ANSI standard; + | but no environment/compiler where it does not work is currently + | known. +**/ + +typedef struct sFroot { + char *extension; + struct sFnode *firstNode; + struct sFnode *lastNode; +} Froot; + +typedef struct sFnode { + time_t mTime; + struct sFnode *next; + int write; + char name[1]; +} Fnode; + +/** + | Global variables: + | - confirm: will be 0 or 1 according to the -i command option; + | - recurse: will be 0 or 1 according to the -r command option; + | - keep: will be 0 or 1 according to the -k command option; + | - output_level: See the definitions above for more details; + | - pretend: will be 0 or 1 according to -p command option; + | - older: will be 0 or 1 according to -o command option; + | - bExt: the extension for backup files: defaults to "~" (the emacs + | convention); + | - n_bExt: the length of the previous string; + | - programName: the name of the executable; + | - protoTree: Froot's of the file names having extensions relevant to + | TeX. ".tex" extensions are assumed to be pointed to by protoTree[0]. + | - keep_exts: Array containing extensions to keep. + | - protoTreeSize, keep_exts_size: number of elements in protoTree and + | keep_exts. +**/ + +Froot *protoTree; +char **keep_exts; +int protoTreeSize; + +static int confirm = FALSE; +static int recurse = FALSE; +static int keep = FALSE; +static int output_level = WHISPER; +static int pretend = FALSE; +static int older = FALSE; +static char bExt[MAX_B_EXT] = "~"; +static size_t n_bExt; +static char *programName; + +char *remove_exts[] = { + ".tex", /* Must be first */ + ".aux", + ".bbl", + ".blg", + ".dvi", + ".idx", + ".ilg", + ".ind", + ".lof", + ".log", + ".lot", + ".nav", + ".out", + ".pdf", + ".ps", + ".snm", + ".synctex.gz", + ".thm", + ".toc", + ".toc.old", + ".xyc" +}; + +char *keep_exts_defaults[] = { + ".pdf", + ".ps", + ".dvi" +}; +int keep_exts_size = 3; + +/** + | Procedure prototypes (in alphabetical order) +**/ + +static char *baseName(char *); +static Froot *buildTree(char *, Froot *); +static void clean(char *); +static void examineTree(Froot *, char *); +static void insertNode(char *, size_t, time_t, int, Froot *); +static void noMemory(void); +static void nuke(char *); +static void putsMessage(char *, int); +static void printTree(Froot *); +static void releaseTree(Froot *); +static void setupTrees(void); +static void syntax(void); + +/*---------------------------* + | And now, our main program | + *---------------------------*/ + +int main( + int argc, + char *argv[] +){ + Froot *dirNames; /* To hold the directories to be scanned */ + Fnode *pFN; /* Running pointer over directory names */ + int to_bExt = FALSE; /* Flag "next parameter to bExt" */ + + /** + | Scans the arguments appropriately; the required directories are stored + | in the linked list starting at "dirNames". + **/ + + programName = baseName(argv[0]); + + if ((dirNames = calloc(2, sizeof(Froot))) == 0) { + noMemory(); + } + dirNames->extension = "argv"; + + while (--argc) { + if ((*++argv)[0] == '-') { + switch ( (*argv)[1] ) { + case 'i': case 'I': + confirm = TRUE; + break; + + case 'r': case 'R': + recurse = TRUE; + break; + + case 'k': case 'K': + keep = TRUE; + break; + + case 'b': case 'B': + to_bExt = TRUE; + break; + + case 'q': case 'Q': + output_level = QUIET; + break; + + case 'v': case 'V': + output_level = VERBOSE; + break; + + case 'd': case 'D': + output_level = DEBUG; + break; + + case 'p': case 'P': + pretend = TRUE; + break; + + case 'o': case 'O': + older = TRUE; + break; + + default: + syntax(); + } + + } else { + if (to_bExt) { + strcpy(bExt, *argv); + to_bExt = FALSE; + } else { + insertNode(*argv, 0, 0, 0, dirNames); + } + } + } + + if (to_bExt) { + syntax(); + } + n_bExt = strlen(bExt); + + setupTrees(); + + /** + | If no parameter has been given, clean the current directory + **/ + + if ((pFN = dirNames->firstNode) == 0) { + clean("."); + } else { + while (pFN != 0) { + clean(pFN->name); + pFN = pFN->next; + } + } + releaseTree(dirNames); + + return EXIT_SUCCESS; +} + +/*------------------------------------------* + | The called procedures (in logical order) | + *------------------------------------------*/ + +static void setupTrees(void) +{ + /** + | Initialise protoTree, keep_exts + **/ + config_t cfg; /* To hold our config */ + config_setting_t *setting; /* To hold a setting */ + int i; /* Iterator */ + int needed; /* number of characters in filename */ + char *extension, *cfg_file; + + if (output_level >= DEBUG) + printf("Initialising protoTree, keep_exts.\n"); + + protoTreeSize = sizeof(remove_exts) / sizeof(char *) + 1; + if ((protoTree = malloc(sizeof(Froot) * protoTreeSize)) == 0) { + noMemory(); + } + /* remove_exts has (protoTreeSize - 1) elements */ + if (output_level >= DEBUG) + printf("Created protoTree of size %d\n", protoTreeSize); + for (i=0; i < protoTreeSize - 1; i++) { + protoTree[i].extension = remove_exts[i]; + protoTree[i].firstNode = 0; + protoTree[i].lastNode = 0; + if (output_level >= DEBUG) + printf("Added %d-th extension %s to protoTree of size %d at pos %d\n", + i, remove_exts[i], protoTreeSize, i); + } + + /* Add the sentinel */ + protoTree[protoTreeSize - 1].extension = 0; + protoTree[protoTreeSize - 1].firstNode = 0; + protoTree[protoTreeSize - 1].lastNode = 0; + if (output_level >= DEBUG) + printf("Added sentinal to protoTree at pos %d.\n", protoTreeSize - 1); + + keep_exts = keep_exts_defaults; + /** + | Initialise and read the config file + **/ + + config_init(&cfg); + + needed = strlen(getenv("HOME")) + strlen("/.lintexrc") + 1; + cfg_file = (char *) malloc(sizeof(char) * needed); + if (getenv("HOME")) { + strcpy(cfg_file, getenv("HOME")); + strcat(cfg_file, "/.lintexrc"); + if (output_level >= DEBUG) + printf("Using config file %s.\n", cfg_file); + } + + if (! access(cfg_file, R_OK)) { + /* We have read access to the config file */ + if (! config_read_file(&cfg, cfg_file)) { + fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + exit(EXIT_FAILURE); + } + + /* Lets extract our settings */ + /* Extensions to remove */ + setting = config_lookup(&cfg, "remove-exts"); + if (setting != NULL) { + unsigned int count = config_setting_length(setting); + + Froot *protoTreeTemp; + if ((protoTreeTemp = (Froot *) malloc(sizeof(Froot) * (count + protoTreeSize))) == 0) { + noMemory(); + } + memcpy(protoTreeTemp, protoTree, sizeof(Froot) * protoTreeSize); + if (output_level >= DEBUG) + printf("Copied protoTree to larger location to add config exts.\n"); + + for (i = 0; i < count; i++) { + extension = (char *) config_setting_get_string_elem(setting, i); + protoTreeTemp[protoTreeSize - 1 + i].extension = extension; + protoTreeTemp[protoTreeSize - 1 + i].firstNode = 0; + protoTreeTemp[protoTreeSize - 1 + i].lastNode = 0; + if (output_level >= DEBUG) + printf("Added %d-th config extension %s to protoTree of size %d at pos %d\n", + i, extension, protoTreeSize + count, protoTreeSize - 1 + i); + } + + /* Add the sentinel */ + protoTreeTemp[protoTreeSize - 1 + count].extension = 0; + protoTreeTemp[protoTreeSize - 1 + count].firstNode = 0; + protoTreeTemp[protoTreeSize - 1 + count].lastNode = 0; + protoTree = protoTreeTemp; + if (output_level >= DEBUG) + printf("Added sentinel to protoTree at position %d\n", protoTreeSize - 1 + count); + + protoTreeSize += count; + } + + /* Extensions to keep */ + setting = config_lookup(&cfg, "keep-exts"); + if (setting != NULL) { + int i; + keep_exts_size = config_setting_length(setting); + + if ((keep_exts = (char **) malloc(sizeof(char *) * keep_exts_size)) == 0) { + noMemory(); + } + + for (i = 0; i < keep_exts_size; i++) { + keep_exts[i] = (char *) config_setting_get_string_elem(setting, i); + if (output_level >= DEBUG) + printf("Added %d-th config extension %s to keep_exts of size %d at pos %d\n", + i, keep_exts[i], keep_exts_size, i); + } + } + } else { + if (! access(cfg_file, F_OK)) { + /* File exists */ + fprintf(stderr, + "Warning: Insufficient permissions to read config file $HOME/.lintexrc"); + } + } +} + +static void insertNode( + char *name, + size_t lName, + time_t mTime, + int write, + Froot *root +){ + + /** + | Creates a new Fnode, to be inserted at the _end_ of the linked + | list pointed to by root->firstNode (i.e., the list is organized + | as a "queue", a.k.a. "FIFO" list): if a new node cannot be created, + | an error message is printed and the program aborted. + | If "lName" is bigger than zero, the file name is represented by the + | first lName characters of "name"; otherwise by the whole string in + | "name". + **/ + + Fnode *pFN; /* The new node created by insertNode */ + size_t sSize; /* Structure size */ + + sSize = sizeof(Fnode) + (lName == 0 ? strlen(name) : lName); + + if ((pFN = malloc(sSize)) == 0) { + noMemory(); + } + pFN->mTime = mTime; + pFN->write = write; + pFN->next = 0; + + if (lName == 0) { + strcpy(pFN->name, name); + } else { + strncpy(pFN->name, name, lName); + pFN->name[lName] = '\0'; + } + + if (root->lastNode == 0) { + root->firstNode = pFN; + } else { + root->lastNode->next = pFN; + } + root->lastNode = pFN; +} + +static void noMemory(void) +{ + fprintf(stderr, "%s: couldn't obtain heap memory\n", programName); + exit(EXIT_FAILURE); +} + +static void clean( + char *dirName +){ + + /** + | Does the job for the directory "dirName". + | + | Builds a structure holding the TeX-related files, and does the + | required cleanup; finally, removes the file structure. + | If the list appended to "dirs" has been filled, recurse over the + | tree of subdirectories. + **/ + + Froot *teXTree; /* Root node of the TeX-related files */ + Froot *dirs; /* Subdirectories in this directory */ + Fnode *pFN; /* Running pointer over subdirectories */ + + if ((dirs = calloc(2, sizeof(Froot))) == 0) { + noMemory(); + } + dirs->extension = "subs"; + + if ((teXTree = buildTree(dirName, dirs)) != 0) { + + if (output_level >= DEBUG) { + printTree(teXTree); + } + + examineTree(teXTree, dirName); + releaseTree(teXTree); + } + + for (pFN = dirs->firstNode; pFN != 0; pFN = pFN->next) { + clean(pFN->name); + } + releaseTree(dirs); +} + +static Froot *buildTree( + char *dirName, + Froot *subDirs +){ + + /** + | - Opens the required directory; + | - allocates a structure to hold the names of the TeX-related files, + | initialized from the global structure "protoTree"; + | - starts a loop over all the files of the given directory. + **/ + + DIR *pDir; /* Pointer returned from opendir() */ + struct dirent *pDe; /* Pointer returned from readdir() */ + Froot *teXTree; /* Root node of the TeX-related files */ + + if (output_level >= DEBUG) { + printf("* Scanning directory \"%s\" - confirm = %c, recurse = %c, ", + dirName, (confirm ? 'Y' : 'N'), (recurse ? 'Y' : 'N')), + printf("keep = %c\n", (keep ? 'Y' : 'N')); + printf("* Editor trailer: \"%s\"\n", bExt); + puts("------------------------------Phase 1: directory scan"); + } + + if ((pDir = opendir(dirName)) == 0) { + fprintf(stderr, + "%s: \"%s\" cannot be opened (or is not a directory)\n", + programName, dirName); + return 0; + } + + if ((teXTree = malloc(protoTreeSize * sizeof(Froot))) == 0) { + noMemory(); + } + memcpy(teXTree, protoTree, protoTreeSize * sizeof(Froot)); + + while ((pDe = readdir(pDir)) != 0) { + char tName[FILENAME_MAX]; /* Fully qualified file name */ + struct stat sStat; /* To be filled by stat(2) */ + size_t len; /* Lenght of the current file name */ + size_t last; /* Index of its last character */ + char *pFe; /* Pointer to file extension */ + + /** + | - Tests for empty inodes (already removed files); + | - skips the . and .. (current and previous directory); + | - tests the trailing part of the file name against the extension of + | the backup files, to be always deleted. + **/ + + if (pDe->d_ino == 0) continue; + if (strcmp(pDe->d_name, ".") == 0) continue; + if (strcmp(pDe->d_name, "..") == 0) continue; + + sprintf(tName, "%s/%s", dirName, pDe->d_name); + + len = strlen(pDe->d_name); + last = len - 1; + + if (n_bExt != 0) { /* If 0, no backup files to delete */ + int crit; /* What exceeds backup extensions */ + + crit = len - n_bExt; + if (crit > 0 && strcmp(pDe->d_name + crit, bExt) == 0) { + nuke(tName); + continue; + } + } + + /** + | If the file is a directory and the -r option has been given, stores + | the directory name in the linked list pointed to by "subDirs", for + | recursive calls. + | + | N.B.: if stat(2) fails, the file is skipped. + **/ + + if (stat(tName, &sStat) != 0) { + fprintf(stderr, "File \"%s", tName); + perror("\""); + continue; + } + + if (S_ISDIR(sStat.st_mode) != 0) { + + if (output_level >= DEBUG) { + printf("File %s - is a directory\n", pDe->d_name); + } + + if (recurse) { + insertNode(tName, 0, 0, 0, subDirs); + } + continue; + } + + /** + | If the file has an extension (the rightmost dot followed by at + | least one character), and if that extension matches one of the + | entries in teXTree[i].extension: stores the file name (with the + | extension stripped) in the appropriate linked list, together with + | its modification time. + **/ + + if ((pFe = strrchr(pDe->d_name, '.')) != 0) { + size_t nameLen; + + nameLen = pFe - pDe->d_name; + if (nameLen < last) { + Froot *pTT; + + if (output_level >= DEBUG) { + printf("File %s - extension %s", pDe->d_name, pFe); + } + + /** + | Loop on recognized TeX-related file extensions + **/ + + for (pTT = teXTree; pTT->extension != 0; pTT++) { + if (strcmp(pFe, pTT->extension) == 0) { + int i; + if (keep) { + i = 0; + } else { + i = keep_exts_size; + } + for (i = 0; i < keep_exts_size; i++) { + if (strcmp(pFe, keep_exts[i]) == 0) { + /** + | The current file's extension is in keep_exts, we want to keep + | it. + **/ + i = -1; + break; + } + } + + if ((!keep) | (i != -1)) { + /** + | Only add the file if we didn't find its extension in keep_exts + **/ + insertNode(pDe->d_name, nameLen, sStat.st_mtime, access(tName, W_OK), pTT); + if (output_level >= DEBUG) { + printf(" - inserted in tree"); + } + } else if (keep) { + if (output_level >= DEBUG) { + printf(" - not inserted in tree (extension in keep-exts)"); + } else if (output_level >= VERBOSE) { + printf("*** %s not removed; keep activated ***\n", pDe->d_name); + } + } + break; + } + } /* loop on known extensions */ + + if (output_level >= DEBUG) { + puts(""); + } + + } else { + if (output_level >= VERBOSE) { + printf("File %s - empty extension\n", pDe->d_name); + } + } + + } else { + if (output_level >= DEBUG) { + printf("File %s - without extension\n", pDe->d_name); + } + } + } /* while (readdir) ... */ + + if (closedir(pDir) != 0) { + fprintf(stderr, "Directory \"%s", dirName); + perror("\""); + } + + return teXTree; +} + +static void printTree( + Froot *teXTree +){ + + /** + | Prints all the file names archived in the linked lists (for + | debugging purposes). + **/ + + if (output_level >= DEBUG) { + Froot *pTT; /* Running pointer over teXTree elements */ + + puts("------------------------------Phase 2: tree printout");; + for (pTT = teXTree; pTT->extension != 0; pTT++) { + Fnode *pTeX; /* Running pointer over TeX-related files */ + int nNodes = 0; /* Counter */ + + for (pTeX = pTT->firstNode; pTeX != 0; pTeX = pTeX->next) { + ++nNodes; + printf("%s%s\n", pTeX->name, pTT->extension); + } + printf(" --> %d file%s with extension %s\n", nNodes, + (nNodes == 1 ? "" : "s"), pTT->extension); + } + } +} + +static void examineTree( + Froot *teXTree, + char *dirName +){ + + /** + | Examines the linked lists for this directory doing the effective + | cleanup. + **/ + + Froot *pTT; /* Pointer over linked list trees */ + Fnode *pTeX; /* Running pointer over the .tex files */ + + /** + | Looks, for all the .tex files, if a corresponding entry with the same + | name exists (with a different extension) in the other lists; if so, + | and if its modification time is later than the one of the related + | .tex file, removes it from the file system. + **/ + putsMessage("------------------------------Phase 3: effective cleanup", + DEBUG); + + for (pTeX = teXTree->firstNode; pTeX != 0; pTeX = pTeX->next) { + char tName[FILENAME_MAX]; + + sprintf(tName, "%s/%s.tex", dirName, pTeX->name); + pTT = teXTree; + + if (output_level >= DEBUG) { + printf(" Finding files related to %s:\n", tName); + } + + for (pTT++; pTT->extension != 0; pTT++) { + Fnode *pComp; + + for (pComp = pTT->firstNode; pComp != 0; pComp = pComp->next) { + char cName[FILENAME_MAX]; + + if (strcmp(pTeX->name, pComp->name) == 0) { + sprintf(cName, "%s/%s%s", dirName, pTeX->name, pTT->extension); + pComp->name[0] = '\0'; + + /** + | Remove generated file if more recent than source (default) or if + | we permit the removal of files older than source + **/ + if (difftime(pComp->mTime, pTeX->mTime) > 0.0 || older) { + if (pComp->write == 0) { + nuke(cName); + } else { + if (output_level >= DEBUG) { + printf("*** %s readonly; perms are %d***\n", cName, + pComp->write); + } + if (output_level >= VERBOSE) { + printf("*** %s not removed; it is read only ***\n", cName); + } + } + } else { + if (output_level >= VERBOSE) { + printf("*** %s not removed; %s is newer ***\n", cName, tName); + } + } + break; + } + } + } + } + + /** + | If some garbage file has not been deleted, list it + **/ + + putsMessage("------------------------------Phase 4: left garbage files", + DEBUG); + + pTT = teXTree; + for (pTT++; pTT->extension != 0; pTT++) { + Fnode *pComp; + + for (pComp = pTT->firstNode; pComp != 0; pComp = pComp->next) { + if (pComp->name[0] != '\0') { + char cName[FILENAME_MAX]; + + sprintf(cName, "%s/%s%s", dirName, pComp->name, pTT->extension); + if (output_level >= VERBOSE) { + printf("*** %s not removed; no .tex file found ***\n", cName); + } + } + } + } +} + +static void releaseTree( + Froot *teXTree +){ + + /** + | Cleanup of the file name storage structures: an _array_ of Froot's, + | terminated by a NULL extension pointer as a sentinel, is assumed. + | Every linked list nodes is freed; then releaseTree frees also the + | root structure. + **/ + + Froot *pFR; + + putsMessage("------------------------------Phase 5: tree cleanup", DEBUG); + + for (pFR = teXTree; pFR->extension != 0; pFR++) { + Fnode *pFN, *p; + + int nNodes = 0; + if (output_level >= DEBUG) { + printf("Dealing with extensions %s ...", pFR->extension); + } + + for (pFN = pFR->firstNode; pFN != 0; pFN = p) { + p = pFN->next; + free(pFN); + + nNodes++; + } + + if (output_level >= DEBUG) { + printf(" %d nodes freed\n", nNodes); + } + } + + free(teXTree); +} + +static void nuke( + char *name +){ + + /** + | Removes "name" (the fully qualified file name) from the file system + **/ + + if ((output_level >= DEBUG) || pretend) { + printf("*** File \"%s\" would have been removed ***\n", name); + } + + if (pretend) { + /* We don't need to continue if we aren't going to remove the file */ + return; + } + + if (confirm) { + char yn[LONG_ENOUGH], c; + + do { + printf("Remove %s (y|n) ? ", name); + if (fgets(yn, LONG_ENOUGH, stdin) == 0) return; + if (yn[0] == '\0' || (c = tolower((unsigned char) yn[0])) == 'n') { + return; + } + } while (c != 'y'); + } + + if (remove(name) != 0) { + fprintf(stderr, "File \"%s", name); + perror("\""); + } else { + if (output_level >= WHISPER) { + printf("%s has been removed\n", name); + } + } + +} + +static char *baseName( + char *pc +){ + char *p; + +/** + | Strips the (eventual) path information from the string pointed + | to by 'pc'; if no file name is given, returns an empty string. +**/ + + p = strrchr(pc, '/'); + if (p == 0) return pc; + return ++p; +} + +static void putsMessage( + char *message, + int message_level +){ + if (message_level <= output_level) { + puts(message); + } +} + +static void syntax() +{ + printf("lintex version %s\n", VERSION); + puts("Usage:"); + printf(" %s [OPTIONS] [DIR [DIR ...]]\n", programName); + puts("Purpose:"); + puts(" Removes unneeded TeX auxiliary files and editor backup files from" + " the"); + puts(" given directories (default: the current directory); the TeX files" + " are"); + puts(" actually removed only if their modification time is more recent" + " than"); + puts(" the one of the related TeX source and if they aren't readonly."); + puts(" Please see the manpage for a list of extensions that get removed."); + puts("Options:"); + puts(" -i : asks the user before removing any file;"); + puts(" -r : scans recursively the subdirectories of the given"); + puts(" directories;"); + puts(" -b ext : \"ext\" is the trailing string identifying editor backup" + " files"); + puts(" (defaults to \"~\"). -b \"\" avoids any cleanup of special"); + puts(" files;"); + puts(" -p : pretend, show what files would be removed but don't actually"); + puts(" remove them;"); + puts(" -k : keeps final document (.pdf, .ps, .dvi);"); + puts(" -o : permit removal of files older than their sources;"); + puts(" -q : quiet, only print error messages;"); + puts(" -v : verbose, prints which files were removed and which weren't;"); + puts(" -d : debug output, prints the answers to all of life's questions."); + + exit(EXIT_SUCCESS); +} diff --git a/support/lintex/lintex.com b/support/lintex/lintex.com new file mode 100644 index 0000000000..6c77c6facd --- /dev/null +++ b/support/lintex/lintex.com @@ -0,0 +1,90 @@ +$! Last Rev.: MLO 1996-03-18 +$! File: LINTEX.COM --- To delete TeX-related garbage (files with extensions +$! .DVI, .AUX, .TOC, ...) +$! @LINTEX deletes TeX-related files only if a .TEX exists +$! @LINTEX F ('force') deletes TeX-related files anyway +$! @LINTEX C ('confirm') asks user if a .TEX does not exists +$!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +$! +$ ver = f$verify(0) +$ say := write sys$output +$! +$ if p1 .eqs. "" +$ then +$ opt = "" +$ else +$ opt = f$extract(0, 1, f$edit(p1, "upcase")) +$ if opt.nes."F" .and. opt.nes."C" +$ then +$ say "''opt': invalid option" +$ exit +$ endif +$ endif +$! +$ filext = "DVI/AUX/TOC/LIS/LOF/LOT/PS" +$ numext = 0 +$! +$ purge /log +$! +$ eloop: ext = f$element(numext, "/", filext) +$ if ext .nes. "/" +$ then +$ filnam = "*." + ext +$ floop: file = f$search(filnam, 1) +$ if file .nes. "" +$ then +$ first = f$locate("]", file) +$ len = f$length(file) +$ if first .eq. len +$ then +$ short = file +$ else +$ short = f$extract(first+1, len, file) +$ endif +$ filedate = f$cvtime(f$file(file, "cdt")) +$ gosub delchk +$ goto floop +$ endif +$ numext = numext + 1 +$ goto eloop +$ endif +$! +$ if ver then set verify +$ exit +$! +$! SUBROUTINE DELCHK +$! +$ delchk: +$ texfile = f$parse(".tex;", file) +$ tex = f$search(texfile, 2) +$ if tex .eqs. "" +$ then +$ errmes = "a .TEX file does not exist." +$ gosub chkopt +$ else +$ texdate = f$cvtime(f$file(tex, "cdt")) +$ if texdate .les. filedate +$ then +$ delete /log 'file' +$ else +$ errmes = "the .TEX file is newer." +$ gosub chkopt +$ endif +$ endif +$ return +$! +$! SUBROUTINE CHKOPT +$! +$ chkopt: +$ if opt.eqs."F" +$ then +$ delete /log 'file' +$ else +$ if opt.eqs."C" +$ then +$ delete /log /confirm 'file' +$ else +$ say "--> ''short' not deleted; ''errmes'" +$ endif +$ endif +$ return diff --git a/support/lintex/lintex.pdf b/support/lintex/lintex.pdf Binary files differnew file mode 100644 index 0000000000..4bbb58b6e5 --- /dev/null +++ b/support/lintex/lintex.pdf diff --git a/support/lintex/lintexrc b/support/lintex/lintexrc new file mode 100644 index 0000000000..45f77d192a --- /dev/null +++ b/support/lintex/lintexrc @@ -0,0 +1,2 @@ +keep-exts = [".pdf", ".ps", ".dvi"]; +remove-exts = [".glo", ".gls"]; diff --git a/support/lintex/mktests.sh b/support/lintex/mktests.sh new file mode 100644 index 0000000000..6f97bc3150 --- /dev/null +++ b/support/lintex/mktests.sh @@ -0,0 +1,13 @@ +#!/bin/sh +mkdir -p test/dir1 +for ext in foo bar dir1/baz; do \ + touch test/${ext}.tex; \ +done +sleep 1 +for test in foo bar dir1/baz; do \ + for ext in pdf ps dvi log aux; do \ + touch test/${test}.${ext}; \ + done; \ +done +sleep 1 +touch test/foo.tex |