diff options
author | Eric Gerbier <gerbier@users.sourceforge.net> | 2012-11-21 06:19:57 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-11-21 06:19:57 -0800 |
commit | 1dfc44e72c21c074c25dff8f2832a06747c544e9 (patch) | |
tree | d0f53149a80a1130372dc9d7d3fa7807bfa2aa30 | |
download | rpmorphan-1dfc44e72c21c074c25dff8f2832a06747c544e9.tar.gz rpmorphan-1dfc44e72c21c074c25dff8f2832a06747c544e9.tar.bz2 rpmorphan-1dfc44e72c21c074c25dff8f2832a06747c544e9.zip |
Imported Upstream version 1.11upstream/1.11upstream
-rw-r--r-- | Authors | 7 | ||||
-rw-r--r-- | COPYING | 280 | ||||
-rw-r--r-- | Changelog | 105 | ||||
-rw-r--r-- | Makefile | 178 | ||||
-rw-r--r-- | NEWS | 148 | ||||
-rw-r--r-- | Readme | 45 | ||||
-rw-r--r-- | Readme.fr | 40 | ||||
-rw-r--r-- | Todo | 9 | ||||
-rw-r--r-- | keep | 0 | ||||
-rw-r--r-- | locale/en/rpmorphan_trans.pl | 81 | ||||
-rw-r--r-- | locale/fr_FR/rpmorphan_trans.pl | 80 | ||||
-rw-r--r-- | rpmdep.1 | 250 | ||||
-rwxr-xr-x | rpmdep.pl | 414 | ||||
-rw-r--r-- | rpmduplicates.1 | 229 | ||||
-rwxr-xr-x | rpmduplicates.pl | 266 | ||||
-rw-r--r-- | rpmextra.1 | 222 | ||||
-rwxr-xr-x | rpmextra.pl | 425 | ||||
-rw-r--r-- | rpmorphan-curses-lib.pl | 501 | ||||
-rwxr-xr-x | rpmorphan-lib.pl | 792 | ||||
-rw-r--r-- | rpmorphan-tk-lib.pl | 441 | ||||
-rw-r--r-- | rpmorphan.1 | 489 | ||||
-rw-r--r-- | rpmorphan.logrotate | 7 | ||||
-rw-r--r-- | rpmorphan.lsm | 16 | ||||
-rwxr-xr-x | rpmorphan.pl | 1405 | ||||
-rw-r--r-- | rpmorphan.spec | 271 | ||||
-rw-r--r-- | rpmorphanrc.sample | 54 | ||||
-rw-r--r-- | rpmusage.1 | 358 | ||||
-rwxr-xr-x | rpmusage.pl | 567 |
28 files changed, 7680 insertions, 0 deletions
@@ -0,0 +1,7 @@ +this software has been written by +Eric Gerbier<gerbier@users.sourceforge.net> + +It has been released under the GNU GENERAL PUBLIC LICENSE, version 2 + +the homepage of this project is http://sourceforge.net/projects/rpmorphan + @@ -0,0 +1,280 @@ + 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 non-commercial 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 diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..0f5cb9e --- /dev/null +++ b/Changelog @@ -0,0 +1,105 @@ +2012-08-03 Eric Gerbier <gerbier@users.sourceforge.net> 1.11 + * add suggests option + * add /etc/rpmorphanrc + * fix bug with space in filenames + * add env RPMORPHAN_METHOD for debugging + +2011-07-08 Eric Gerbier <gerbier@users.sourceforge.net> 1.10 + * bugfix eval for Curses (mandriva) + * fix bad version display + * clean all scripts + * reduce size/duplicated code in rpmorphan/rpmusage + * reduce menu length in curses gui (< 72) + * display package summary in a popup on S key + +2011-03-21 Eric Gerbier <gerbier@users.sourceforge.net> 1.9 + * fix perlcritic warnings + * add grpmorphan as link to rpmorphan -gui + * add rpmextra tool + +2010-04-30 Eric Gerbier <gerbier@users.sourceforge.net> 1.8 + * fix bug on rpmdep ( missing installtime in urpm mode) + * add help translation (gui) + +2010-03-01 Eric Gerbier <gerbier@users.sourceforge.net> 1.7 + * Curses is no more an rpm dependency + * add reload button + +2010-01-20 Eric Gerbier <gerbier@users.sourceforge.net> 1.6 + * curses window size is now variable to use full available size + * split core and gui code (new rpmorphan-curses-lib.pl rpmorphan-tk-lib.pl) + * only add "remove" button in root mode on curses interface + * add a confirm dialog box on remove action + * add sub is_remove_allowed (rpmorphan-lib.pl) + * rename sub filter into rpmfilter (rpmorphan-lib.pl) + * info window is now scrollable (curses gui) + +2009-11-27 Eric Gerbier <gerbier@users.sourceforge.net> 1.5 + * add logrotate config + * remove all matching packages (same name) + * fix a bug on curses interface + * merge spec file with fedora one + * add info for fedora/mandriva users in package description + * add Readme.fr translation + * can use RPM2 perl package for faster database access + * use URPM perl package for faster database access on mandriva + +2009-01-05 Eric Gerbier <gerbier@users.sourceforge.net> 1.4 + * improve diagnostic if "rpm -e" return some comments + +2008-10-19 Eric Gerbier <gerbier@users.sourceforge.net> 1.3 + * fix a bug if exclude are set in config file (thanks Szymon Siwek) + * display number of deleted file + +2008-04-07 Eric Gerbier <gerbier@users.sourceforge.net> 1.2 + * write log to /var/log/rpmorphan.log + * add rpmdep.pl tool + * add rpmduplicate.pl tool + +2007-11-22 Eric Gerbier <gerbier@users.sourceforge.net> 1.1 + * (rpmorphan) add a default target : guess-lib + * (rpmorphan) test rpm delete + * fix warnings from perlcritic tool + * (rpmorphan) split too complex code (add read_one_rc, init_cache) + * (rpmorphan) add keep-file option, which allow to cutomize the keep file + * add rpmorphan-lib.pl to store common code for rpmorphan, rpmusage + * (rpmusage) add a default target : all + +2007-04-27 Eric Gerbier <gerbier@users.sourceforge.net> 1.0 + * fix bug with tk : core dump because update call before mainloop + * add w_ prefix for widgets variables + * add dry-run (simulation) mode + * (gui) add 3 counters : packages, orphans, selected + * (gui) display_status call debug sub + * new Liste_pac global variable + * (gui) change curses geometry to work on console + * (gui) recursive analysis + * improve virtuals : now check how many package provide this one + +2007-03-29 Eric Gerbier <gerbier@users.sourceforge.net> 0.9 + * add curses interface + * test for root access before removing packages + * add optionnal configuration file + * recode debugging system + * apply conway coding rules + * add tk_get_current_elem sub + * add get_from_command_line sub + * add status widget + +2007-03-08 Eric Gerbier <gerbier@users.sourceforge.net> 0.8 + * add simple graphical user interface (optionnal) + * remove global variable opt_verbose + * split code in functions : access_time_filter, read_rpm_data, search_orphans + +2007-02-28 Eric Gerbier <gerbier@users.sourceforge.net> 0.4 + * add optionnal cache + +2007-02-03 Eric Gerbier <gerbier@users.sourceforge.net> 0.3 + * add rpmusage.pl tool + * add a link from rpmorphan.pl to rpmorphan + +2007-01-30 Eric Gerbier <gerbier@users.sourceforge.net> 0.2 + * add permanent list of exclude + +2007-01-23 Eric Gerbier <gerbier@users.sourceforge.net> 0.1 + * initial release diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0c79441 --- /dev/null +++ b/Makefile @@ -0,0 +1,178 @@ +# $Id: rpmorphan-1.11 | Makefile | Wed Aug 1 14:09:35 2012 +0200 | Eric Gerbier $ +# recommanded by debian install guide +DESTDIR= + +PACKAGE=rpmorphan + +SHELL = /bin/sh +VERSION=$(shell grep Version rpmorphan.lsm | awk '{print $$2}') + +BINDIR = $(DESTDIR)/usr/bin +LOGETC = $(DESTDIR)/etc/ +LOGROT = $(LOGETC)/logrotate.d/ +MANDIR = $(DESTDIR)/usr/share/man +MAN1DIR = $(MANDIR)/man1 +# on some distribution it is $(PACKAGE)-$(VERSION) (fedora), on others $(PACKAGE) (mandriva) +# we force it with a define in rpm target to be able to build on any host the same way +DOCDIR=$(DESTDIR)/usr/share/doc/$(PACKAGE)-$(VERSION) +# for keep file +DATADIR=$(DESTDIR)/var/lib/rpmorphan +# locales +TARGET_LOCALE=$(DESTDIR)/usr/lib/rpmorphan/locale + +# log +LOGDIR=$(DESTDIR)/var/log +LOGFILE=rpmorphan.log + +DOC = Authors Changelog COPYING NEWS Todo $(PACKAGE).lsm $(PACKAGE).spec Makefile Readme Readme.fr rpmorphanrc.sample +DATA = keep +CONF = rpmorphan.logrotate + +# others programs +PROG2=rpmusage rpmdep rpmduplicates rpmextra +LIB=$(PACKAGE)-lib rpmorphan-curses-lib rpmorphan-tk-lib + +PROGS = $(PACKAGE) $(PROG2) +MANPAGES1 := $(addsuffix .1, $(PROGS)) +HTMLPAGES := $(addsuffix .1.html, $(PROGS)) +SCRIPTS := $(addsuffix .pl, $(PROGS) $(LIB) ) + +# file with keyword +KEYWORD_FILES = $(SCRIPTS) Makefile Readme Readme.fr rpmorphanrc.sample + +# translations +LANGS := en fr_FR +LOCALE_BASE := rpmorphan_trans.pl +LOCALES := $(foreach lang,$(LANGS),$(addprefix locale/$(lang)/,$(LOCALE_BASE))) + +# convert pod to other doc format +%.1 : %.pl + pod2man $^ > $@ + +%.1.html : %.pl + pod2html --header $^ | sed -f html.sed > $@ + tidy -m -i -asxhtml -utf8 --doctype strict $@ || echo "tidy" + +# loop to check all perl scripts +define check_perl_script + for fic in $(SCRIPTS);do \ + perl -cw $$fic || exit; \ + perlcritic --verbose 10 -3 $$fic || exit; \ + done; +endef + +# loop to make links on all programs +define make_links + for fic in $(PROGS);do \ + cd $(BINDIR) && ln -s $$fic.pl $$fic; \ + done; +endef + +# loop to install all locales +define install_locales + for lang in $(LANGS); do \ + d=$(TARGET_LOCALE)/$$lang; \ + mkdir --parents $$d; \ + (cd locale/$$lang && install --mode=644 $(LOCALE_BASE) $$d); \ + done +endef + +# default +help : + @echo "available target are :" + @echo "make all : same as make install" + @echo "make help : this help" + @echo "make install : install software" + @echo "make installdoc : install software documentation" + @echo "make uninstall : remove software" + @echo "make uninstalldoc : remove software documentation" + @echo "### for project packagers only ###########" + @echo "make alldist : build all packages" + @echo "make check : check perl syntaxe" + @echo "make clean : remove temporary files" + @echo "make dist : build a tar.gz package" + @echo "make html : build html doc from pod" + @echo "make man : build man page from pod" + @echo "make rpm : build an rpm package" + +all: install + +# install perl scripts +install : $(DOC) $(MANPAGES1) $(SCRIPTS) + mkdir -p $(BINDIR) + install -m 755 $(SCRIPTS) $(BINDIR) + $(make_links) + cd $(BINDIR) && ln -s rpmorphan grpmorphan + mkdir -p $(MAN1DIR) + install -m 644 ${MANPAGES1} $(MAN1DIR) + mkdir -p $(DATADIR) + install -m 644 ${DATA} $(DATADIR) + mkdir -p $(TARGET_LOCALE) + $(install_locales) + mkdir -p $(LOGDIR) + touch $(LOGDIR)/${LOGFILE} + chmod 640 $(LOGDIR)/${LOGFILE} + mkdir -p $(LOGROT) + install -m 644 ${CONF} $(LOGROT)/rpmorphan + install -m 644 rpmorphanrc.sample $(LOGETC)/rpmorphanrc + +# install doc +installdoc : $(DOC) + mkdir -p $(DOCDIR) + install -m 644 ${DOC} $(DOCDIR) + +uninstall : + cd $(BINDIR) && rm $(SCRIPTS) $(PROGS) + cd $(MAN1DIR) && rm ${MANPAGES1} + rm -rf $(DATADIR) + rm -f $(LOGROT)/rpmorphan + rm -f $(LOGETC)/rpmorphanrc + +uninstalldoc : + rm -rf $(DOCDIR) + +################################################################################ +# targets for project packagers +################################################################################ + +# build all packages +alldist : check dist rpm html + +# check perl script syntax +check : $(SCRIPTS) + $(check_perl_script) + +# build man pages +man : $(MANPAGES1) + +# build tar.gz package +dist : $(DOC) man $(SCRIPTS) expand + mkdir $(PACKAGE)-$(VERSION) + cp -a ${CONF} $(DATA) locale $(DOC) $(MANPAGES1) $(SCRIPTS) $(PACKAGE)-$(VERSION) + tar cvfz $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION) + rm -rf $(PACKAGE)-$(VERSION) + ~/bin/gensign.sh $(PACKAGE)-$(VERSION).tar.gz + +# build rpm package +rpm : dist + rpmbuild -ta --sign --define '_docdir_fmt %%{NAME}-%%{VERSION}' $(PACKAGE)-$(VERSION).tar.gz + +# clean temp files +clean : unexpand + rm -f $(MANPAGES1) + rm -f $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION).tar.gz.sig + rm -f pod2* + rm -f *.html + +# build man page in html for web site +html : $(HTMLPAGES) + mv *.html ../web + +# expand svn keywords just for publish +expand: $(KEYWORD_FILES) + git tag -f "$(PACKAGE)-$(VERSION)" + git-svn-keyword-expand $(KEYWORD_FILES) + +# remove svn keywords to keep repository clean +unexpand: $(KEYWORD_FILES) + git-svn-keyword-unexpand $(KEYWORD_FILES) @@ -0,0 +1,148 @@ + 1.11 version + ------------- +new features : +* add suggests option +* add /etc/rpmorphanrc +* add env RPMORPHAN_METHOD for debugging + +bugfix : +* fix bug with space in filenames +------------------------------------------------------------------------------- + 1.10 version + ------------- +new features : +* display package summary in a popup on S key + +bugfix : +* bugfix eval for Curses (mandriva) +* fix bad version display + +improvment : +* reduce size/duplicated code in rpmorphan/rpmusage +* reduce menu length in curses gui (< 72) + +others : +* clean all scripts +------------------------------------------------------------------------------- + 1.9 version + ------------- +new features : +- add rpmextra tool +- add grpmorphan as link to rpmorphan -gui +others : +- fix perlcritic warnings +------------------------------------------------------------------------------- + 1.8 version + ------------- +bugfix : +- fix bug on rpmdep ( missing installtime in urpm mode) + +new features : +- add help translation (gui) +------------------------------------------------------------------------------- + 1.7 version + ------------- +bugfix : +- Curses is no more an rpm dependency + +new features : +- add reload button +------------------------------------------------------------------------------- + 1.6 version + ------------- +other : +- add a confirm dialog box on remove action +- curses window size is now variable to use full available size +- info window is now scrollable (curses gui) +------------------------------------------------------------------------------- + 1.5 version + ------------- +new features : +- use URPM perl package for faster database access on mandriva +- can use RPM2 perl package for faster database access (fedora) +bugfix : +- fix a bug on curses interface +- remove all matching packages +others : +- merge spec file with fedora one +- add info for fedora/mandriva users +------------------------------------------------------------------------------- + 1.4 version + ------------- +other : +when removing a package, anf if rpm send some output, the code now check the rpm +database to know if the remove works or not +------------------------------------------------------------------------------- + 1.3 version + ------------- +incompatibility : none +bugfix : +- fix a bug if exclude are set in config file (thanks Szymon Siwek) +new features : +- display number of deleted file +other : +------------------------------------------------------------------------------- + 1.2 version + ------------- +incompatibility : none +bugfix : none +new features : +- write log to /var/log/rpmorphan.log +- add rpmdep.pl tool +- add rpmduplicate.pl tool +other : +------------------------------------------------------------------------------- + 1.1 version + ------------- +incompatibility : none +bugfix : none +new features : +- (rpmorphan) add a default target : guess-lib +- (rpmorphan) add keep-file option, which allow to cutomize the keep file +- (rpmusage) add a default target : all +other : +- the different tools now share a common library +- rpmlint was used to improve the rpm package + +------------------------------------------------------------------------------- + 1.0 version + ------------- +add dry-run (simulation) mode +(gui) add 3 counters : packages, orphans, selected +(gui) recursive analysis +improve virtuals : now check if how many package provide this one +------------------------------------------------------------------------------- + 0.9 version + ------------- +add another graphical interface (curses), available by the --curses command line +argument +add configuration file +------------------------------------------------------------------------------- + 0.8 version + ------------- +add simple graphical user interface (optionnal), which require +the perl-tk package :use --gui command line argument + +the code has been split in smaller subroutines. +------------------------------------------------------------------------------- + 0.4 version + ------------- +add optionnal cache, which can be shared by all rpmorphan tools +to spare rpm query time : +use --use-cache and --clear-cache command line arguments +------------------------------------------------------------------------------- + 0.3 version + ------------- +new feature : add rpmusage tool +------------------------------------------------------------------------------- + + 0.2 version + ------------- +new feature : add a permanent list of exclude file (default is empty) and +4 new options for rpmorphan : --zero-keep, --list-keep, --add-keep, --del-keep + +------------------------------------------------------------------------------- + 0.1 version + ------------- + +first public release @@ -0,0 +1,45 @@ +rpmorphan finds "orphaned"[2,5] packages on your system. It determines +which packages have no other packages depending on their installation, +and shows you a list of these packages. It intends to be clone of +deborphan Debian tools for rpm packages. + +It will try to help you to remove unused packages, for example: +* after a distribution upgrade +* when you want to suppress packages after some tests + +Several tools are also provided : +* rpmusage - display rpm packages last use date +* rpmdep - display the full dependency of an installed rpm package +* rpmduplicates - find programs with several version installed +* rpmextra - find installed packages not in distribution + +notes : +******* + +For all users : +[1] this rpm package will make rpmorphan work in command line mode. +If you want to use a graphical interface, you will need to install extra +rpm packages : perl-Tk or perl-Curses-UI. + +For Fedora users : +[2] Note that orphan is used in the sense of Debian's deborphan, and +is NOT the same as Fedora orphaned packages which are packages that +have no current maintainer. + +[3] Yum offers a program called 'package-cleanup' which, +called with the '--leaves' option, can show quickly a list of +unused library packages. + +[4] rpmorphan can be speed up by installing the perl-RPM2 rpm package + +For Mandriva users : +[5] the 'urpme --auto-orphans' command show quickly a a list of +unused library packages. + +quick start +*********** +rpmorphan --gui --all + +-- +Eric Gerbier +$Id: rpmorphan-1.11 | Readme | Mon Mar 21 12:08:17 2011 +0000 | gerbier $ diff --git a/Readme.fr b/Readme.fr new file mode 100644 index 0000000..e724ea2 --- /dev/null +++ b/Readme.fr @@ -0,0 +1,40 @@ +Le logiciel rpmorphan liste les packages rpm qui n'ont plus de +dépendances avec les autres paquets installés sur votre système. +C'est un clone du logiciel deborphan de debian pour les packages rpm. + +Il peut vous aider pour supprimer les packages inutilisés, par exemple : +* après une montée de version système +* lors de la suppression de logiciels après des tests + +plusieurs outils sont également fournis : +* rpmusage : donne la date de la dernière utilisation d'un package +* rpmdep : fournit l'ensemble des dependances (recursive) d'un package +* rpmduplicates : cherche les logiciels qui ont plusieurs versions installées +* rpmextra : cherche les packages installés hors distribution + +notes : +******* +Pour tous les utilisateurs +[1] ce package rpm permet de faire fonctionner rpmorphan en mode +ligne de commande. Si vous voulez profiter d'une interface graphique, +vous devez installer des packages rpm suplementaires : perl-Tk ou perl-Curses-UI + +Pour les utilisateurs de Fedora : +[2] la notion d'orphelin est à prendre au sens du logiciel debian deborphan, et +non pas au sens général de Fedora (packages sans mainteneur). + +[3] Yum contient le programme 'package-cleanup', qui appelé avec l'option --leaves, +permet d'afficher rapidement les librairies inutilisées + +[4] rpmorphan peut être accéléré en installant le package rpm perl-RPM2 + +Pour les utilisateurs de Mandriva +[5] la commande 'urpme --auto-orphans' permet d'afficher rapidement les librairies inutilisées. + +demarrage rapide +**************** +rpmorphan.pl --all --gui + +-- +Eric Gerbier +$Id: rpmorphan-1.11 | Readme.fr | Mon Mar 21 12:08:17 2011 +0000 | gerbier $ @@ -0,0 +1,9 @@ +graphical interface +- display more informations (summary, version, install time ? ) +- search tool +- add command line options as menus ? +- clean display when delete rpm ? + +require/recommends/suggest (suse) ? + +translate gui menu ? diff --git a/locale/en/rpmorphan_trans.pl b/locale/en/rpmorphan_trans.pl new file mode 100644 index 0000000..f03300d --- /dev/null +++ b/locale/en/rpmorphan_trans.pl @@ -0,0 +1,81 @@ +#!/usr/bin/perl +############################################################################### +# rpmorphan-curses-lib.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-curses-lib.pl 170 2010-01-14 12:13:48Z gerbier $ +# +# 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. +# +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +use vars qw( %TRANS ); + +$TRANS{'help'} = <<'EOF'; +rpmorphan will help you to clean unused package +it will show you orphaned packages (without any rpm dependencies to them) + +buttons are : +------------- +remove : remove selected package(s) +info : show informations about current package +select all : select all packages (dangerous) +unselect all : unselect all packages +log : display list of suppressed packages +help : this help screen +quit : exit rpmorphan + +mouse use : +----------- +- left click to select/unselect a package +- right click to display rpm informations + +hotkeys : +--------- +x/r : remove package +i : show package information +S : show package summary in a popup +s : select all +u : unselect all +l : display log +h : help +q : quit + +status window +------------- +total : total number of packages (filtered by guess filter and exclude list) +orphans : number of packages resolved as orphans +selected : number of selected orphans + +notes +----- +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. + +website +------- +http://rpmorphan.sourceforge.net +EOF + +############################################################################### +1; diff --git a/locale/fr_FR/rpmorphan_trans.pl b/locale/fr_FR/rpmorphan_trans.pl new file mode 100644 index 0000000..a520345 --- /dev/null +++ b/locale/fr_FR/rpmorphan_trans.pl @@ -0,0 +1,80 @@ +#!/usr/bin/perl +############################################################################### +# rpmorphan-curses-lib.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-curses-lib.pl 170 2010-01-14 12:13:48Z gerbier $ +# +# 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. +# +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +use vars qw( %TRANS ); + +$TRANS{'help'} = <<'EOF'; +rpmorphan vous aide a supprimer les packages inutiles : +il affiche les packages orphelins (ceux dont aucun autre package ne dépends) + +les bouttons disponibles sont : +------------------------------- +remove : pour lancer la suppression du/des packages selectionnes +info : affiche les informations sur le package courant +select all : selectionne tous les packages (dangereux) +unselect all : deselectionne tous les packages +log : affiche la log (liste des packages supprimes) +help : affiche cette aide +quit : fin du programme + +utilisation de la souris : +-------------------------- +- click gauche pour selectionner/deselectionner un package +- click droit pour afficher les informations sur le package (info) + +raccourcis clavier : +-------------------- +x/r : supprime le package +i : affiche les infos du package +S : affiche le sommaire du package dans un popup +s : selectionne tous les packages +u : deselectionne tous les packages +l : affiche la log +h : aide +q : quit + +informations de bas de page +--------------------------- +total : nombre de packages pris en compte +( filtrage par les options guess et les listes d'exclusions) +orphans : nombre de packages "orphelins" +selected : nombre de packages selectionnes + +notes +----- +Ce programme est libre, vous pouvez le redistribuer et/ou le modifier selon les +termes de la Licence Publique Générale GNU publiée par la Free Software Foundation +(version 2 ou bien toute autre version ultérieure choisie par vous). + +Ce programme est distribué car potentiellement utile, mais SANS AUCUNE GARANTIE, +ni explicite ni implicite, y compris les garanties de commercialisation ou d'adaptation +dans un but spécifique. Reportez-vous à la Licence Publique Générale GNU pour plus de détails. + +website +------- +http://rpmorphan.sourceforge.net +EOF + +############################################################################### +1; diff --git a/rpmdep.1 b/rpmdep.1 new file mode 100644 index 0000000..720dae3 --- /dev/null +++ b/rpmdep.1 @@ -0,0 +1,250 @@ +.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "RPMDEP 1" +.TH RPMDEP 1 "2012-08-01" "perl v5.14.2" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +rpmdep \- display the full dependency of an installed rpm package +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +rpmdep search recursively for package dependencies. +It resolvs all dependencies to package names. +It can also prepare a file to build a graph of dependencies, with graphviz. +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +rpmdep.pl [options] package +.PP +options: +.PP +.Vb 5 +\& \-help brief help message +\& \-man full documentation +\& \-V, \-\-version print version +\& \-use\-cache use cache file instead rpm query +\& \-clear\-cache clear cache file +\& +\& \-verbose verbose +\& \-dot dotfile build a dot file for graphviz +.Ve +.SH "REQUIRED ARGUMENTS" +.IX Header "REQUIRED ARGUMENTS" +the package to resolv +.SH "OPTIONS" +.IX Header "OPTIONS" +.IP "\fB\-help\fR" 8 +.IX Item "-help" +Print a brief help message and exits. +.IP "\fB\-man\fR" 8 +.IX Item "-man" +Print the manual page and exits. +.IP "\fB\-version\fR" 8 +.IX Item "-version" +Print the program release and exit. +.IP "\fB\-verbose\fR" 8 +.IX Item "-verbose" +The program works and print debugging messages. +.IP "\fB\-dot\fR" 8 +.IX Item "-dot" +create a dot file to be used by graphviz +.IP "\fB\-use\-cache\fR" 8 +.IX Item "-use-cache" +the rpm query may be long (10 to 30 s). If you will run an rpmorphan tool +several time, this option will allow to gain a lot of time : +it save the rpm query on a file cache (first call), then +use this cache instead quering rpm (others calls). +.IP "\fB\-clear\-cache\fR" 8 +.IX Item "-clear-cache" +to remove cache file. Can be used with \-use\-cache to write +a new cache. +.SH "USAGE" +.IX Header "USAGE" +rpmdep.pl \-\-use\-cache \-dot bash.dot bash +.PP +dot \-Tps bash.dot \-o bash.ps +.SH "FILES" +.IX Header "FILES" +the program can use the /tmp/rpmorphan.cache file +.SH "DIAGNOSTICS" +.IX Header "DIAGNOSTICS" +the verbose mode allow to see all the recursive work +.PP +others messages are +.IP "\fBpackage ... is not installed\fR" 8 +.IX Item "package ... is not installed" +this is not a name of an installed package +.IP "\fB... has no dependencies\fR" 8 +.IX Item "... has no dependencies" +this can comes for some admin tools (ash for example) +.IP "\fB... depends upon (list)\fR" 8 +.IX Item "... depends upon (list)" +return a list of sorted and comma separated packages +.SH "EXIT STATUS" +.IX Header "EXIT STATUS" +O if all is ok +.PP +>=1 if a problem +.SH "CONFIGURATION" +.IX Header "CONFIGURATION" +nothing +.SH "DEPENDENCIES" +.IX Header "DEPENDENCIES" +you should use graphviz to build graph from +.SH "INCOMPATIBILITIES" +.IX Header "INCOMPATIBILITIES" +not known +.SH "BUGS AND LIMITATIONS" +.IX Header "BUGS AND LIMITATIONS" +the program does not work well on program installed +with several versions +.SH "NOTES" +.IX Header "NOTES" +this program can be used as \*(L"normal\*(R" user +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmorphan\fR\|(1) +.PP +\fIrpmusage\fR\|(1) +.PP +\fIrpmduplicates\fR\|(1) +.PP +\fIrpmextra\fR\|(1) +.SH "LICENSE AND COPYRIGHT" +.IX Header "LICENSE AND COPYRIGHT" +Copyright (C) 2008 by Eric Gerbier +This program is free software; you can redistribute it and/or modify +it under the terms of the \s-1GNU\s0 General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +.SH "AUTHOR" +.IX Header "AUTHOR" +Eric Gerbier +.PP +you can report any bug or suggest to gerbier@users.sourceforge.net diff --git a/rpmdep.pl b/rpmdep.pl new file mode 100755 index 0000000..6991651 --- /dev/null +++ b/rpmdep.pl @@ -0,0 +1,414 @@ +#!/usr/bin/perl +############################################################################### +# rpmdep.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-1.11 | rpmdep.pl | Mon Jul 4 12:46:50 2011 +0000 | gerbier $ +# +# 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. +# +############################################################################### +# this program search the full dependency of an installed rpm package +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +use Getopt::Long; # arg analysis +use Pod::Usage; # man page +use Data::Dumper; # debug + +# library +use File::Basename; +my $dirname = dirname($PROGRAM_NAME); +require $dirname . '/rpmorphan-lib.pl' + or die "problem to load rpmorphan-lib: $ERRNO\n"; + +# the code should use no "print" calls +# but instead debug, warning, info calls +######################################################### +# is to be defined because rpmorphan-lib need it +sub display_status($) { + debug( shift @_ ); + return; +} +########################################################## +# resolv problems with characters in names for graphwiz +sub norm($) { + my $name = shift @_; + + my $norm_name = $name; + $norm_name =~ s/-/_/g; + $norm_name =~ s/\./_/g; + $norm_name =~ s/\+//g; + + return $norm_name; +} +######################################################### +# search the dependency of $name +# no prototype because recursive +sub solve { + my $name = shift @_; # package name + my $rh_provides = shift @_; # general provide hash + my $rh_depends = shift @_; # general dependencies hash + my $rh_files = shift @_; # general dependencies hash + my $rh_res = shift @_; # result cache + my $fh_dot = shift @_; # flag for dot + my $level = shift @_; # recurse level + + $level++; + + my $state = 0; # state ( 0 for ok) + + foreach my $dep ( keys %{ $rh_depends->{$name} } ) { + my $debug = "(solve $level) $name -> $dep "; + + # dep may be a package or a file or a virtual + my $pac; # searched package + + # analyse and resolv dependency + if ( exists $rh_files->{$dep} ) { + debug("$dep is a package"); + $pac = $dep; + } + elsif ( $dep =~ m/^rpmlib/ ) { + + # strange case, but real, for example : + # rpmlib(PayloadFilesHavePrefix) + # rpmlib(CompressedFileNames) + debug( $debug . ' (skip rpmlib)' ); + next; + } + elsif ( exists $rh_provides->{$dep} ) { + debug("$dep is a file"); + $pac = $rh_provides->{$dep}; + $debug .= " -> $pac "; + + #debug($debug); + } + else { + warning("can not find who provide $dep"); + $state++; + next; + } + + # filter dependency + if ( exists $rh_res->{$pac} ) { + + # already found + debug( $debug . ' (already found)' ); + } + elsif ( $pac eq $name ) { + + # no dependency on self + debug( $debug . ' (not on self)' ); + } + else { + if ($fh_dot) { + print {$fh_dot} norm($name) . ' -> ' . norm($pac) . ";\n"; + } + $rh_res->{$pac} = 1; + debug($debug); + my $sub_state = + solve( $pac, $rh_provides, $rh_depends, $rh_files, $rh_res, + $fh_dot, $level ); + $state += $sub_state; + } + + } # foreach + return $state; +} +######################################################### +# +# main +# +######################################################### +my $version = '0.5'; + +my $opt_help; +my $opt_man; +my $opt_version; +my $opt_use_cache; +my $opt_clear_cache; +my $opt_verbose; + +my $opt_dot; + +my %opt = ( + 'help' => \$opt_help, + 'man' => \$opt_man, + 'verbose' => \$opt_verbose, + 'version' => \$opt_version, + 'dot' => \$opt_dot, + 'use-cache' => \$opt_use_cache, + 'clear-cache' => \$opt_clear_cache, +); + +Getopt::Long::Configure('no_ignore_case'); +GetOptions( \%opt, 'help|?', 'man', 'verbose', 'version|V', 'dot=s', + 'use-cache!', 'clear-cache' ) + or pod2usage(2); + +init_debug($opt_verbose); + +if ($opt_help) { + pod2usage(1); +} +elsif ($opt_man) { + pod2usage( -verbose => 2 ); +} +elsif ($opt_version) { + print_version($version); + exit; +} + +# test if a target is set +if ( $#ARGV != 0 ) { + pod2usage('need a target : package name'); +} +my $package = $ARGV[0]; + +# hash structures to be filled with rpm query +my %files; +my %install_time; +my %provides; +my %depends; +my %virtual; +my %requires; + +read_rpm_data( \%opt, \%provides, \%install_time, \%files, \%depends, \%virtual, + \%requires ); + +if ( !exists $install_time{$package} ) { + warning("package $package is not installed"); + exit 1; +} + +# first we have to change order used %depends +my %depends_from; +foreach my $key1 ( keys %depends ) { + foreach my $key2 ( keys %{ $depends{$key1} } ) { + + # ex key2 = afick-gui key1 = afick + $depends_from{$key2}{$key1} = 1; + } +} + +# same for provides +my %is_provided_by; +foreach my $key1 ( keys %provides ) { + foreach my $key2 ( @{ $provides{$key1} } ) { + + # key2 is provided by $key1 package + $is_provided_by{$key2} = $key1; + } +} + +# dot graph ? +my $fh_dot; +if ($opt_dot) { + + # open output file + ## no critic (RequireBriefOpen) + if ( open $fh_dot, '>', $opt_dot ) { + print {$fh_dot} "digraph \"rpmdep\" {\n"; + } + else { + warning("can not write to $opt_dot file : $ERRNO\n"); + $opt_dot = 0; + } +} + +debug('2 : solve'); +my %res; +my $state = + solve( $package, \%is_provided_by, \%depends_from, \%files, \%res, $fh_dot, + 0 ); + +my $res = join q{,}, sort keys %res; +if ($res) { + info("$package depends upon $res"); +} +else { + info("$package has no dependencies"); +} + +if ($opt_dot) { + print {$fh_dot} "}\n"; + close $fh_dot or die "problem to close dot file :$OS_ERROR\n"; +} + +exit $state; +__END__ + +=head1 NAME + +rpmdep - display the full dependency of an installed rpm package + +=head1 DESCRIPTION + +rpmdep search recursively for package dependencies. +It resolvs all dependencies to package names. +It can also prepare a file to build a graph of dependencies, with graphviz. + +=head1 SYNOPSIS + +rpmdep.pl [options] package + +options: + + -help brief help message + -man full documentation + -V, --version print version + -use-cache use cache file instead rpm query + -clear-cache clear cache file + + -verbose verbose + -dot dotfile build a dot file for graphviz + +=head1 REQUIRED ARGUMENTS + +the package to resolv + +=head1 OPTIONS + +=over 8 + +=item B<-help> + +Print a brief help message and exits. + +=item B<-man> + +Print the manual page and exits. + +=item B<-version> + +Print the program release and exit. + +=item B<-verbose> + +The program works and print debugging messages. + +=item B<-dot> + +create a dot file to be used by graphviz + +=item B<-use-cache> + +the rpm query may be long (10 to 30 s). If you will run an rpmorphan tool +several time, this option will allow to gain a lot of time : +it save the rpm query on a file cache (first call), then +use this cache instead quering rpm (others calls). + +=item B<-clear-cache> + +to remove cache file. Can be used with -use-cache to write +a new cache. + +=back + +=head1 USAGE + +rpmdep.pl --use-cache -dot bash.dot bash + +dot -Tps bash.dot -o bash.ps + +=head1 FILES + +the program can use the /tmp/rpmorphan.cache file + +=head1 DIAGNOSTICS + +the verbose mode allow to see all the recursive work + +others messages are + +=over 8 + +=item B<package ... is not installed> + +this is not a name of an installed package + +=item B<... has no dependencies> + +this can comes for some admin tools (ash for example) + +=item B<... depends upon (list)> + +return a list of sorted and comma separated packages + +=back + +=head1 EXIT STATUS + +O if all is ok + +>=1 if a problem + +=head1 CONFIGURATION + +nothing + +=head1 DEPENDENCIES + +you should use graphviz to build graph from + +=head1 INCOMPATIBILITIES + +not known + +=head1 BUGS AND LIMITATIONS + +the program does not work well on program installed +with several versions + +=head1 NOTES + +this program can be used as "normal" user + +=head1 SEE ALSO + +=for man +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmorphan\fR\|(1) +.PP +\fIrpmusage\fR\|(1) +.PP +\fIrpmduplicates\fR\|(1) +.PP +\fIrpmextra\fR\|(1) + +=for html +<a href="rpmorphan.1.html">rpmorphan(1)</a><br /> +<a href="rpmusage.1.html">rpmusage(1)</a><br /> +<a href="rpmduplicates.1.html">rpmduplicates(1)</a><br /> +<a href="rpmextra.1.html">rpmextra(1)</a><br /> + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2008 by Eric Gerbier +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. + +=head1 AUTHOR + +Eric Gerbier + +you can report any bug or suggest to gerbier@users.sourceforge.net + +=cut + diff --git a/rpmduplicates.1 b/rpmduplicates.1 new file mode 100644 index 0000000..9c821fc --- /dev/null +++ b/rpmduplicates.1 @@ -0,0 +1,229 @@ +.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "RPMDUPLICATES 1" +.TH RPMDUPLICATES 1 "2012-08-01" "perl v5.14.2" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +rpmduplicates \- find program with several version installed +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +sometimes, the upgrade of a system will install new packages, and \*(L"forget\*(R" to remove +the older version. Rpmorphan do not work with this kind of unused packages. Rpmduplicates +is the answer. I recommend to run it after each distribution upgrade +(for example from fedora core 7 to 8). +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +rpmduplicates.pl [options] +.PP +options: +.PP +.Vb 3 +\& \-help brief help message +\& \-man full documentation +\& \-V, \-\-version print version +\& +\& \-verbose verbose +.Ve +.SH "REQUIRED ARGUMENTS" +.IX Header "REQUIRED ARGUMENTS" +nothing +.SH "OPTIONS" +.IX Header "OPTIONS" +.IP "\fB\-help\fR" 8 +.IX Item "-help" +Print a brief help message and exits. +.IP "\fB\-man\fR" 8 +.IX Item "-man" +Print the full manual page and exits. +.IP "\fB\-version\fR" 8 +.IX Item "-version" +Print the program release and exit. +.IP "\fB\-verbose\fR" 8 +.IX Item "-verbose" +The program works and print debugging messages. +.SH "USAGE" +.IX Header "USAGE" +rpmduplicates.pl +.SH "FILES" +.IX Header "FILES" +nothing +.SH "DIAGNOSTICS" +.IX Header "DIAGNOSTICS" +it will show the duplicates with their version and build date +.PP +and suggest to remove the older one +.PP +example : +.PP +duplicate kernel-devel : 2.6.24.4\-64.fc8 (Sat Mar 29 15:36:41 2008) / 2.6.24.3\-50.fc8 (Thu Mar 20 20:43:47 2008) +.PP +suggest : rpm \-e kernel\-devel\-2.6.24.3\-50.fc8 +.SH "EXIT STATUS" +.IX Header "EXIT STATUS" +the number of duplicates packages found +.SH "CONFIGURATION" +.IX Header "CONFIGURATION" +nothing +.SH "DEPENDENCIES" +.IX Header "DEPENDENCIES" +rpmorphan library +.SH "INCOMPATIBILITIES" +.IX Header "INCOMPATIBILITIES" +not known +.SH "BUGS AND LIMITATIONS" +.IX Header "BUGS AND LIMITATIONS" +not known +.SH "NOTES" +.IX Header "NOTES" +this program can be used as \*(L"normal\*(R" user +.PP +the gpg-pubkey packages are excluded from the result +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmorphan\fR\|(1) +.PP +\fIrpmusage\fR\|(1) +.PP +\fIrpmdep\fR\|(1) +.PP +\fIrpmextra\fR\|(1) +.SH "LICENSE AND COPYRIGHT" +.IX Header "LICENSE AND COPYRIGHT" +Copyright (C) 2008 by Eric Gerbier +This program is free software; you can redistribute it and/or modify +it under the terms of the \s-1GNU\s0 General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +.SH "AUTHOR" +.IX Header "AUTHOR" +Eric Gerbier +.PP +you can report any bug or suggest to gerbier@users.sourceforge.net diff --git a/rpmduplicates.pl b/rpmduplicates.pl new file mode 100755 index 0000000..840575e --- /dev/null +++ b/rpmduplicates.pl @@ -0,0 +1,266 @@ +#!/usr/bin/perl +############################################################################### +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-1.11 | rpmduplicates.pl | Mon Jul 4 12:47:55 2011 +0000 | gerbier $ +# +# 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. +# +############################################################################### +# search for packages with several versions installed +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +use Getopt::Long; # arg analysis +use Pod::Usage; # man page +use Data::Dumper; # debug + +# library +use File::Basename; +my $dirname = dirname($PROGRAM_NAME); + +# not very usefull, but warning, debug ... +require $dirname . '/rpmorphan-lib.pl' + or die "problem to load rpmorphan-lib: $ERRNO\n"; + +my $VERSION = '0.2'; + +my $opt_help; +my $opt_man; +my $opt_version; +my $opt_verbose; + +my %opt = ( + 'help' => \$opt_help, + 'man' => \$opt_man, + 'verbose' => \$opt_verbose, + 'version' => \$opt_version, +); + +Getopt::Long::Configure('no_ignore_case'); +GetOptions( \%opt, 'help|?', 'man', 'verbose', 'version|V' ) + or pod2usage(2); + +init_debug($opt_verbose); + +if ($opt_help) { + pod2usage(1); +} +elsif ($opt_man) { + pod2usage( -verbose => 2 ); +} +elsif ($opt_version) { + print_version($VERSION); + exit; +} + +# we cannot use common cache as duplicate package are not clean taken +# by the common code +## no critic (RequireInterpolationOfMetachars) +my $cmd = + 'rpm -qa --queryformat "%{NAME} %{VERSION}-%{RELEASE} %{BUILDTIME}\n" '; +## use critic + +my $fh; +## no critic (ProhibitTwoArgOpen,RequireBriefOpen) +if ( !open $fh, "$cmd |" ) { + die "can not open $cmd : $ERRNO\n"; +} +## use critic + +my %h_ver; # versions +my %h_buildtime; # buildtime + +my $nb = 0; +my $SPACE = q{ }; + +while ( my $line = <$fh> ) { + chomp $line; + my ( $soft, $version, $build ) = split $SPACE, $line; + + next if ( $soft eq 'gpg-pubkey' ); + + if ( exists $h_ver{$soft} ) { + + # we already have found a package with same name + my $old_ver = $h_ver{$soft}; + my $old_date = $h_buildtime{$soft}; + + my $old; + if ( $old_date < $build ) { + + # current package was build after the stored one + # keep the latest + $h_buildtime{$soft} = $build; + + # delete the older + $old = $old_ver; + } + else { + + # current package was build before the stored one + # delete the older + $old = $version; + } + $old_date = localtime $old_date; + $build = localtime $build; + print "duplicate $soft : $old_ver ($old_date) / $version ($build)\n"; + print "suggest : rpm -e $soft-$old\n"; + $nb++; + } + else { + $h_ver{$soft} = $version; + $h_buildtime{$soft} = $build; + } +} +close $fh or warning("can not close $cmd : $ERRNO"); + +if ( $nb == 0 ) { + print "no duplicate found !\n"; +} + +exit $nb; +__END__ + +=head1 NAME + +rpmduplicates - find program with several version installed + +=head1 DESCRIPTION + +sometimes, the upgrade of a system will install new packages, and "forget" to remove +the older version. Rpmorphan do not work with this kind of unused packages. Rpmduplicates +is the answer. I recommend to run it after each distribution upgrade +(for example from fedora core 7 to 8). + +=head1 SYNOPSIS + +rpmduplicates.pl [options] + +options: + + -help brief help message + -man full documentation + -V, --version print version + + -verbose verbose + +=head1 REQUIRED ARGUMENTS + +nothing + +=head1 OPTIONS + +=over 8 + +=item B<-help> + +Print a brief help message and exits. + +=item B<-man> + +Print the full manual page and exits. + +=item B<-version> + +Print the program release and exit. + +=item B<-verbose> + +The program works and print debugging messages. + +=back + +=head1 USAGE + +rpmduplicates.pl + +=head1 FILES + +nothing + +=head1 DIAGNOSTICS + +it will show the duplicates with their version and build date + +and suggest to remove the older one + +example : + +duplicate kernel-devel : 2.6.24.4-64.fc8 (Sat Mar 29 15:36:41 2008) / 2.6.24.3-50.fc8 (Thu Mar 20 20:43:47 2008) + +suggest : rpm -e kernel-devel-2.6.24.3-50.fc8 + +=head1 EXIT STATUS + +the number of duplicates packages found + +=head1 CONFIGURATION + +nothing + +=head1 DEPENDENCIES + +rpmorphan library + +=head1 INCOMPATIBILITIES + +not known + +=head1 BUGS AND LIMITATIONS + +not known + +=head1 NOTES + +this program can be used as "normal" user + +the gpg-pubkey packages are excluded from the result + +=head1 SEE ALSO + +=for man +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmorphan\fR\|(1) +.PP +\fIrpmusage\fR\|(1) +.PP +\fIrpmdep\fR\|(1) +.PP +\fIrpmextra\fR\|(1) + +=for html +<a href="rpmorphan.1.html">rpmorphan(1)</a><br /> +<a href="rpmusage.1.html">rpmusage(1)</a><br /> +<a href="rpmdep.1.html">rpmdep(1)</a><br /> +<a href="rpmextra.1.html">rpmextra(1)</a><br /> + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2008 by Eric Gerbier +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. + +=head1 AUTHOR + +Eric Gerbier + +you can report any bug or suggest to gerbier@users.sourceforge.net + +=cut + diff --git a/rpmextra.1 b/rpmextra.1 new file mode 100644 index 0000000..d79bf5c --- /dev/null +++ b/rpmextra.1 @@ -0,0 +1,222 @@ +.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "RPMEXTRA 1" +.TH RPMEXTRA 1 "2012-08-01" "perl v5.14.2" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +rpmextra \- display extra packages : not from distribution repositories +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +rpmextra search for extra packages : +packages installed on the system that are not available +in any repository listed in the config file +.PP +this packages are often installed manually and are not +updated as others packages by distribution tools +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +rpmextra [options] +.PP +options: +.PP +.Vb 4 +\& \-help brief help message +\& \-man full documentation +\& \-V, \-\-version print version +\& \-verbose verbose +.Ve +.SH "REQUIRED ARGUMENTS" +.IX Header "REQUIRED ARGUMENTS" +none +.SH "OPTIONS" +.IX Header "OPTIONS" +.IP "\fB\-help\fR" 8 +.IX Item "-help" +Print a brief help message and exits. +.IP "\fB\-man\fR" 8 +.IX Item "-man" +Print the manual page and exits. +.IP "\fB\-version\fR" 8 +.IX Item "-version" +Print the program release and exit. +.IP "\fB\-verbose\fR" 8 +.IX Item "-verbose" +The program works and print debugging messages. +.SH "USAGE" +.IX Header "USAGE" +rpmextra +.SH "DIAGNOSTICS" +.IX Header "DIAGNOSTICS" +the verbose mode allow to see which +functions are used +.SH "EXIT STATUS" +.IX Header "EXIT STATUS" +O if all is ok +.PP +>=1 if a problem +.SH "CONFIGURATION" +.IX Header "CONFIGURATION" +nothing +.SH "DEPENDENCIES" +.IX Header "DEPENDENCIES" +rpmextra depends upon rpm package managers. +it can use yum (Redhat), urpmi (Mandriva), zypper (Suse) tools +.SH "INCOMPATIBILITIES" +.IX Header "INCOMPATIBILITIES" +not known +.SH "BUGS AND LIMITATIONS" +.IX Header "BUGS AND LIMITATIONS" +the program does not work well on program installed +with several versions +.SH "NOTES" +.IX Header "NOTES" +this program can be used as \*(L"normal\*(R" user +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmorphan\fR\|(1) +.PP +\fIrpmusage\fR\|(1) +.PP +\fIrpmduplicates\fR\|(1) +.PP +\fIrpmdep\fR\|(1) +.SH "LICENSE AND COPYRIGHT" +.IX Header "LICENSE AND COPYRIGHT" +Copyright (C) 2008 by Eric Gerbier +This program is free software; you can redistribute it and/or modify +it under the terms of the \s-1GNU\s0 General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +.SH "AUTHOR" +.IX Header "AUTHOR" +Eric Gerbier +.PP +you can report any bug or suggest to gerbier@users.sourceforge.net diff --git a/rpmextra.pl b/rpmextra.pl new file mode 100755 index 0000000..d9d4c51 --- /dev/null +++ b/rpmextra.pl @@ -0,0 +1,425 @@ +#!/usr/bin/perl +############################################################################### +# rpmextra.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-1.11 | rpmextra.pl | Mon Jul 4 12:49:43 2011 +0000 | gerbier $ +# +# 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. +# +############################################################################### +# this program search extra packages +# same as : yum list extra +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +use Getopt::Long; # arg analysis +use Pod::Usage; # man page +use Data::Dumper; # debug + +# library +use File::Basename; +my $dirname = dirname($PROGRAM_NAME); +require $dirname . '/rpmorphan-lib.pl' + or die "problem to load rpmorphan-lib: $ERRNO\n"; + +# the code should use no "print" calls +# but instead debug, warning, info calls +######################################################### +# is to be defined because rpmorphan-lib need it +sub display_status($) { + debug( shift @_ ); + return; +} +########################################################## +# search for a command in PATH variable +sub search_in_path($) { + my $prog = shift @_; + + foreach ( split /:/, $ENV{'PATH'} ) { + if ( -x "$_/$prog" ) { + return "$_/$prog"; + } + } + return; +} +########################################################## +# get list of installed packages +sub get_installed_rpm() { + + debug('get list of installed packages'); + ## no critic (ProhibitBacktickOperators) + my @list = `/bin/rpm -qa`; + chomp @list; + my @sorted_list = sort @list; + return @sorted_list; +} +######################################################### +# for distribution using package-cleanup +# ex : redhat/centos +sub get_extra_cleanup($) { + my $cleanup_path = shift @_; # path to package-cleanup + + debug('using package-cleanup'); + + my $cmd = "$cleanup_path -q --orphans"; + + # output look like : + # kernel-2.6.35.12-90.fc14.i686 + # rpmorphan-1.8-1.noarch + + my %hash; + ## no critic (ProhibitTwoArgOpen) + if ( open my $fh, "$cmd |" ) { + my $nr = 0; + while ( my $line = <$fh> ) { + chomp $line; + + # skip first lines + if ( $nr > 1 ) { + $hash{$line} = 1; + } + $nr++; + } + close $fh or warning("problem on close $cmd : $ERRNO"); + } + else { + warning("can not read $cmd output"); + exit; + } + + foreach my $pac ( sort keys %hash ) { + print "$pac\n"; + } + return; +} +######################################################### +# for distribution using yum +# ex : redhat/centos/fedora +sub get_extra_yum($) { + my $yum_path = shift @_; # path to yum + + debug('using yum'); + my $cmd = "$yum_path -q list extras 2>/dev/null"; + + # output look like : + # zynaddsubfx.x86_64 2.4.0-2.fc12 fedora + # zynjacku.x86_64 5-4.fc12 updates + + my %hash; + ## no critic (ProhibitTwoArgOpen) + if ( open my $fh, "$cmd |" ) { + my $nr = 0; + while ( my $line = <$fh> ) { + chomp $line; + + if ( $nr > 0 ) { + + # line follow the format : name version repository + # but if version is too long, line may be splitted in 2 lines !!! + ## no critic (ProhibitEmptyQuotes) + my ( $name, $version, undef ) = split ' ', $line; + if ( !$version ) { + print "problem line $line\n"; + } + else { + $name =~ s/\..*//; # remove arch + $name .= $version; # add version + $hash{$name} = 1; + } + } + $nr++; + } + close $fh or warning("problem on close $cmd : $ERRNO"); + } + else { + warning("can not read $cmd output"); + exit; + } + + foreach my $pac ( sort keys %hash ) { + print "$pac\n"; + } + return; +} +######################################################### +# for distribution using urpmq +# ex : Mandriva +sub get_extra_urpmq($) { + my $urpmq_path = shift @_; # path to urpmq + + # maybe use urpm perl module ? + debug('using urpmq'); + my $cmd = "$urpmq_path --list -r"; + + # output look like : + # zziplib0-devel-0.13.58-1mdv2010.1 + # zzuf-0.13-1mdv2010.1 + + # get list of all available packages + my %available; + debug('get list of packages in repository'); + ## no critic (ProhibitTwoArgOpen) + if ( open my $fh, "$cmd |" ) { + while ( my $line = <$fh> ) { + chomp $line; + $available{$line} = 1; + } + close $fh or warning("problem on close $cmd : $ERRNO"); + } + else { + warning("can not read $cmd output"); + exit; + } + + my @installed = get_installed_rpm(); + + foreach my $pac (@installed) { + if ( !exists $available{$pac} ) { + print "$pac\n"; + } + } + return; +} +######################################################### +# for distribution using zypper +# ex : Suse +sub get_extra_zypper($) { + my $zypper_path = shift @_; + + debug('using zypper'); + + my $cmd = "$zypper_path pa"; + + # output look like : + # i | openSUSE-11.4 OSS | ConsoleKit | 0.4.3-6.1 | i586 + # | openSUSE-11.4 OSS | ConsoleKit-devel | 0.4.3-6.1 | i586 + + # get list of all available packages + debug('get list of packages in repository'); + my %available; + ## no critic (ProhibitTwoArgOpen) + if ( open my $fh, "$cmd |" ) { + while ( my $line = <$fh> ) { + chomp $line; + next unless ( $line =~ m/\|/ ); # skip first lines + $line =~ s/\s+//g; # remove spaces + + my @tab = split /\|/, $line; + ## no critic(ProhibitMagicNumbers) + my $pac = $tab[2] . q{-} . $tab[3] . q{.} . $tab[4]; + $available{$pac} = 1; + } + close $fh or warning("problem on close $cmd : $ERRNO"); + } + else { + warning("can not read $cmd output"); + exit; + } + + my @installed = get_installed_rpm(); + + foreach my $pac (@installed) { + if ( !exists $available{$pac} ) { + print "$pac\n"; + } + } + + return; +} +######################################################### +# +# main +# +######################################################### +my $version = '0.2'; + +my $opt_help; +my $opt_man; +my $opt_version; +my $opt_verbose; + +my %opt = ( + 'help' => \$opt_help, + 'man' => \$opt_man, + 'verbose' => \$opt_verbose, + 'version|V' => \$opt_version, +); + +Getopt::Long::Configure('no_ignore_case'); +GetOptions( \%opt, 'help|?', 'man', 'verbose', 'version|V', ) + or pod2usage(2); + +init_debug($opt_verbose); + +if ($opt_help) { + pod2usage(1); +} +elsif ($opt_man) { + pod2usage( -verbose => 2 ); +} +elsif ($opt_version) { + print_version($version); + exit; +} + +my $yum_util_path = search_in_path('package-cleanup'); +my $yum_path = search_in_path('yum'); +my $urpmq_path = search_in_path('urpmq'); +my $zypper_path = search_in_path('zypper'); + +## no critic (ProhibitCascadingIfElse) +if ($yum_util_path) { + get_extra_cleanup($yum_util_path); +} +elsif ($yum_path) { + get_extra_yum($yum_path); +} +elsif ($urpmq_path) { + get_extra_urpmq($urpmq_path); +} +elsif ($zypper_path) { + get_extra_zypper($zypper_path); +} +else { + warning('unknown package tool'); +} +## use critic + +exit; +__END__ + +=head1 NAME + +rpmextra - display extra packages : not from distribution repositories + +=head1 DESCRIPTION + +rpmextra search for extra packages : +packages installed on the system that are not available +in any repository listed in the config file + +this packages are often installed manually and are not +updated as others packages by distribution tools + +=head1 SYNOPSIS + +rpmextra [options] + +options: + + -help brief help message + -man full documentation + -V, --version print version + -verbose verbose + +=head1 REQUIRED ARGUMENTS + +none + +=head1 OPTIONS + +=over 8 + +=item B<-help> + +Print a brief help message and exits. + +=item B<-man> + +Print the manual page and exits. + +=item B<-version> + +Print the program release and exit. + +=item B<-verbose> + +The program works and print debugging messages. + +=back + +=head1 USAGE + +rpmextra + +=head1 DIAGNOSTICS + +the verbose mode allow to see which +functions are used + +=head1 EXIT STATUS + +O if all is ok + +>=1 if a problem + +=head1 CONFIGURATION + +nothing + +=head1 DEPENDENCIES + +rpmextra depends upon rpm package managers. +it can use yum (Redhat), urpmi (Mandriva), zypper (Suse) tools + +=head1 INCOMPATIBILITIES + +not known + +=head1 BUGS AND LIMITATIONS + +the program does not work well on program installed +with several versions + +=head1 NOTES + +this program can be used as "normal" user + +=head1 SEE ALSO + +=for man +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmorphan\fR\|(1) +.PP +\fIrpmusage\fR\|(1) +.PP +\fIrpmduplicates\fR\|(1) +.PP +\fIrpmdep\fR\|(1) + +=for html +<a href="rpmorphan.1.html">rpmorphan(1)</a><br /> +<a href="rpmusage.1.html">rpmusage(1)</a><br /> +<a href="rpmduplicates.1.html">rpmduplicates(1)</a><br /> +<a href="rpmdep.1.html">rpmdep(1)</a><br /> + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2008 by Eric Gerbier +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. + +=head1 AUTHOR + +Eric Gerbier + +you can report any bug or suggest to gerbier@users.sourceforge.net + +=cut + diff --git a/rpmorphan-curses-lib.pl b/rpmorphan-curses-lib.pl new file mode 100644 index 0000000..90c218f --- /dev/null +++ b/rpmorphan-curses-lib.pl @@ -0,0 +1,501 @@ +#!/usr/bin/perl +############################################################################### +# rpmorphan-curses-lib.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-1.11 | rpmorphan-curses-lib.pl | Wed Jul 6 13:59:20 2011 +0000 | gerbier $ +# +# 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. +# +############################################################################### +# curses gui code +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +## no critic(ProhibitPackageVars) +use vars + qw( $W_list $Opt_dry_run @Liste_pac $Main_ui &display_total &insert_gui_orphans ); +## use critic + +#use Curses; # for KEY_ENTER +## no critic(RequireCheckingReturnValueOfEval) +eval { require Curses; }; +if ($EVAL_ERROR) { + die "can not find Curses package : $EVAL_ERROR\n"; +} +else { + import Curses; +} +## use critic + +#### global variable +my $flag_info = 0; # flag for info window (avoid several calls) + +######################################################### +# select all +sub curses_select_callback($) { + my $widget = shift; + + my $container = $widget->parent(); + my $listbox = $container->getobj('list_id'); + + my $pac_list = $W_list->values(); + my @pac2 = @{$pac_list}; + my @sel = ( 0 .. $#pac2 ); + $listbox->set_selection(@sel); + + # update + $listbox->draw(); + return; +} +######################################################### +# unselect all +sub curses_unselect_callback($) { + my $widget = shift; + + my $container = $widget->parent(); + my $listbox = $container->getobj('list_id'); + + $listbox->clear_selection(); + + # update + $listbox->draw(); + return; +} +######################################################### +# remove selected items +sub curses_remove_callback($) { + my $widget = shift; + + my $container = $widget->parent(); + my $listbox = $container->getobj('list_id'); + my @sel = $listbox->get(); # selected items + + my $old_pac_list = $W_list->values(); + + # a confirm dialog box + my $yes = $Main_ui->dialog( + -message => "confirm remove @sel ?", + -buttons => [ 'yes', 'no' ], + -values => [ 1, 0 ], + -title => 'Confirm', + ); + + if ($yes) { + remove_packages( \@sel, $old_pac_list ); + } + + # update + $listbox->draw(); + + return; +} +######################################################### +# display help window +sub curses_help_callback($;) { + my $this = shift @_; + + my $text = get_help_text(); + $this->root()->dialog( + -message => $text, + -buttons => ['ok'], + -title => 'help', + ); + return; +} +######################################################### +sub curses_log_callback($;) { + my $this = shift @_; + + my @l = get_log(); + + @l = ('empty') if ( !@l ); + my $l = join "\n", @l; + + $this->root()->dialog( + -message => $l, + -buttons => ['ok'], + -title => 'log', + ); + return; +} +######################################################### +# display rpm summary on the current item +sub curses_summary_callback($) { + my $widget = shift; + + my $listbox = $widget->parent()->getobj('list_id'); + my $current = $listbox->get_active_value(); + + curses_popup( get_package_summary($current) ); + return; +} +######################################################### +# display rpm info on the current item +sub curses_info_callback($) { + my $widget = shift; + + # a flag to avoid a fatal error + # only allow one call + if ($flag_info) { + return; + } + else { + $flag_info++; + } + + my $listbox = $widget->parent()->getobj('list_id'); + my $current = $listbox->get_active_value(); + + my $txt; + if ( defined $current ) { + my @res = get_package_info($current); + $txt = join "\n", @res; + } + else { + $txt = 'you should select a package'; + } + + # old version + # but the scrollbar does not work (not focusable) + # my $dialog = $listbox->root()->dialog( + # -message => $txt, + # -buttons => ['ok'], + # -title => 'info', + # -vscrollbar => 'left', + # ); + + # new version with TextViewer + my $win = $listbox->parent(); + my $textviewer = + $win->add( 'mytextviewer_id', 'TextViewer', -text => $txt, ); + + # any key will quit + $win->set_binding( \&curses_exit_text_callback, KEY_ENTER() ); + $textviewer->focus(); + return; +} +######################################################### +# called from reload button +sub curses_load_callback($) { + my $widget = shift; + + load_data(1); + return; +} +######################################################### +sub curses_exit_text_callback() { + + $flag_info--; + my $win = $Main_ui->getobj('win1_id'); + $win->delete('mytextviewer_id'); + $win->draw(); + return; +} +######################################################### +#sub curses_redraw_all() { +# +# #while (my ($id, $object) = each %{$Main_ui->{-id2object}}) { +# #curses_popup($id); +# #$object->draw(); +# #} +# my $win = $Main_ui->getobj('win1_id'); +# $Main_ui->draw(); +# $win->draw(); +# foreach my $id ('buttons_id', 'list_id', 'total_id', 'total_var_id','selected_id') { +# my $obj = $win->getobj($id); +# if ($obj ) { +# curses_popup("draw $id"); +# $obj->draw(); +# } +# } +# +# return; +#} +######################################################### +# is called when an item is selected/unselected +# and change a counter +sub curses_list_callback() { + my $widget = shift; + my $listbox = $widget->parent()->getobj('list_id'); + my @sel = $listbox->get(); # selected items + + my $nb = scalar @sel; + my $selected = $widget->parent()->getobj('selected_val_id'); + $selected->text($nb); + $selected->draw(); + + return; +} +######################################################### +# quit gui +sub curses_quit_callback($;) { + my $this = shift @_; + exit; +} +######################################################### +# display messages as a quick popup +# to be used as debuging tool for example +sub curses_popup($) { + my $message = shift @_; + + $Main_ui->status($message); + sleep 1; + $Main_ui->nostatus(); + + return; +} + +######################################################### +sub build_curses_gui() { + + # Create the root object. + $Main_ui = Curses::UI->new( + -clear_on_exit => 0, + -debug => 0, + ); + + # add a window + my $win1 = $Main_ui->add( + 'win1_id', + 'Window', + -title => build_title(), + -titlefullwidth => 1, + -border => 1, + ); + my $height = $win1->height(); + + # debug + #print "windows height : $height, width : " . $win1->width() . "\n"; + + ## no critic (ProhibitMagicNumbers) + my $margin = + 5; # 1 for top menu, 2 for status/selected and 2 for borders + ## use critic + my $min_height = 2 * $margin; + if ( $height < $min_height ) { + die "the window is too small ($height)\n"; + } + + my @buttons; + if ( is_remove_allowed($Opt_dry_run) ) { + + # I do not know how to disable the button, + # so I only add it in this mode + push @buttons, + ( + { + -label => 'Remove ', + -onpress => \&curses_remove_callback, + }, + ); + + # Bind <x> and <r> for remove + $Main_ui->set_binding( sub { curses_remove_callback($W_list) }, 'x' ); + $Main_ui->set_binding( sub { curses_remove_callback($W_list) }, 'r' ); + + } + push @buttons, + ( + { + -label => 'Info ', + -onpress => \&curses_info_callback, + }, + { + -label => 'Select_all ', + -onpress => \&curses_select_callback, + }, + { + -label => 'Unselect_all ', + -onpress => \&curses_unselect_callback, + }, + { + -label => 'Log ', + -onpress => \&curses_log_callback, + }, + { + -label => 'reLoad ', + -onpress => \&curses_load_callback, + }, + { + -label => 'Help ', + -onpress => \&curses_help_callback, + }, + { + -label => 'Quit', + -onpress => \&curses_quit_callback, + }, + ); + + $win1->add( + 'buttons_id', + 'Buttonbox', + -y => 0, + -buttons => \@buttons, + ); + + # listbox + my $list_height = $height - $margin; + $W_list = $win1->add( + 'list_id', 'Listbox', + -y => 1, + -width => 60, + -height => $list_height, + -border => 1, + -vscrollbar => 1, + -multi => 1, + -onselchange => \&curses_list_callback, + ); + + my $empty = q{}; + + # total + $win1->add( + 'total_id', + 'Label', + -text => 'total', + -x => 1, + -y => $list_height + 1, + ); + my $w_total = $win1->add( + 'total_val_id', + 'TextViewer', + -text => $empty, + -height => 1, + -width => 5, + -x => 7, + -y => $list_height + 1, + ); + + # orphans + $win1->add( + 'orphans_id', + 'Label', + -text => 'orphans', + -x => 12, + -y => $list_height + 1, + ); + my $w_orphans = $win1->add( + 'orphans_val_id', + 'TextViewer', + -text => $empty, + -height => 1, + -width => 5, + -x => 20, + -y => $list_height + 1, + ); + + # selected + $win1->add( + 'selected_id', + 'Label', + -text => 'selected', + -x => 25, + -y => $list_height + 1, + ); + my $w_selected = $win1->add( + 'selected_val_id', + 'TextViewer', + -text => $empty, + -height => 1, + -width => 4, + -x => 35, + -y => $list_height + 1, + ); + + # status + $win1->add( + 'status_id', + 'Label', + -text => 'status:', + -x => 1, + -y => $list_height + 2, + ); + my $w_status = $win1->add( + 'status_val_id', + 'TextViewer', + -text => $empty, + -height => 1, + -width => 50, + -x => 9, + -y => $list_height + 2, + ); + + # begin on list + $W_list->focus(); + + # Bind <i> for info + $Main_ui->set_binding( sub { curses_info_callback($W_list) }, 'i' ); + + # S for summary + $Main_ui->set_binding( sub { curses_summary_callback($W_list) }, 'S' ); + + # Bind <s> for select + $Main_ui->set_binding( sub { curses_select_callback($W_list) }, 's' ); + + # Bind <u> for unselect + $Main_ui->set_binding( sub { curses_unselect_callback($W_list) }, 'u' ); + + # Bind <l> for log + $Main_ui->set_binding( sub { curses_log_callback($W_list) }, 'l' ); + + # Bind <h> for help + $Main_ui->set_binding( sub { curses_help_callback($W_list) }, 'h' ); + + # Bind <L> for reload + $Main_ui->set_binding( sub { curses_load_callback($W_list) }, 'L' ); + + # Bind <q> to quit. + $Main_ui->set_binding( sub { exit }, 'q' ); + + # for debuging + #$Main_ui->set_binding( sub { dump_struct() }, 'd' ); + + # common gui subroutines with different coding + ############################################## + *insert_gui_orphans = sub { + $W_list->values(@_); + $W_list->draw(); + + # display orphans total + my $nb = scalar @_; + $w_orphans->text($nb); + $w_orphans->draw(); + + # reset selected + $w_selected->text(0); + $w_selected->draw(); + }; + + *display_status = sub { + my $text = shift @_; + debug($text); + + $w_status->text($text); + $w_status->draw(); + }; + + *display_total = sub { + + # display orphans total + my $nb = scalar @Liste_pac; + $w_total->text($nb); + $w_total->draw(); + }; + + return; +} +######################################################### +1; diff --git a/rpmorphan-lib.pl b/rpmorphan-lib.pl new file mode 100755 index 0000000..5eacbcc --- /dev/null +++ b/rpmorphan-lib.pl @@ -0,0 +1,792 @@ +#!/usr/bin/perl +############################################################################### +# rpmorphan-lib.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-1.11 | rpmorphan-lib.pl | Wed Aug 1 09:36:43 2012 +0200 | Eric Gerbier $ +# +# 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. +# +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +use Data::Dumper; # debug +use File::stat; # get_last_access + +## no critic (RequireCheckingReturnValueOfEval) + +# the code should use no "print" calls +# but use instead debug, warning, info calls +############################################################################### +sub nodebug { + return; +} +############################################################################### +sub backend_debug { + my $text = $_[0]; + print "debug $text\n"; + return; +} +############################################################################### +# change debug subroutine +# this way seems better than previous one as +# - it does not need any "global" verbose variable +# - it suppress any verbose test (quicker) +sub init_debug($) { + my $verbose = shift @_; + + # to avoid error messages + ## no critic ( NoWarnings ); + no warnings 'redefine'; + ## use critic; + + if ($verbose) { + *debug = \&backend_debug; + } + else { + *debug = \&nodebug; + } + + use warnings 'all'; + return; +} +############################################################################### +# used to print warning messages +sub warning($) { + my $text = shift @_; + warn "WARNING $text\n"; + return; +} +############################################################################### +# used to print normal messages +sub info($) { + my $text = shift @_; + print "$text\n"; + return; +} +############################################################################### +sub print_version($) { + my $version = shift @_; + info("$PROGRAM_NAME version $version"); + return; +} +######################################################### +# used to check on option +sub is_set($$) { + my $rh_opt = shift @_; # hash of program arguments + my $key = shift @_; # name of desired option + + #debug("is_set $key"); + my $r_value = $rh_opt->{$key}; + return ${$r_value}; +} +######################################################### +sub is_verbose($) { + my $rh_opt = shift @_; # hash of program arguments + + return is_set( $rh_opt, 'verbose' ); +} +######################################################### +# apply a filter on package list according program options +sub rpmfilter($$) { + my $rh_opt = shift @_; + my $rh_list_pac = shift @_; + + display_status('apply filters'); + + # we just want the list of keys + my @list = keys %{$rh_list_pac}; + + if ( is_set( $rh_opt, 'all' ) ) { + debug('all'); + return @list; + } + else { + debug('guess'); + + my @filtered_list; + if ( is_set( $rh_opt, 'guess-perl' ) ) { + debug('guess-perl'); + my @res = grep { /^perl/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-python' ) ) { + my @res = grep { /^python/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-pike' ) ) { + my @res = grep { /^pike/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-ruby' ) ) { + my @res = grep { /^ruby/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-common' ) ) { + my @res = grep { /-common$/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-data' ) ) { + my @res = grep { /-data$/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-doc' ) ) { + my @res = grep { /-doc$/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-dev' ) ) { + my @res = grep { /-devel$/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-lib' ) ) { + my @res = grep { /^lib/ } @list; + push @filtered_list, @res; + } + if ( is_set( $rh_opt, 'guess-custom' ) ) { + my $regex = ${ $rh_opt->{'guess-custom'} }; + my @res = grep { /$regex/ } @list; + push @filtered_list, @res; + } + return @filtered_list; + } +} +######################################################### +# difference between 2 date in unix format +# return in days +sub diff_date($$) { + my $now = shift @_; # current + my $time = shift @_; # + + # convert from seconds to days + ## no critic(ProhibitParensWithBuiltins,ProhibitMagicNumbers) + return int( ( $now - $time ) / 86_400 ); +} +######################################################### +# return the date (unix format) of last access on a package +# (scan all inodes for atime) and the name of the file +sub get_last_access($) { + my $ra_files = shift @_; # array of file names + + my $last_date = 0; # means a very old date for linux : 1970 + my $last_file = q{}; + FILE: foreach my $file ( @{$ra_files} ) { + next FILE unless ( -e $file ); + my $stat = stat $file; + my $atime = $stat->atime(); + + if ( $atime > $last_date ) { + $last_date = $atime; + $last_file = $file; + } + } + return ( $last_date, $last_file ); +} +######################################################### +# depending the cache option, return a command to execute +# - as an rpm query (piped) +# - as a cache file to open +sub init_cache($) { + my $rh_opt = shift @_; # hash of program arguments + + # the main idea is to reduce the number of call to rpm in order to gain time + # the ';' separator is used to separate fields + # the ',' separator is used to separate data in fields arrays + + # note : we do not ask for PROVIDEFLAGS PROVIDEVERSION + # REQUIREFLAGS REQUIREVERSION + + my $suggests = ( is_set( $rh_opt, 'suggests' ) ) ? '[%{SUGGESTS},]' : q{}; + + my $rpm_cmd = +"rpm -qa --queryformat '%{NAME};[%{REQUIRENAME},]$suggests;[%{PROVIDES},];[%{FILENAMES},];%{INSTALLTIME}\n' 2>/dev/null"; + my $cache_file = '/tmp/rpmorphan.cache'; + my $fh_cache; + my $cmd; + + if ( is_set( $rh_opt, 'clear-cache' ) ) { + unlink $cache_file if ( -f $cache_file ); + } + + if ( is_set( $rh_opt, 'use-cache' ) ) { + if ( -f $cache_file ) { + + # cache exists : use it + $cmd = $cache_file; + display_status("use cache file $cache_file"); + } + else { + + # use rpm command + $cmd = "$rpm_cmd |"; + + # and create cache file + ## no critic (RequireBriefOpen) + if ( open $fh_cache, '>', $cache_file ) { + display_status("create cache file $cache_file"); + } + else { + warning("can not create cache file $cache_file : $ERRNO"); + } + } + } + else { + + # output may be long, so we use a pipe to avoid to store a big array + $cmd = "$rpm_cmd |"; + display_status('read rpm data'); + + unlink $cache_file if ( -f $cache_file ); + } + + return ( $cmd, $fh_cache ); +} +######################################################### +## no critic (ProhibitManyArgs) +sub analyse_rpm_info($$$$$$$$$$) { + my $name = shift @_; # current package name + my $ra_prov = shift @_; # data from database on name + my $ra_req = shift @_; # idem + my $ra_files = shift @_; # idem + my $rh_objects = shift @_; + my $rh_provides = shift @_; + my $rh_files = shift @_; + my $rh_depends = shift @_; + my $rh_virtual = shift @_; + my $rh_requires = shift @_; + + ## use critic; + + # we do not use version in keys + # so we only keep the last seen data for a package name + if ( exists $rh_provides->{$name} ) { + debug("duplicate package $name"); + } + + VIRTUAL: foreach my $p ( @{$ra_prov}, @{$ra_files} ) { + + # do not add files + #next VIRTUAL if ( $p =~ m!^/! ); + + # some bad package may provide more than on time the same object + if ( ( exists $rh_objects->{$p} ) + && ( $rh_objects->{$p} ne $name ) ) + { + + # method 1 (old) + # we do not use who provide this virtual for now + #$rh_virtual->{$p} = 1; + + # method 2 (current) + # to improve the code, we can use virtual as a counter + # ( as a garbage collector ) + # so we can remove all providing except the last one + if ( !exists $rh_virtual->{$p} ) { + + # add previous data + $rh_virtual->{$p}{ $rh_objects->{$p} } = 1; + } + + # add new virtual + $rh_virtual->{$p}{$name} = 1; + } + else { + + # keep memory of seen "provided" + $rh_objects->{$p} = $name; + } + } + + # $name package provide @prov + # file are also included in "provide" + push @{ $rh_provides->{$name} }, @{$ra_prov}, @{$ra_files}; + + # list are necessary for access-time option + push @{ $rh_files->{$name} }, @{$ra_files}; + + # this will be helpfull when recursive remove package + push @{ $rh_requires->{$name} }, @{$ra_req}; + + # build a hash for dependencies + foreach my $require ( @{$ra_req} ) { + + # we have to suppress auto-depends (ex : ark package) + my $flag_auto = 0; + PROVIDE: foreach my $p ( @{$ra_prov} ) { + if ( $require eq $p ) { + $flag_auto = 1; + + #debug("skip auto-depency on $name"); + last PROVIDE; + } + } + + # $name depends from $require + # exemple : depends { 'afick' } { afick-gui } = 1 + # push @{ $rh_depends->{$require} }, $name unless $flag_auto; + # we use a hash to help when we have to delete data (on recursive remove) + $rh_depends->{$require}{$name} = 1 unless $flag_auto; + } + return; +} +######################################################### +# read rpm information about all installed packages +# can be from database, or from rpmorphan cache +sub read_rpm_data_base($$$$$$$) { + my $rh_opt = shift @_; # hash of program arguments + my $rh_provides = shift @_; + my $rh_install_time = shift @_; + my $rh_files = shift @_; + my $rh_depends = shift @_; + my $rh_virtual = shift @_; + my $rh_requires = shift @_; + + display_status('read rpm data using base RPM'); + my ( $cmd, $fh_cache ) = init_cache($rh_opt); + + # because we can open a pipe or a cache, it is not possible to use + # the 3 arg form of open + ## no critic (ProhibitTwoArgOpen,RequireBriefOpen); + my $fh; + if ( !open $fh, $cmd ) { + + # no critic; + die "can not open $cmd : $ERRNO\n"; + ## use critic + } + debug('1 : analysis'); + my %objects; + while (<$fh>) { + + # write cache + print {$fh_cache} $_ if ($fh_cache); + + my ( $name, $req, $prov, $files, $install_time ) = split /;/, $_; + + # install time are necessary for install-time option + $rh_install_time->{$name} = $install_time; + + # see rpm query format in init_cache + my $delim = q{,}; + my @prov = split /$delim/, $prov; + my @req = split /$delim/, $req; + my @files = split /$delim/, $files; + + analyse_rpm_info( + $name, \@prov, \@req, \@files, + \%objects, $rh_provides, $rh_files, $rh_depends, + $rh_virtual, $rh_requires + ); + + } + close $fh or warning("problem to close rpm command : $ERRNO"); + + # close cache if necessary + if ($fh_cache) { + close $fh_cache or warning("problem to close cache file : $ERRNO"); + } + return; +} +######################################################### +# read database info by use of RPM2 module +sub read_rpm_data_rpm2($$$$$$$) { + my $rh_opt = shift @_; # hash of program arguments + my $rh_provides = shift @_; + my $rh_install_time = shift @_; + my $rh_files = shift @_; + my $rh_depends = shift @_; + my $rh_virtual = shift @_; + my $rh_requires = shift @_; + + # cache ? + # acces to data by rpm2 should be fast, + # so do not need cache + # and init_cache works with a command to exec + # not with a perl module + + display_status('read rpm data using RPM2'); + import RPM2; + my $db = RPM2->open_rpm_db(); + + my $flag_suggests = is_set( $rh_opt, 'suggests' ); + + debug('1 : analysis'); + my %objects; + + my $i = $db->find_all_iter(); + while ( my $pkg = $i->next ) { + + my $name = $pkg->name; + my $install_time = $pkg->installtime; + $rh_install_time->{$name} = $install_time; + + my @req = $pkg->requires; + my @prov = $pkg->provides; + my @files = $pkg->files; + + if ($flag_suggests) { + my @suggests = $pkg->tag('SUGGESTS'); + + #debug("suggests for $name : " . join ' ', @suggests); + push @req, @suggests; + } + + analyse_rpm_info( + $name, \@prov, \@req, \@files, + \%objects, $rh_provides, $rh_files, $rh_depends, + $rh_virtual, $rh_requires + ); + } + return; +} +######################################################### +# URPM return dependencies as perl[ = 5.8] +# we have to suppress the version and only keep the package +sub clean_rel($) { + my $ra = shift @_; + + foreach my $elem ( @{$ra} ) { + $elem =~ s/\[.*\]//; + } + return; +} +######################################################### +# read database info by use of URPM module +sub read_rpm_data_urpm($$$$$$$) { + my $rh_opt = shift @_; # hash of program arguments + my $rh_provides = shift @_; + my $rh_install_time = shift @_; + my $rh_files = shift @_; + my $rh_depends = shift @_; + my $rh_virtual = shift @_; + my $rh_requires = shift @_; + + # cache ? + # acces to data by rpm2 should be fast, + # so do not need cache + # and init_cache works with a command to exec + # not with a perl module + + display_status('read rpm data using URPM'); + import URPM; + my $db = URPM::DB::open(); + debug('1 : analysis'); + + my $flag_suggests = is_set( $rh_opt, 'suggests' ); + + my %objects; + $db->traverse( + sub { + my ($package) = @_; # this is a URPM::Package object + my $name = $package->name; + my $installtime = $package->queryformat('%{INSTALLTIME}'); + $rh_install_time->{$name} = $installtime; + my @req = $package->requires(); + clean_rel( \@req ); + my @prov = $package->provides(); + clean_rel( \@prov ); + my @files = $package->files(); + + if ($flag_suggests) { + my $suggests = $package->queryformat('[%{SUGGESTS},]'); + my @suggests = split /,/, $suggests; + + #debug("suggests for $name : " . join ' ', @suggests); + clean_rel( \@suggests ); + push @req, @suggests; + } + + analyse_rpm_info( + $name, \@prov, \@req, \@files, + \%objects, $rh_provides, $rh_files, $rh_depends, + $rh_virtual, $rh_requires + ); + } + ); + + return; +} +######################################################### +sub read_rpm_data($$$$$$$) { + my $rh_opt = shift @_; # hash of program arguments + my $rh_provides = shift @_; + my $rh_install_time = shift @_; + my $rh_files = shift @_; + my $rh_depends = shift @_; + my $rh_virtual = shift @_; + my $rh_requires = shift @_; + + # empty all structures + %{$rh_provides} = (); + %{$rh_install_time} = (); + %{$rh_files} = (); + %{$rh_depends} = (); + %{$rh_virtual} = (); + %{$rh_requires} = (); + + my %code = ( + 'URPM' => \&read_rpm_data_urpm, + 'RPM2' => \&read_rpm_data_rpm2, + 'basic' => \&read_rpm_data_base, + ); + my @def_list = ( 'URPM', 'RPM2', 'basic' ); + my @list; + if ( exists $ENV{'RPMORPHAN_METHOD'} ) { + my $method = $ENV{'RPMORPHAN_METHOD'}; + + # should be 'URPM', 'RPM2' or 'basic' + if ( exists $code{$method} ) { + push @list, $method; + + # for security : basic will allways work + push @list, 'basic'; + } + else { + warning("unknown method $method, use default"); + @list = @def_list; + } + } + else { + + # default list (ordered) of perl packages to test + @list = @def_list; + } + my $ok = 0; + foreach my $method (@list) { + debug("try $method"); + eval { require $method . '.pm'; }; + if ($EVAL_ERROR) { + debug("can not use $method"); + } + else { + $ok = 1; + *read_rpm_data_m = $code{$method}; + read_rpm_data_m( $rh_opt, $rh_provides, $rh_install_time, $rh_files, + $rh_depends, $rh_virtual, $rh_requires ); + last; + } + } + if ( !$ok ) { + + # if nothing is working, use default basic method + read_rpm_data_base( $rh_opt, $rh_provides, $rh_install_time, $rh_files, + $rh_depends, $rh_virtual, $rh_requires ); + } + return; +} +## use critic +######################################################### +sub read_one_rc($$$) { + my $rh_list = shift @_; # list of available parameters + my $fh_rc = shift @_; + my $rcfile = shift @_; + + # perl cookbook, 8.16 + my $line = 1; + RC: while (<$fh_rc>) { + chomp; + s/#.*//; # comments + s/^\s+//; # skip spaces + s/\s+$//; + next RC unless length; + my ( $key, $value ) = split /\s*=\s*/, $_, 2; + if ( defined $key ) { + if ( exists $rh_list->{$key} ) { + + # the last line wins + if ( ref( $rh_list->{$key} ) eq 'ARRAY' ) { + @{ $rh_list->{$key} } = get_from_command_line($value); + } + else { + ${ $rh_list->{$key} } = $value; + } + + # special case : verbose will modify immediately behavior + init_debug($value) if ( $key eq 'verbose' ); + + debug( +"rcfile ($rcfile) : found $key parameter with value : $value" + ); + } + else { + warning("bad $key parameter in line $line in $rcfile file"); + } + } + else { + warning("bad line $line in $rcfile file"); + } + $line++; + } + + return; +} +######################################################### +# read all existing rc file from general to local : +# host, home, local directory +sub readrc($) { + + my $rh_list = shift @_; # list of available parameters + + # can use local rc file, home rc file, host rc file + my @list_rc = + ( '/etc/rpmorphanrc', $ENV{HOME} . '/.rpmorphanrc', '.rpmorphanrc', ); + + foreach my $rcfile (@list_rc) { + + if ( -f $rcfile ) { + debug("read rc from $rcfile"); + if ( open my $fh_rc, '<', $rcfile ) { + read_one_rc( $rh_list, $fh_rc, $rcfile ); + close $fh_rc + or warning("problem to close rc file $rcfile :$ERRNO"); + } + else { + warning("can not open rcfile $rcfile : $ERRNO"); + } + } + else { + debug("no rcfile $rcfile found"); + } + } + return; +} +######################################################### +# because arg can be given in one or several options : +# --add toto1 --add toto2 +# --add toto1,toto2 +sub get_from_command_line(@) { + my @arg = @_; + + my $comma = q{,}; + ## no critic (ProhibitParensWithBuiltins); + return split /$comma/, join( $comma, @arg ); + ## use critic; +} +######################################################### +sub is_remove_allowed($) { + my $opt_dry_run = shift @_; + + return ( ( $EFFECTIVE_USER_ID == 0 ) && ( !$opt_dry_run ) ); +} +######################################################### +# analyse command line options for guess +sub ana_guess($) { + my $rh_opt = shift @_; + + debug('ana_guess'); + + my $true = 1; + + # guess-all force all others + if ( is_set( $rh_opt, 'guess-all' ) ) { + $rh_opt->{'guess-perl'} = \$true; + $rh_opt->{'guess-python'} = \$true; + $rh_opt->{'guess-pike'} = \$true; + $rh_opt->{'guess-ruby'} = \$true; + $rh_opt->{'guess-common'} = \$true; + $rh_opt->{'guess-data'} = \$true; + $rh_opt->{'guess-doc'} = \$true; + $rh_opt->{'guess-dev'} = \$true; + $rh_opt->{'guess-lib'} = \$true; + } + + my $is_guess = + is_set( $rh_opt, 'guess-perl' ) + || is_set( $rh_opt, 'guess-python' ) + || is_set( $rh_opt, 'guess-pike' ) + || is_set( $rh_opt, 'guess-ruby' ) + || is_set( $rh_opt, 'guess-common' ) + || is_set( $rh_opt, 'guess-data' ) + || is_set( $rh_opt, 'guess-doc' ) + || is_set( $rh_opt, 'guess-dev' ) + || is_set( $rh_opt, 'guess-lib' ) + || is_set( $rh_opt, 'guess-custom' ); + + # test if a target is set + if ( ( !@{ $rh_opt->{'package'} } ) + && ( !is_set( $rh_opt, 'all' ) ) + && ( !$is_guess ) ) + { + + # default behavior if no targets selected + debug('set target to guess-lib (default)'); + $rh_opt->{'guess-lib'} = \$true; + } + return; +} +######################################################### +# parse command line exclude option +sub build_excluded($$) { + my $rh_opt = shift @_; + my $rh_excluded = shift @_; + + debug('build_excluded'); + if ( $rh_opt->{'exclude'} ) { + + # allow comma separated options on tags option + # and build a hash for faster access + my @liste_ex = get_from_command_line( @{ $rh_opt->{'exclude'} } ); + foreach my $ex (@liste_ex) { + $rh_excluded->{$ex} = 1; + debug("build_excluded : exclude $ex"); + } + } + + # to be passed to load_data; + # be carefull : we use here a new key : excluded , not exclude (command line) + $rh_opt->{'excluded'} = $rh_excluded; + return; +} +######################################################### +sub get_package_info($) { + my $pac = shift @_; + + my $cmd = "rpm -qil $pac"; + ## no critic ( ProhibitBacktickOperators ); + my @res = `$cmd`; + ## use critic + chomp @res; + + return @res; +} +######################################################### +sub get_package_summary($) { + my $pac = shift @_; + + my $cmd = "rpm -q --queryformat '%{SUMMARY}' $pac"; + ## no critic ( ProhibitBacktickOperators ); + my $res = `$cmd`; + ## use critic + + return $res; +} +######################################################### +# return true is rpm allow SUGGEST tags +sub is_suggests() { + + my $cmd = 'rpm -q --querytags '; + ## no critic ( ProhibitBacktickOperators ); + my @res = `$cmd`; + ## use critic + foreach my $tag (@res) { + chomp $tag; + if ( $tag =~ m/^SUGGESTS/ ) { + return 1; + } + } + return 0; +} +######################################################### + +1; diff --git a/rpmorphan-tk-lib.pl b/rpmorphan-tk-lib.pl new file mode 100644 index 0000000..0c0893e --- /dev/null +++ b/rpmorphan-tk-lib.pl @@ -0,0 +1,441 @@ +#!/usr/bin/perl +############################################################################### +# rpmorphan-tk-lib.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-1.11 | rpmorphan-tk-lib.pl | Wed Jul 6 14:15:24 2011 +0000 | gerbier $ +# +# 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. +# +############################################################################### +# this file contains all tk code for rpmorphan gui +############################################################################### + +use strict; +use warnings; + +use English '-no_match_vars'; + +## no critic(ProhibitPackageVars) +use vars + qw( $W_list $Opt_dry_run $Inloop @Liste_pac &display_total &insert_gui_orphans ); +## use critic + +################################################################ +# select all +sub tk_select_callback() { + + $W_list->selectionSet( 0, 'end' ); + return; +} +################################################################ +# unselect all +sub tk_unselect_callback() { + + $W_list->selectionClear( 0, 'end' ); + return; +} +######################################################### +# change cursor to watch +sub tk_cursor2watch() { + + my $cursor = $W_list->cget('-cursor'); + $W_list->configure( -cursor => 'watch' ); + $W_list->update(); + + return $cursor; +} +######################################################### +# restore cursor +sub tk_cursor2normal($) { + my $cursor = shift @_; # saved cursor + + # restore pointer + $W_list->configure( -cursor => $cursor ); + $W_list->update(); + return; +} +######################################################### +# remove selected packages +sub tk_remove_callback($) { + my $main = shift @_; # parent widget + + # change pointer + my $cursor = tk_cursor2watch(); + + # get full list + my @liste = $W_list->get( 0, 'end' ); + + # get selected items + my @sel = $W_list->curselection(); + + #transform index into names + my @selnames = map { $liste[$_] } @sel; + + # confirm dialog + my $answer = tk_dialog( $main, 'confirm', "confirm remove @selnames ?" ); + + if ( $answer =~ m/Yes/ ) { + + remove_packages( \@selnames, \@liste ); + } + + # restore pointer + tk_cursor2normal($cursor); + return; +} +######################################################### +# general text display in a new text window +# is used by all help buttons +sub tk_display_message($$@) { + my $main = shift @_; # parent widget + my $title = shift @_; # window title + my @baratin = @_; # text to display + + my $top = $main->Toplevel( -title => $title ); + $top->Button( -text => 'quit', -command => [ $top => 'destroy' ] )->pack(); + my $text = $top->Scrolled( + 'ROText', + -scrollbars => 'e', + -height => 25, + -width => 128, + -wrap => 'word' + )->pack( -side => 'left', -expand => 1, -fill => 'both' ); + $top->bind( '<Key-q>', [ $top => 'destroy' ] ); + + $text->insert( 'end', @baratin ); + $text->see('1.0'); + return; +} + +################################################################ +# general dialog +sub tk_dialog($$$) { + my $main = shift @_; # parent widget + my $title = shift @_; # window title + my $baratin = shift @_; # text to display + + my $dialog = $main->Dialog( + -text => $baratin, + -default_button => 'Yes', + -buttons => [qw/Yes No/] + ); + my $answer = $dialog->Show(); + + return $answer; +} +################################################################ +# display help text +sub tk_help_callback($) { + my $main = shift; # parent widget + + my $baratin = get_help_text(); + + tk_display_message( $main, 'help', $baratin ); + return; +} +################################################################ +# display log file +sub tk_log_callback($) { + my $main = shift; # parent widget + + my @l = get_log(); + my $l = join "\n", @l; + + tk_display_message( $main, 'help', $l ); + return; +} +######################################################### +# return name of current package +sub tk_get_current_elem() { + + # get current position + my $elem = $W_list->get('active'); + + # this code will be used if we display more + # than name on each line + # my $space = q{ }; + # my @c = split /$space/, $elem; + # my $c = $c[0]; + # return $c; + + return $elem; +} +######################################################### +# display package summary +sub tk_summary_callback($) { + my $main = shift @_; # parent widget + + my $c = tk_get_current_elem(); + if ($c) { + tk_popup( $main, get_package_summary($c) ); + } + else { + print "no current element selected\n"; + } + + return; +} +################################################################ +# display package info +sub tk_info_callback($) { + my $main = shift @_; # parent widget + + # get current position + my $c = tk_get_current_elem(); + + my @res = get_package_info($c); + chomp @res; + my $message = join "\n", @res; + + tk_display_message( $main, 'info', $message ); + return; +} +######################################################### +# for reload button +sub tk_load_callback($) { + my $main = shift @_; # parent widget + + load_data(1); + + return; +} +######################################################### +# is called when an item is selected/unselected +# and change a counter +sub tk_list_callback { + my $widget = shift @_; + my $selected = shift @_; + + # get selected items + my @sel = $W_list->curselection(); + + my $nb = scalar @sel; + + $selected->delete( '1.0', 'end' ); + $selected->insert( '1.0', $nb ); + + return; +} +######################################################### +# common sub for button and key +sub tk_quit($) { + my $main = shift @_; + $main->destroy(); + + return; +} +######################################################### +sub tk_popup($$) { + my $main = shift @_; + my $message = shift @_; + + my $top = $main->Toplevel(); + $top->title('popup'); + my $d = $top->Message( -text => $message ); + $d->pack(); + $d->focus(); + $top->update(); + + # wait 1 second and destroy popup + ## no critic (ProhibitMagicNumbers) + $main->after(1000); + + $top->destroy(); + + return; +} +######################################################### +sub build_tk_gui() { + + my $main = MainWindow->new( -title => build_title() ); + my $w_balloon = $main->Balloon(); + my $side = 'top'; + + # frame for buttons + ################### + my $frame1 = $main->Frame(); + $frame1->pack( -side => 'top', -expand => 0, -fill => 'none' ); + + # remove + my $remove_button = $frame1->Button( + -text => 'Remove', + -command => [ \&tk_remove_callback, $main ], + ); + $remove_button->pack( -side => 'left' ); + $w_balloon->attach( $remove_button, -msg => 'remove selected packages' ); + if ( is_remove_allowed($Opt_dry_run) ) { + + $main->bind( '<Key-r>', [ \&tk_remove_callback, $main ] ); + $main->bind( '<Key-x>', [ \&tk_remove_callback, $main ] ); + } + else { + $remove_button->configure( -state => 'disabled' ); + } + + # info + my $info_button = $frame1->Button( + -text => 'Info', + -command => [ \&tk_info_callback, $main ], + ); + $info_button->pack( -side => 'left' ); + $w_balloon->attach( $info_button, -msg => 'get info on current package' ); + $main->bind( '<Key-i>', [ \&tk_info_callback, $main ] ); + + # select all + my $select_button = $frame1->Button( + -text => 'Select all', + -command => [ \&tk_select_callback ], + ); + $select_button->pack( -side => 'left' ); + $w_balloon->attach( $select_button, -msg => 'select all packages' ); + $main->bind( '<Key-s>', [ \&tk_select_callback ] ); + + # unselect all + my $unselect_button = $frame1->Button( + -text => 'Unselect all', + -command => [ \&tk_unselect_callback ], + ); + $unselect_button->pack( -side => 'left' ); + $w_balloon->attach( $unselect_button, -msg => 'unselect all packages' ); + $main->bind( '<Key-u>', [ \&tk_unselect_callback ] ); + + # log + my $log_button = $frame1->Button( + -text => 'Log', + -command => [ \&tk_log_callback, $main ], + ); + $log_button->pack( -side => 'left' ); + $w_balloon->attach( $log_button, -msg => 'display log' ); + $main->bind( '<Key-l>', [ \&tk_log_callback, $main ] ); + + # reload button + my $reload_button = $frame1->Button( + -text => 'reLoad', + -command => [ \&tk_load_callback, $main ] + ); + $reload_button->pack( -side => 'left' ); + $w_balloon->attach( $reload_button, -msg => 'reload rpm database' ); + $main->bind( '<Key-L>', [ \&tk_load_callback, $main ] ); + + # help + my $help_button = $frame1->Button( + -text => 'Help', + -command => [ \&tk_help_callback, $main ], + ); + $help_button->pack( -side => 'left' ); + $w_balloon->attach( $help_button, -msg => 'help on rpmorphan interface' ); + $main->bind( '<Key-h>', [ \&tk_help_callback, $main ] ); + + # quit + my $exit_button = + $frame1->Button( -text => 'Quit', -command => [ \&tk_quit, $main ] ); + $exit_button->pack( -side => 'left' ); + $w_balloon->attach( $exit_button, -msg => 'quit the software' ); + $main->bind( '<Key-q>', [ \&tk_quit, $main ] ); + + # frame for the list + #################### + my $frame2 = $main->Frame(); + $frame2->pack( -side => $side, -expand => 1, -fill => 'both' ); + + $W_list = $frame2->Scrolled( + 'Listbox', + -scrollbars => 'e', + -selectmode => 'multiple' + )->pack( -side => $side, -expand => 1, -fill => 'both' ); + + $w_balloon->attach( $W_list, -msg => 'list window' ); + + # for summary + $main->bind( '<Key-S>', [ \&tk_summary_callback, $main ] ); + + # button 3 : full info + $main->bind( '<Button-3>', [ \&tk_info_callback, $main ] ); + + # frame for status + ################### + my $frame3 = $main->Frame(); + $frame3->pack( -side => 'bottom', -expand => 0, -fill => 'none' ); + + # number of packages + $frame3->Label( -text => 'total' )->pack( -side => 'left' ); + my $w_total = $frame3->ROText( -width => 4, -height => 1 ); + $w_total->pack( -side => 'left', -expand => 0, ); + $w_balloon->attach( $w_total, -msg => 'number of packages' ); + + # number of orphans + $frame3->Label( -text => 'orphans' )->pack( -side => 'left' ); + my $w_orphans = $frame3->ROText( -width => 4, -height => 1 ); + $w_orphans->pack( -side => 'left', -expand => 0, ); + $w_balloon->attach( $w_orphans, -msg => 'number of orphans packages' ); + + # number of selected + $frame3->Label( -text => 'selected' )->pack( -side => 'left' ); + my $w_selected = $frame3->ROText( -width => 4, -height => 1 ); + $w_selected->pack( -side => 'left', -expand => 0, ); + $w_balloon->attach( $w_selected, -msg => 'number of selected orphans' ); + + # to allow to show number of selected + $W_list->bind( '<<ListboxSelect>>', [ \&tk_list_callback, $w_selected ] ); + + # status + $frame3->Label( -text => 'status' )->pack( -side => 'left' ); + my $w_status = $frame3->ROText( -width => 40, -height => 1 ); + $w_status->pack( -side => 'left', -expand => 1, -fill => 'both' ); + $w_balloon->attach( $w_status, + -msg => 'display informations on internal state' ); + + # for debuging + $main->bind( '<Key-d>', [ \&dump_struct ] ); + + # common gui subroutines with different coding + ############################################## + # define insert_gui_orphans as a ref to anonymous sub + *insert_gui_orphans = sub { + $W_list->delete( 0, 'end' ); + $W_list->insert( 'end', @_ ); + + my $nb = scalar @_; + $w_orphans->delete( '1.0', 'end' ); + $w_orphans->insert( '1.0', $nb ); + + # reset + $w_selected->delete( '1.0', 'end' ); + $w_selected->insert( '1.0', 0 ); + }; + + *display_status = sub { + my $text = shift @_; + + debug($text); + + $w_status->delete( '1.0', 'end' ); + $w_status->insert( '1.0', $text ); + + # an update before the mainloop call cause a core dump + # so we have to test if we are before of after + $w_status->update() if $Inloop; + }; + + *display_total = sub { + + # display orphans total + my $nb = scalar @Liste_pac; + $w_total->delete( '1.0', 'end' ); + $w_total->insert( '1.0', $nb ); + }; + return; +} + +######################################################### +1; diff --git a/rpmorphan.1 b/rpmorphan.1 new file mode 100644 index 0000000..9a08e5c --- /dev/null +++ b/rpmorphan.1 @@ -0,0 +1,489 @@ +.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "RPMORPHAN 1" +.TH RPMORPHAN 1 "2012-08-01" "perl v5.14.2" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +rpmorphan \- find orphaned packages +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +rpmorphan finds \*(L"orphaned\*(R" packages on your system. It determines which packages have no other +packages depending on their installation, and shows you a list of these packages. +It is clone of deborphan debian software for rpm packages. +.PP +It will try to help you to remove unused packages, for exemple : +.PP +\&\- after a distribution upgrade +.PP +\&\- when you want to suppress packages after some tests +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +rpmorphan [options] [targets] +.PP +or +.PP +grpmorphan [options] [targets] +.PP +using grpmorphan is a short way to use \*(L"rpmorphan \-gui\*(R" +.PP +options: +.PP +.Vb 3 +\& \-help brief help message +\& \-man full documentation +\& \-V, \-\-version print version +\& +\& \-verbose verbose +\& \-dry\-run simulate package remove +\& \-fullalgo force full algorythm +\& \-suggests use suggested package as if required +\& \-use\-cache use cache to avoid rpm query +\& \-clear\-cache remove cache file +\& \-gui display the graphical interface +\& \-tk display the tk graphical interface +\& \-curses display the curses graphical interface +\& +\& \-exclude pac exclude pac from results +\& \-install\-time +/\-d apply on packages which are installed before (after) d days +\& \-access\-time d apply on packages which are not been accessed for d days (slow) +.Ve +.PP +targets: +.PP +.Vb 10 +\& \-package pac search if pac is an orphan package +\& \-all apply on all packages +\& \-guess\-perl apply on perl packages +\& \-guess\-python apply on python packages +\& \-guess\-pike apply on pike packages +\& \-guess\-ruby apply on ruby packages +\& \-guess\-common apply on common packages +\& \-guess\-data apply on data packages +\& \-guess\-doc apply on documentation packages +\& \-guess\-dev apply on development packages +\& \-guess\-lib apply on library packages (this is the default target if none is specified) +\& \-guess\-all apply all \-guess\-* options (perl, python ...) +\& \-guess\-custom regex apply the given regex to filter to package\*(Aqs names to filter the output +.Ve +.PP +keep file management +.PP +.Vb 5 +\& \-keep\-file file define the keep file to be used +\& \-list\-keep list permanent exclude list +\& \-zero\-keep empty permanent exclude list +\& \-add\-keep pac add pac package to permanent exclude list +\& \-del\-keep pac remove pac package from permanent exclude list +.Ve +.SH "REQUIRED ARGUMENTS" +.IX Header "REQUIRED ARGUMENTS" +you should provide at least one target. this can be +.IP "\fB\-all\fR" 8 +.IX Item "-all" +all the installed rpm package +.IP "\fB\-package\fR" 8 +.IX Item "-package" +one or several explicit package +.IP "\fB\-guess\-*\fR" 8 +.IX Item "-guess-*" +pre-selected packages groups +.IP "\fB\-guess\-custom\fR" 8 +.IX Item "-guess-custom" +give a filter to select the packages you want study. you can use regular expressions. +.SH "OPTIONS" +.IX Header "OPTIONS" +.IP "\fB\-help\fR" 8 +.IX Item "-help" +Print a brief help message and exits. +.IP "\fB\-man\fR" 8 +.IX Item "-man" +Print the manual page and exits. +.IP "\fB\-version\fR" 8 +.IX Item "-version" +Print the program release and exit. +.IP "\fB\-verbose\fR" 8 +.IX Item "-verbose" +The program works and print debugging messages. +.IP "\fB\-dry\-run\fR" 8 +.IX Item "-dry-run" +this is a simulation mode : rpmorphan will show you what is +the result of package removing. The window's title is modified +to show this mode. +.IP "\fB\-use\-cache\fR" 8 +.IX Item "-use-cache" +the rpm query may be long (10 to 30 s). If you will run an rpmorphan tool +several time, this option will allow to gain a lot of time : +it save the rpm query on a file cache (first call), then +use this cache instead quering rpm (others calls). +.IP "\fB\-clear\-cache\fR" 8 +.IX Item "-clear-cache" +to remove cache file. Can be used with \-use\-cache to write +a new cache. +.IP "\fB\-gui\fR" 8 +.IX Item "-gui" +display a graphical interface which allow to show informations, remove packages +(an internal help is provided). This is currently the same as the \-tk option +.IP "\fB\-tk\fR" 8 +.IX Item "-tk" +display a tk graphical interface which allow to show informations, remove packages +(an internal help is provided). The Tk perl module is necessary to run the gui. +.IP "\fB\-curses\fR" 8 +.IX Item "-curses" +display a curses graphical interface which allow to show informations, remove packages +(an internal help is provided). The Curses::UI perl module is necessary to run the gui. +.IP "\fB\-exclude\fR" 8 +.IX Item "-exclude" +this option will specify the packages to exclude from the output. +Can be used as '\-\-exclude pac1 \-\-exclude pac2' +or '\-\-exclude \*(L"pac1, pac2\*(R"' +.IP "\fB\-install\-time\fR" 8 +.IX Item "-install-time" +install-time is a filter on the period from the package installation date to now (in days). +if set positive, it only allow packages installed before x days. +if set negative, it only allow packages installed since x days. +.IP "\fB\-access\-time\fR" 8 +.IX Item "-access-time" +access-time is designed to filter packages which have not been used since x days. +.Sp +be careful : this option will slow the program +.IP "\fB\-fullalgo\fR" 8 +.IX Item "-fullalgo" +for a small list of packages, rpmorphan use a different quicker methode : rpm \-e \-\-test +.Sp +this option can be used to force the use of the full algo +.IP "\fB\-suggests\fR" 8 +.IX Item "-suggests" +some rpm version offers a meta-data dependency call suggests : it is +for usefull but not necessary packages. if this option is set, the +suggested packages are used in rpmorphan as required packages. +The default value for this option is unset. +.IP "\fB\-package\fR" 8 +.IX Item "-package" +search if the given package(s) is(are) orphaned. +Can be used as '\-\-package pac1 \-\-package pac2' +or '\-\-package \*(L"pac1, pac2\*(R"' +.IP "\fB\-all\fR" 8 +.IX Item "-all" +apply on all installed packages. The output should be interpreted. +For example lilo or grub are orphaned packages, but are necessary +to boot ... +.Sp +the \*(L"\-install\-time\*(R" and \*(L"\-access\-time\*(R" options may be useful to filter the list +.IP "\fB\-guess\-perl\fR" 8 +.IX Item "-guess-perl" +This option tries to find perl modules. It tries to match \*(L"^perl\*(R" +.IP "\fB\-guess\-python\fR" 8 +.IX Item "-guess-python" +This option tries to find python modules. It tries to match \*(L"^python\*(R" +.IP "\fB\-guess\-pike\fR" 8 +.IX Item "-guess-pike" +This option tries to find pike modules. It tries to match \*(L"^pike\*(R" +.IP "\fB\-guess\-ruby\fR" 8 +.IX Item "-guess-ruby" +This option tries to find ruby modules. It tries to match \*(L"^ruby\*(R" +.IP "\fB\-guess\-common\fR" 8 +.IX Item "-guess-common" +This option tries to find common packages. It tries to match \*(L"\-common$\*(R" +.IP "\fB\-guess\-data\fR" 8 +.IX Item "-guess-data" +This option tries to find data packages. It tries to match \*(L"\-data$\*(R" +.IP "\fB\-guess\-doc\fR" 8 +.IX Item "-guess-doc" +This option tries to find documentation packages. It tries to match \*(L"\-doc$\*(R" +.IP "\fB\-guess\-data\fR" 8 +.IX Item "-guess-data" +This option tries to find data packages. It tries to match \*(L"\-data$\*(R" +.IP "\fB\-guess\-dev\fR" 8 +.IX Item "-guess-dev" +This option tries to find development packages. It tries to match \*(L"\-devel$\*(R" +.IP "\fB\-guess\-lib\fR" 8 +.IX Item "-guess-lib" +This option tries to find library packages. It tries to match \*(L"^lib\*(R" +.IP "\fB\-guess\-all\fR" 8 +.IX Item "-guess-all" +This is a short to tell : Try all of the above (perl, python ...) +.IP "\fB\-guess\-custom\fR" 8 +.IX Item "-guess-custom" +this will allow you to specify your own filter. for exemple \*(L"^wh\*(R" +will match whois, whatsnewfm ... +.IP "\fB\-keep\-file\fR" 8 +.IX Item "-keep-file" +define the keep file to be used. If not set, the /var/lib/rpmorphan/keep will be used +.IP "\fB\-list\-keep\fR" 8 +.IX Item "-list-keep" +list the permanent list of excluded packages and exit. +.IP "\fB\-zero\-keep\fR" 8 +.IX Item "-zero-keep" +empty the permanent list of excluded packages and exit. +.IP "\fB\-add\-keep\fR" 8 +.IX Item "-add-keep" +add package(s) to the permanent list of excluded packages and exit. +.Sp +Can be used as '\-\-add\-keep pac1 \-\-add\-keep pac2' +or '\-\-add\-keep \*(L"pac1, pac2\*(R"' +.IP "\fB\-del\-keep\fR" 8 +.IX Item "-del-keep" +remove package(s) from the permanent list of excluded packages and exit. +.Sp +Can be used as '\-\-add\-keep pac1 \-\-add\-keep pac2' +or '\-\-add\-keep \*(L"pac1, pac2\*(R"' +.SH "USAGE" +.IX Header "USAGE" +rpmorphan can be useful after a distribution upgrade, to remove packages forgotten +by the upgrade tool. It is interesting to use the options "\-all \-install\-time +xx'. +.PP +If you want to remove some recent tested packages, my advice is "\-all \-install\-time \-xx'. +.PP +if you just want to clean your disk, use '\-all \-access\-time xxx' +.SH "FILES" +.IX Header "FILES" +/var/lib/rpmorphan/keep : the permanent exclude list +.PP +/tmp/rpmorphan.cache : cache file to store rpm query. The cache +file is common to all rpmorphan tools +.PP +/var/log/rpmorphan.log : log of all deleted packages +.SH "CONFIGURATION" +.IX Header "CONFIGURATION" +the program can read rcfile if some exists. +it will load in order +.PP +/etc/rpmorphanrc +.PP +~/.rpmorphanrc +.PP +\&.rpmorphanrc +.PP +In this file, +.PP +# are comments, +.PP +and parameters are stored in the following format : +parameter = value +.PP +example : +.PP +all = 1 +.PP +curses = 1 +.SH "DEPENDENCIES" +.IX Header "DEPENDENCIES" +rpmorphan uses standard perl module in console mode. +.PP +If you want to use the Tk graphical interface, you should install the Tk module +(perl-Tk rpm package). +.PP +If you want to use the curses interface, you should install the Curses::UI perl module +( perl-Curses-UI rpm package). +.PP +If you want to speed up rpmorphan, you should install the \s-1RPM2\s0 perl module ( perl\-RPM2 rpm package). +On mandriva, the \s-1URPM\s0 perl module (dependency from urpmi) will be used instead. +.SH "EXAMPLES" +.IX Header "EXAMPLES" +.IP "display orphaned libraries" 8 +.IX Item "display orphaned libraries" +rpmorphan +.IP "display all orphaned packages in a curses interface" 8 +.IX Item "display all orphaned packages in a curses interface" +rpmorphan \-\-all \-\-curses +.IP "display orphaned packages, not used since one year" 8 +.IX Item "display orphaned packages, not used since one year" +rpmorphan \-\-all \-access\-time +365 +.IP "display all orphaned packages, installed in the last 10 days" 8 +.IX Item "display all orphaned packages, installed in the last 10 days" +rpmorphan \-\-all \-install\-time \-10 +.IP "display all orphaned packages, installed one month ago (or more)" 8 +.IX Item "display all orphaned packages, installed one month ago (or more)" +rpmorphan \-\-all \-install\-time +30 +.SH "BUGS AND LIMITATIONS" +.IX Header "BUGS AND LIMITATIONS" +Virtuals packages are not well (for now) taken in account. +Let's see an example : lilo and grub provide 'bootloader' virtual, which is +necessary for system boot. +if the 2 are installed, they will be shown all 2 as orphans (but you can not remove +the 2). +If you remove one of them, the other is now longer shown as orphan. +.PP +the software can only work with one version of each software : +we only treat the first version seen +.SH "INCOMPATIBILITIES" +.IX Header "INCOMPATIBILITIES" +not known +.SH "DIAGNOSTICS" +.IX Header "DIAGNOSTICS" +to be written +.SH "NOTES" +.IX Header "NOTES" +this program can be used as \*(L"normal\*(R" user to show orphans, +but you need to run it as supersuser (root) to remove packages +or apply changes in the permanent exclude list +.PP +access-time and install-time options are \*(L"new\*(R" features, not available +in deborphan tool +.PP +For Fedora users : +Yum offers a program called 'package\-cleanup' which, +called with the '\-\-leaves' option, can show quickly a list of +unused library packages. +.PP +For Mandriva users : +the 'urpme \-\-auto\-orphans' command show quickly a a list of +unused library packages. +.SH "ENVIRONMENT" +.IX Header "ENVIRONMENT" +.IP "\s-1RPMORPHAN_METHOD\s0" 8 +.IX Item "RPMORPHAN_METHOD" +for experts only : allow to force the method used to get rpm +data. It can be set to \s-1URPM\s0, \s-1RPM2\s0 or 'basic' (for external rpm query) +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmusage\fR\|(1) +.PP +\fIrpmdep\fR\|(1) +.PP +\fIrpmduplicates\fR\|(1) +.PP +\fIrpmextra\fR\|(1) +.SH "EXIT STATUS" +.IX Header "EXIT STATUS" +should be allways 0 +.SH "LICENSE AND COPYRIGHT" +.IX Header "LICENSE AND COPYRIGHT" +Copyright (C) 2006 by Eric Gerbier +This program is free software; you can redistribute it and/or modify +it under the terms of the \s-1GNU\s0 General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +.SH "AUTHOR" +.IX Header "AUTHOR" +Eric Gerbier +.PP +you can report any bug or suggest to gerbier@users.sourceforge.net diff --git a/rpmorphan.logrotate b/rpmorphan.logrotate new file mode 100644 index 0000000..4660ffe --- /dev/null +++ b/rpmorphan.logrotate @@ -0,0 +1,7 @@ +/var/log/rpmorphan.log { + missingok + notifempty + size 30k + yearly + create 0600 root root +} diff --git a/rpmorphan.lsm b/rpmorphan.lsm new file mode 100644 index 0000000..df87950 --- /dev/null +++ b/rpmorphan.lsm @@ -0,0 +1,16 @@ +Begin4 +Title: rpmorphan +Version: 1.11 +Entered-date: 2012-08-03 +Description: rpmorphan finds "orphaned" packages on your system. It determines + which packages have no other packages depending on their installation, + and shows you a list of these packages. + It intends to be clone of deborphan debian tools for rpm packages. +Keywords: administration,rpm +Author: gerbier@users.sourceforge.net (Eric Gerbier) +Maintained-by: gerbier@users.sourceforge.net (Eric Gerbier) +Primary-site: http://rpmorphan.sourceforge.net + 46 Ko rpmorphan-1.11.tar.gz +Platforms: linux, works with perl +Copying-policy: GPL +End diff --git a/rpmorphan.pl b/rpmorphan.pl new file mode 100755 index 0000000..a034955 --- /dev/null +++ b/rpmorphan.pl @@ -0,0 +1,1405 @@ +#!/usr/bin/perl +############################################################################### +# rpmorphan.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-1.11 | rpmorphan.pl | Wed Aug 1 20:47:14 2012 +0200 | Eric Gerbier $ +# +# 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. +# +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +use Getopt::Long; # arg analysis +use Pod::Usage; # man page +use POSIX qw(strftime); +use Data::Dumper; # debug + +# library +use File::Basename; +my $dirname = dirname($PROGRAM_NAME); +my $basename = basename($PROGRAM_NAME); +require $dirname . '/rpmorphan-lib.pl' + or die "problem to load rpmorphan-lib: $ERRNO\n"; + +# translations +############## +use lib q{.}; # for tests +use lib '/usr/lib/rpmorphan'; # for prod +my $lang = $ENV{'LANG'}; +my $locale_dir; +if ( $lang =~ m/fr_FR/ ) { + $locale_dir = 'locale/fr_FR/rpmorphan_trans.pl'; +} +else { + $locale_dir = 'locale/en/rpmorphan_trans.pl'; +} +## no critic(RequireCheckingReturnValueOfEval) +eval { require $locale_dir; }; +if ($EVAL_ERROR) { + warning("can not load translation file $locale_dir"); + exit; +} + +############################################################################### +# global variables +############################################################################### +## no critic(Capitalization) +## no critic(ProhibitPackageVars) + +# translations +use vars qw( %TRANS ); + +# main widget (used for mainloop in curse mode) +use vars qw( $Main_ui); + +# widget list of packages for gui +use vars qw( $W_list ); + +# to know if we are or not in mainloop +use vars qw( $Inloop ); +$Inloop = 0; + +# command line option for access_time test +my $Opt_access_time; + +# options (for load_data, from tk and curses) +my %Opt; + +# command line option for dry-run (simulation mode) +use vars qw( $Opt_dry_run ); + +# hash structures to be filled with rpm query +my %Provides; +my %Depends; +my %Requires; +my %Install_time; +my %Files; +my %Virtual; + +# list of filtered packages +use vars qw( @Liste_pac ); +## use critic +## no critic(RequireCheckingReturnValueOfEval) + +# the code should use no "print" calls +# but instead debug, warning, info calls +########################################################## +# for debuging +# dump internal structures +sub dump_struct() { + print Dumper( 'depends', \%Depends ); + print Dumper( 'virtuals', \%Virtual ); + + display_status('dump done'); + return; +} +######################################################### +{ + + # for gui, we will have to call this sub after each delete + # as it is very expensive (file access) + # we will use a cache + # + my %access_time; + +# this is a filter : we will test if any file was accessed since $access_time days +# be careful : this code can be very slow + sub access_time_filter($$$$) { + my $name = shift @_; # package name + my $rh_files = shift @_; # general hash of package files + my $access_time = shift @_; # option access-time + my $now = shift @_; # current time + + my $ret; + if ($access_time) { + + my $age; + + # test for cache + if ( exists $access_time{$name} ) { + + # get from cache + $age = $access_time{$name}; + } + else { + + # not in cache : get it from file system + my ( $last_time, $file ) = + get_last_access( $rh_files->{$name} ); + $age = diff_date( $now, $last_time ); + debug("last access on $file is $age days old"); + $access_time{$name} = $age; + } + + if ( $age > $access_time ) { + debug("keep package $name : old access ($age days)"); + $ret = 1; + } + else { + debug("skip package $name : too recent access ($age days)"); + $ret = 0; + } + } + else { + + # no filter + $ret = 1; + } + return $ret; + } +} +######################################################### +# load_data is called from tk and curses lib (callback) +sub load_data($) { + my $opt_gui = shift @_; # true if use a gui + # also use %Opt gloval var + + # phase 1 : get data and build structures + read_rpm_data( \%Opt, \%Provides, \%Install_time, \%Files, \%Depends, + \%Virtual, \%Requires ); + +# phase 2 : guess filter +# if a package is set, it will be first used, then it will use in order -all, then (guess*) +# (see filter sub) + display_status('2 : guess filter'); + my @opt_package = @{ $Opt{'package'} }; + if (@opt_package) { + @Liste_pac = @opt_package; + } + else { + @Liste_pac = rpmfilter( \%Opt, \%Provides ); + } + + # needed by access-time and install-time options + my $now = time; + + # phase 3 : excluded and install-time filter + display_status('3 : excluded and install-time'); + my $opt_install_time = ${ $Opt{'install-time'} }; + my $excluded = $Opt{'excluded'}; + my @liste_pac2; + PACKAGE: foreach my $pac (@Liste_pac) { + if ( exists $excluded->{$pac} ) { + display_status("skip $pac : excluded"); + next PACKAGE; + } + + if ($opt_install_time) { + my $diff = diff_date( $now, $Install_time{$pac} ); + if ( $opt_install_time > 0 ) { + if ( $diff < $opt_install_time ) { + display_status( + "skip $pac : too recent install ($diff days)"); + next PACKAGE; + } + } + elsif ( $diff > -$opt_install_time ) { + display_status("skip $pac : too old install ($diff days)"); + next PACKAGE; + } + } + push @liste_pac2, $pac; + } + + # note : access-time filter is a very slow method + # so we apply this filter after dependencies research + # see below access_time_filter call + + # we want the result sorted in alphabetic order + @Liste_pac = sort @liste_pac2; + display_total(); + + # phase 4 : solve dependencies + my @orphans = + search_orphans( \%Provides, \%Files, \%Depends, \%Virtual, $now, + \@Liste_pac, $Opt_access_time ); + + # display + if ($opt_gui) { + insert_gui_orphans(@orphans); + } + else { + + # just display @orphans + my $txt = join "\n", @orphans; + info($txt); + } + + display_status('ready'); + return; +} +######################################################### +# return true is $name is an orphan +sub solve($$$$) { + my $name = shift @_; # package name + my $rh_provides = shift @_; # general provide hash + my $rh_depends = shift @_; # general dependencies hash + my $rh_virtual = shift @_; # virtual package hash + + debug("(solve) : $name"); + + my $flag_orphan = 1; + PROVIDE: foreach my $prov ( @{ $rh_provides->{$name} } ) { + + # dependencies can be to "virtual" object : smtpdaemon, webclient, bootloader + # for example, try "rpm -q --whatprovides webclient" + # you will get many packages : elinks,kdebase-common,mozilla,wget,lynx ... + + # with this new algo, let's see an example + # lilo and grub provide 'bootloader' virtual, which is necessary + # If the 2 are installed, they will be shown all 2 as orphans + # but if you remove one of them, the other is now longer orphan + if ( exists $rh_virtual->{$prov} ) { + + # this feature is a virtual + my @virtuals = sort keys %{ $rh_virtual->{$prov} }; + my $nb_virtual = scalar @virtuals; + if ( $nb_virtual == 1 ) { + + # name provide a virtual and is the only one + # so it act as a real dependency + $flag_orphan = 0; + debug("$name is required by virtual @virtuals ($prov)"); + last PROVIDE; + } + else { + + # name provide virtual but is not the only one + # we consider it an orphan + debug("$name : skip virtual $prov (@virtuals)"); + next PROVIDE; + } + } + + if ( exists $rh_depends->{$prov} ) { + + # some packages depends from $prov ... + $flag_orphan = 0; + + # only in verbose mode + if ( is_verbose( \%Opt ) ) { + my @depends = sort keys %{ $rh_depends->{$prov} }; + debug("$name is required by @depends ($prov)"); + } + last PROVIDE; + } + } + return $flag_orphan; +} +######################################################### +# use the slow "rpm -e --test" command +# but is quicker if used only on 1 package (no analysis) +# return true if is an orphan +sub quicksolve($) { + my $name = shift @_; # package name + + debug("(quicksolve) : $name"); + + my $cmd = 'rpm -e --test '; + + ## no critic ( ProhibitBacktickOperators ); + my @depends = `$cmd $name 2>&1`; + ## use critic; + my $ret; + ## no critic (ProhibitMagicNumbers) + if ( $#depends == -1 ) { + + # empty output : ok + #info($name); + $ret = 1; + } + else { + $ret = 0; + debug("$name is required by @depends"); + } + return $ret; +} +########################################################## +sub quick_run($$) { + my $rh_opt = shift @_; + my $rh_excluded = shift @_; + + debug('quick_run'); + + #allow comma separated options on tags option + my @opt_package = get_from_command_line( @{ $rh_opt->{'package'} } ); + $rh_opt->{'package'} = \@opt_package; + + if ( !is_set( $rh_opt, 'fullalgo' ) ) { + ## no critic (ProhibitMagicNumbers) + my $max_packages = 5; + if ( $#opt_package <= $max_packages ) { + + # use quicksolve + PAC: foreach my $pac (@opt_package) { + if ( exists $rh_excluded->{$pac} ) { + debug("skip $pac : excluded"); + next PAC; + } + info($pac) if ( quicksolve($pac) ); + } + exit; + } + else { + debug('too much package for quicksolve algo'); + } + } + else { + debug('full algo required'); + } + return; +} +########################################################## +# resolv dependencies for all packages in ra_liste_pac +sub search_orphans($$$$$$$) { + my $rh_provides = shift @_; + my $rh_files = shift @_; + my $rh_depends = shift @_; + my $rh_virtual = shift @_; + my $now = shift @_; + my $ra_liste_pac = shift @_; # list of all packages + my $opt_access_time = shift @_; + + display_status('4 : solve'); + my @orphans; + foreach my $pac ( @{$ra_liste_pac} ) { + if ( solve( $pac, $rh_provides, $rh_depends, $rh_virtual, ) + and access_time_filter( $pac, $rh_files, $opt_access_time, $now ) ) + { + push @orphans, $pac; + } + } + return @orphans; +} +######################################################### +# remove entries from lists for a package +sub remove_a_package($) { + my $pacname = shift @_; + + # first remove dependencies + PROVIDE: foreach my $req ( @{ $Requires{$pacname} } ) { + + delete $Depends{$req}{$pacname}; + debug("delete Depends { $req } {$pacname}"); + + # suppress entry if empty + my $keys = scalar keys %{ $Depends{$req} }; + if ( !$keys ) { + delete $Depends{$req}; + debug("delete empty Depends { $req }"); + } + } + + # remove virtuals + VIRT: foreach my $prov ( @{ $Provides{$pacname} } ) { + if ( exists $Virtual{$prov} ) { + delete $Virtual{$prov}{$pacname}; + debug("delete Virtual { $prov }{ $pacname }"); + + # suppress entry if empty + my $keys = scalar keys %{ $Virtual{$prov} }; + if ( !$keys ) { + delete $Virtual{$prov}; + debug("delete empty Virtual { $prov }"); + } + } + } + + # then delete all entries + delete $Provides{$pacname}; + delete $Files{$pacname}; + delete $Install_time{$pacname}; + delete $Requires{$pacname}; + + return; +} +######################################################### +sub is_installed($) { + my $pac = shift @_; + + # rpm -q return 0 is package exists, 1 if not + system "rpm -q $pac >/dev/null 2>/dev/null"; + ## no critic (ProhibitMagicNumbers) + # cf perldoc -f system + my $code = $CHILD_ERROR >> 8; + + return !$code; +} +######################################################### +# a generic program to be called by curses or tk sub +sub remove_packages($$) { + my $ra_sel = shift @_; # list of select packages + my $ra_list = shift @_; # list of all orphans packages + + display_status('remove packages'); + my @deleted_packages; + + my $total = scalar @{$ra_sel}; + my $i = 0; + + # remove package + SELECT: foreach my $pac ( @{$ra_sel} ) { + if ( !$Opt_dry_run ) { + $i++; + + # not in dry mode : apply change + + # rpm delete + # caution : it's suppose the shell used is bourne compatible + my $cmd = "rpm -e --allmatches $pac 2>&1"; + + # comment below to test without any real suppress + ## no critic ( ProhibitBacktickOperators ); + my $output = `$cmd`; + ## use critic; + + #print "@output\n"; + + # there is some case where a delete may not work : virtuals + # for example if you have lilo and grub installed, all 2 + # will be shown as orphans, but you will be able to delete only + # the first one ... + + # we can not rely on return code : it seems to be allways 1 + # but a successful delete does not provide any output + if ($output) { + + # to be sure : the only way is to test it + if ( is_installed($pac) ) { + + write_log($output); + display_status("problem to delete $pac (see log)"); + sleep 1; + + next SELECT; + } + else { + + # reports + write_log("$i/$total $pac deleted with output : $output"); + push @deleted_packages, $pac; + } + } + else { + + # reports + write_log("$i/$total $pac deleted"); + push @deleted_packages, $pac; + } + } + + # update internal lists + remove_a_package($pac); + } + + # build a new list of all packages without the removed ones + my @new_list; + LISTE: foreach my $l (@Liste_pac) { + foreach my $pac (@deleted_packages) { + next LISTE if ( $pac eq $l ); + } + push @new_list, $l; + } + @Liste_pac = @new_list; + display_total(); + + # compute new dependencies + my $now = time; + my @orphans = + search_orphans( \%Provides, \%Files, \%Depends, \%Virtual, $now, + \@new_list, $Opt_access_time ); + + # display + insert_gui_orphans(@orphans); + + #build_summary(@new_list); + + display_status('end of cleaning'); + return; +} +######################################################### +# used to build windows's title +sub build_title() { + + my $title = 'rpmorphan version ' . get_version(); + $title .= ' (dry-run)' if $Opt_dry_run; + return $title; +} +######################################################### +# keep +# +# a way to keep a list of exclusions +######################################################### +# list content of keep file +sub list_keep($$) { + my $keep_file = shift @_; + my $display = shift @_; # display or not ? + + my @list; + if ( open my $fh, '<', $keep_file ) { + @list = <$fh>; + chomp @list; + close $fh or warning("problem to close keep file : $ERRNO"); + + info("@list") if ($display); + } + else { + warning("can not read keep file $keep_file : $ERRNO"); + } + return @list; +} +######################################################### +# empty keep file +sub zero_keep($) { + my $keep_file = shift @_; + + if ( open my $fh, '>', $keep_file ) { + close $fh or warning("problem to close keep file : $ERRNO"); + } + else { + warning("can not empty keep file $keep_file : $ERRNO"); + } + return; +} +######################################################### +# add packages names to keep file +sub add_keep($$$) { + my $keep_file = shift @_; + my $ra_list = shift @_; + my $flag_append = shift @_; # true if append, else rewrite + + my @new_list; + my $mode; + if ($flag_append) { + $mode = '>>'; + + # read current list + my @old_list = list_keep( $keep_file, 0 ); + + # check for duplicates and merge + LOOP: foreach my $add ( @{$ra_list} ) { + foreach my $elem (@old_list) { + if ( $elem eq $add ) { + debug("skip add duplicate $add"); + next LOOP; + } + } + push @new_list, $add; + debug("add keep $add"); + } + } + else { + $mode = '>'; + @new_list = @{$ra_list}; + } + + if ( open my $fh, $mode, $keep_file ) { + foreach my $elem (@new_list) { + print {$fh} $elem . "\n"; + } + close $fh or warning("problem to close keep file : $ERRNO"); + } + else { + warning("can not write to keep file $keep_file : $ERRNO"); + } + return; +} +######################################################### +# remove packages names from keep file +sub del_keep($$) { + my $keep_file = shift @_; + my $ra_list = shift @_; + + # read current list + my @old_list = list_keep( $keep_file, 0 ); + + # remove entries + my @new_list; + LOOP: foreach my $elem (@old_list) { + foreach my $del ( @{$ra_list} ) { + if ( $elem eq $del ) { + debug("del keep $del"); + next LOOP; + } + } + push @new_list, $elem; + } + + # rewrite keep file + add_keep( $keep_file, \@new_list, 0 ); + return; +} +################################################################ +sub write_log_file($) { + my $text = shift @_; + + my $logfile = '/var/log/rpmorphan.log'; + + if ( open my $fh, '>>', $logfile ) { + my @date = gmtime; + print {$fh} strftime( '%Y/%m/%d %H:%M:%S ', @date ) . $text . "\n"; + + close $fh or warning("can not close $logfile : $ERRNO"); + } + else { + display_status("can not write to $logfile : $ERRNO"); + } + return; +} +################################################################ +{ + + # store the current session log + # just to avoid a global var + my @log; + + sub write_log { + push @log, @_; + my $text = shift @_; + display_status($text); + write_log_file($text); + return; + } + + sub get_log() { + return @log; + } +} +######################################################### +# check if tk is available +sub test_tk_gui() { + + debug('test_tk_gui'); + eval { + require Tk; + require Tk::Balloon; + require Tk::Dialog; + require Tk::Frame; + require Tk::Label; + require Tk::Listbox; + require Tk::Message; + require Tk::ROText; + }; + if ($EVAL_ERROR) { + warning('can not find Tk perl module'); + return 0; + } + else { + debug('Tk ok'); + import Tk; + import Tk::Balloon; + import Tk::Dialog; + import Tk::Frame; + import Tk::Label; + import Tk::Listbox; + import Tk::Message; + import Tk::ROText; + + # should be ok + require $dirname . '/rpmorphan-tk-lib.pl' + or die "problem to load rpmorphan-tk-lib: $ERRNO\n"; + build_tk_gui(); + return 'tk'; + } +} + +######################################################### +# check if curses is available +sub test_curses_gui() { + + debug('test_curses_gui'); + eval { require Curses::UI; }; + if ($EVAL_ERROR) { + warning('can not find Curses::UI perl module'); + return 0; + } + else { + debug('curses ok'); + import Curses::UI; + + # should be ok + require $dirname . '/rpmorphan-curses-lib.pl' + or die "problem to load rpmorphan-curses-lib: $ERRNO\n"; + build_curses_gui(); + return 'curses'; + } +} +######################################################### +# general gui +######################################################### +sub test_gui() { + return ( test_tk_gui() or test_curses_gui() ); +} +######################################################### +sub get_help_text() { + + my $baratin = $TRANS{'help'}; + return $baratin; +} +######################################################### +# version +######################################################### +{ + my $version = '1.10'; + + sub get_version() { + return $version; + } +} + +######################################################### +# +# main +# +######################################################### +my $opt_verbose = 0; # default value +init_debug($opt_verbose); + +my $opt_help; +my $opt_man; +my $opt_version; +my $opt_fullalgo; +my $opt_use_cache; +my $opt_clear_cache; +my $opt_suggests; +my $opt_gui; +my $opt_tk; +my $opt_curses; + +my $opt_all; + +my $opt_guess_perl; +my $opt_guess_python; +my $opt_guess_pike; +my $opt_guess_ruby; +my $opt_guess_common; +my $opt_guess_data; +my $opt_guess_doc; +my $opt_guess_dev; +my $opt_guess_lib; +my $opt_guess_all; +my $opt_guess_custom; + +my @opt_package; +my @opt_exclude; +my $opt_install_time; + +my $opt_keep_file; +my $opt_list_keep; +my $opt_zero_keep; +my @opt_add_keep; +my @opt_del_keep; + +%Opt = ( + 'help' => \$opt_help, # help + 'man' => \$opt_man, # full help + 'verbose' => \$opt_verbose, # debug + 'dry-run' => \$Opt_dry_run, # + 'version' => \$opt_version, + 'fullalgo' => \$opt_fullalgo, + 'all' => \$opt_all, # all packages + 'guess-perl' => \$opt_guess_perl, # perl packages + 'guess-python' => \$opt_guess_python, # python packages + 'guess-pike' => \$opt_guess_pike, + 'guess-ruby' => \$opt_guess_ruby, + 'guess-common' => \$opt_guess_common, + 'guess-data' => \$opt_guess_data, + 'guess-doc' => \$opt_guess_doc, + 'guess-dev' => \$opt_guess_dev, + 'guess-lib' => \$opt_guess_lib, + 'guess-all' => \$opt_guess_all, # all guess packages + 'guess-custom' => \$opt_guess_custom, + 'package' => \@opt_package, + 'exclude' => \@opt_exclude, + 'suggests' => \$opt_suggests, + 'install-time' => \$opt_install_time, + 'access-time' => \$Opt_access_time, + 'add-keep' => \@opt_add_keep, + 'del-keep' => \@opt_del_keep, + 'list-keep' => \$opt_list_keep, + 'zero-keep' => \$opt_zero_keep, + 'keep-file' => \$opt_keep_file, + 'use-cache' => \$opt_use_cache, + 'clear-cache' => \$opt_clear_cache, + 'gui' => \$opt_gui, + 'tk' => \$opt_tk, + 'curses' => \$opt_curses, +); + +# call from this name force gui +if ( $basename =~ m/grpmorphan/ ) { + $opt_gui = 1; +} + +# first read config file +readrc( \%Opt ); + +# get arguments +Getopt::Long::Configure('no_ignore_case'); +GetOptions( + \%Opt, 'help|?', 'man', 'verbose!', + 'fullalgo!', 'version|V', 'all!', 'guess-perl!', + 'guess-python!', 'guess-pike!', 'guess-ruby!', 'guess-common!', + 'guess-data!', 'guess-doc!', 'guess-dev!', 'guess-lib!', + 'guess-all!', 'guess-custom=s', 'package=s', 'exclude=s', + 'install-time=i', 'access-time=i', 'list-keep', 'zero-keep', + 'add-keep=s', 'del-keep=s', 'use-cache!', 'clear-cache', + 'gui!', 'tk!', 'curses!', 'dry-run!', + 'suggests!', +) or pod2usage(2); + +if ($opt_help) { + pod2usage(1); +} +elsif ($opt_man) { + pod2usage( -verbose => 2 ); +} +elsif ($opt_version) { + print_version( get_version() ); + exit; +} + +# use command line argument +init_debug($opt_verbose); + +# keep file management +# set default value +$opt_keep_file = $opt_keep_file || '/var/lib/rpmorphan/keep'; +if ($opt_list_keep) { + list_keep( $opt_keep_file, 1 ); + exit; +} +if ($opt_zero_keep) { + zero_keep($opt_keep_file); + exit; +} +if (@opt_add_keep) { + my @liste_add = get_from_command_line(@opt_add_keep); + add_keep( $opt_keep_file, \@liste_add, 1 ); + exit; +} +if (@opt_del_keep) { + my @liste_del = get_from_command_line(@opt_del_keep); + del_keep( $opt_keep_file, \@liste_del ); + exit; +} + +# disable suggests option if rpm does not provide it +if ($opt_suggests) { + $opt_suggests = is_suggests(); +} + +ana_guess( \%Opt ); + +# excluded files +my %excluded; + +# first : permanent "keep" file +my @list_keep = list_keep( $opt_keep_file, 0 ); +foreach my $ex (@list_keep) { + $excluded{$ex} = 1; + debug("conf : permanent exclude $ex"); +} + +build_excluded( \%Opt, \%excluded ); + +# install-time and access-time options are only available with the full algo +if ( ($opt_install_time) or ($Opt_access_time) ) { + debug('forced full algo'); + $opt_fullalgo = 1; +} + +# if only few packages, the big algo is not necessary +# quicksolve algo will be faster +if (@opt_package) { + + quick_run( \%Opt, \%excluded ); +} + +# test for graphical interface +if ($opt_gui) { + $opt_gui = test_gui(); +} +elsif ($opt_tk) { + $opt_gui = test_tk_gui(); +} +elsif ($opt_curses) { + $opt_gui = test_curses_gui(); +} + +if ( !$opt_gui ) { + *display_status = \&debug; + *display_total = sub { }; +} + +# load and compute +load_data($opt_gui); + +# display +if ($opt_gui) { + + # this will allow status update + $Inloop = 1; + + # loop events + if ( $opt_gui eq 'curses' ) { + $Main_ui->mainloop; + } + else { + MainLoop(); + } +} + +__END__ + +=head1 NAME + +rpmorphan - find orphaned packages + +=head1 DESCRIPTION + +rpmorphan finds "orphaned" packages on your system. It determines which packages have no other +packages depending on their installation, and shows you a list of these packages. +It is clone of deborphan debian software for rpm packages. + +It will try to help you to remove unused packages, for exemple : + +- after a distribution upgrade + +- when you want to suppress packages after some tests + +=head1 SYNOPSIS + +rpmorphan [options] [targets] + +or + +grpmorphan [options] [targets] + +using grpmorphan is a short way to use "rpmorphan -gui" + +options: + + -help brief help message + -man full documentation + -V, --version print version + + -verbose verbose + -dry-run simulate package remove + -fullalgo force full algorythm + -suggests use suggested package as if required + -use-cache use cache to avoid rpm query + -clear-cache remove cache file + -gui display the graphical interface + -tk display the tk graphical interface + -curses display the curses graphical interface + + -exclude pac exclude pac from results + -install-time +/-d apply on packages which are installed before (after) d days + -access-time d apply on packages which are not been accessed for d days (slow) + +targets: + + -package pac search if pac is an orphan package + -all apply on all packages + -guess-perl apply on perl packages + -guess-python apply on python packages + -guess-pike apply on pike packages + -guess-ruby apply on ruby packages + -guess-common apply on common packages + -guess-data apply on data packages + -guess-doc apply on documentation packages + -guess-dev apply on development packages + -guess-lib apply on library packages (this is the default target if none is specified) + -guess-all apply all -guess-* options (perl, python ...) + -guess-custom regex apply the given regex to filter to package's names to filter the output + +keep file management + + -keep-file file define the keep file to be used + -list-keep list permanent exclude list + -zero-keep empty permanent exclude list + -add-keep pac add pac package to permanent exclude list + -del-keep pac remove pac package from permanent exclude list + +=head1 REQUIRED ARGUMENTS + +you should provide at least one target. this can be + +=over 8 + +=item B<-all> + +all the installed rpm package + +=item B<-package> + +one or several explicit package + +=item B<-guess-*> + +pre-selected packages groups + +=item B<-guess-custom> + +give a filter to select the packages you want study. you can use regular expressions. + +=back + +=head1 OPTIONS + +=over 8 + +=item B<-help> + +Print a brief help message and exits. + +=item B<-man> + +Print the manual page and exits. + +=item B<-version> + +Print the program release and exit. + +=item B<-verbose> + +The program works and print debugging messages. + +=item B<-dry-run> + +this is a simulation mode : rpmorphan will show you what is +the result of package removing. The window's title is modified +to show this mode. + +=item B<-use-cache> + +the rpm query may be long (10 to 30 s). If you will run an rpmorphan tool +several time, this option will allow to gain a lot of time : +it save the rpm query on a file cache (first call), then +use this cache instead quering rpm (others calls). + +=item B<-clear-cache> + +to remove cache file. Can be used with -use-cache to write +a new cache. + +=item B<-gui> + +display a graphical interface which allow to show informations, remove packages +(an internal help is provided). This is currently the same as the -tk option + +=item B<-tk> + +display a tk graphical interface which allow to show informations, remove packages +(an internal help is provided). The Tk perl module is necessary to run the gui. + +=item B<-curses> + +display a curses graphical interface which allow to show informations, remove packages +(an internal help is provided). The Curses::UI perl module is necessary to run the gui. + + +=item B<-exclude> + +this option will specify the packages to exclude from the output. +Can be used as '--exclude pac1 --exclude pac2' +or '--exclude "pac1, pac2"' + +=item B<-install-time> + +install-time is a filter on the period from the package installation date to now (in days). +if set positive, it only allow packages installed before x days. +if set negative, it only allow packages installed since x days. + +=item B<-access-time> + +access-time is designed to filter packages which have not been used since x days. + +be careful : this option will slow the program + +=item B<-fullalgo> + +for a small list of packages, rpmorphan use a different quicker methode : rpm -e --test + +this option can be used to force the use of the full algo + +=item B<-suggests> + +some rpm version offers a meta-data dependency call suggests : it is +for usefull but not necessary packages. if this option is set, the +suggested packages are used in rpmorphan as required packages. +The default value for this option is unset. + +=item B<-package> + +search if the given package(s) is(are) orphaned. +Can be used as '--package pac1 --package pac2' +or '--package "pac1, pac2"' + +=item B<-all> + +apply on all installed packages. The output should be interpreted. +For example lilo or grub are orphaned packages, but are necessary +to boot ... + +the L</-install-time> and L</-access-time> options may be useful to filter the list + +=item B<-guess-perl> + +This option tries to find perl modules. It tries to match "^perl" + +=item B<-guess-python> + +This option tries to find python modules. It tries to match "^python" + +=item B<-guess-pike> + +This option tries to find pike modules. It tries to match "^pike" + +=item B<-guess-ruby> + +This option tries to find ruby modules. It tries to match "^ruby" + +=item B<-guess-common> + +This option tries to find common packages. It tries to match "-common$" + +=item B<-guess-data> + +This option tries to find data packages. It tries to match "-data$" + +=item B<-guess-doc> + +This option tries to find documentation packages. It tries to match "-doc$" + +=item B<-guess-data> + +This option tries to find data packages. It tries to match "-data$" + +=item B<-guess-dev> + +This option tries to find development packages. It tries to match "-devel$" + +=item B<-guess-lib> + +This option tries to find library packages. It tries to match "^lib" + +=item B<-guess-all> + +This is a short to tell : Try all of the above (perl, python ...) + +=item B<-guess-custom> + +this will allow you to specify your own filter. for exemple "^wh" +will match whois, whatsnewfm ... + +=item B<-keep-file> + +define the keep file to be used. If not set, the /var/lib/rpmorphan/keep will be used + +=item B<-list-keep> + +list the permanent list of excluded packages and exit. + +=item B<-zero-keep> + +empty the permanent list of excluded packages and exit. + +=item B<-add-keep> + +add package(s) to the permanent list of excluded packages and exit. + +Can be used as '--add-keep pac1 --add-keep pac2' +or '--add-keep "pac1, pac2"' + +=item B<-del-keep> + +remove package(s) from the permanent list of excluded packages and exit. + +Can be used as '--add-keep pac1 --add-keep pac2' +or '--add-keep "pac1, pac2"' + +=back + +=head1 USAGE + +rpmorphan can be useful after a distribution upgrade, to remove packages forgotten +by the upgrade tool. It is interesting to use the options "-all -install-time +xx'. + +If you want to remove some recent tested packages, my advice is "-all -install-time -xx'. + +if you just want to clean your disk, use '-all -access-time xxx' + +=head1 FILES + +/var/lib/rpmorphan/keep : the permanent exclude list + +/tmp/rpmorphan.cache : cache file to store rpm query. The cache +file is common to all rpmorphan tools + +/var/log/rpmorphan.log : log of all deleted packages + +=head1 CONFIGURATION + +the program can read rcfile if some exists. +it will load in order + +/etc/rpmorphanrc + +~/.rpmorphanrc + +.rpmorphanrc + +In this file, + +# are comments, + +and parameters are stored in the following format : +parameter = value + +example : + +all = 1 + +curses = 1 + +=head1 DEPENDENCIES + +rpmorphan uses standard perl module in console mode. + +If you want to use the Tk graphical interface, you should install the Tk module +(perl-Tk rpm package). + +If you want to use the curses interface, you should install the Curses::UI perl module +( perl-Curses-UI rpm package). + +If you want to speed up rpmorphan, you should install the RPM2 perl module ( perl-RPM2 rpm package). +On mandriva, the URPM perl module (dependency from urpmi) will be used instead. + +=head1 EXAMPLES + +=over 8 + +=item display orphaned libraries + +rpmorphan + +=item display all orphaned packages in a curses interface + +rpmorphan --all --curses + +=item display orphaned packages, not used since one year + +rpmorphan --all -access-time +365 + +=item display all orphaned packages, installed in the last 10 days + +rpmorphan --all -install-time -10 + +=item display all orphaned packages, installed one month ago (or more) + +rpmorphan --all -install-time +30 + +=back + +=head1 BUGS AND LIMITATIONS + +Virtuals packages are not well (for now) taken in account. +Let's see an example : lilo and grub provide 'bootloader' virtual, which is +necessary for system boot. +if the 2 are installed, they will be shown all 2 as orphans (but you can not remove +the 2). +If you remove one of them, the other is now longer shown as orphan. + +the software can only work with one version of each software : +we only treat the first version seen + +=head1 INCOMPATIBILITIES + +not known + +=head1 DIAGNOSTICS + +to be written + +=head1 NOTES + +this program can be used as "normal" user to show orphans, +but you need to run it as supersuser (root) to remove packages +or apply changes in the permanent exclude list + +access-time and install-time options are "new" features, not available +in deborphan tool + +For Fedora users : +Yum offers a program called 'package-cleanup' which, +called with the '--leaves' option, can show quickly a list of +unused library packages. + +For Mandriva users : +the 'urpme --auto-orphans' command show quickly a a list of +unused library packages. + +=head1 ENVIRONMENT + +=over 8 + +=item RPMORPHAN_METHOD + +for experts only : allow to force the method used to get rpm +data. It can be set to URPM, RPM2 or 'basic' (for external rpm query) + +=back + +=head1 SEE ALSO + +=for man +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmusage\fR\|(1) +.PP +\fIrpmdep\fR\|(1) +.PP +\fIrpmduplicates\fR\|(1) +.PP +\fIrpmextra\fR\|(1) + +=for html +<a href="rpmusage.1.html">rpmusage(1)</a><br /> +<a href="rpmdep.1.html">rpmdep(1)</a><br /> +<a href="rpmduplicates.1.html">rpmduplicates(1)</a><br /> +<a href="rpmextra.1.html">rpmextra(1)</a><br /> + +=head1 EXIT STATUS + +should be allways 0 + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2006 by Eric Gerbier +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. + +=head1 AUTHOR + +Eric Gerbier + +you can report any bug or suggest to gerbier@users.sourceforge.net + +=cut diff --git a/rpmorphan.spec b/rpmorphan.spec new file mode 100644 index 0000000..d4edb60 --- /dev/null +++ b/rpmorphan.spec @@ -0,0 +1,271 @@ +%global _binary_filedigest_algorithm 1 +%global _source_filedigest_algorithm 1 + +%define VERSION 1.11 +Summary: List packages that have no dependencies (like deborphan) +# The Summary: line should be expanded to about here -----^ +Summary(fr): Liste les packages rpm orphelins (sans dependances) +Name: rpmorphan +Version: %{VERSION} +Release: 1 +Group: Applications/System +#Group(fr): (translated group goes here) +License: GPLv2+ + +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u}) +BuildArch: noarch + +URL: http://rpmorphan.sourceforge.net +Source: http://downloads.sourceforge.net/%{name}/%{name}-%{version}.tar.gz +# Following are optional fields +Packager: Eric Gerbier <gerbier@users.sourceforge.net> +#Distribution: Red Hat Contrib-Net +#Patch: src-%{version}.patch +#Prefix: /usr + +Requires: perl +Requires: rpm +Requires: logrotate +# rpmorphan can also use tk or curses but it is optionnal +# so this are not required but recommanded (when rpm allow this feature) +#Recommanded: perl-Tk +#Recommanded: perl-Curses-UI +# to speed up rpm query, use RPM2.pm perl package +#Recommanded: perl-RPM2 + +#Obsoletes: +#BuildRequires: + +%description +rpmorphan finds "orphaned"[2,5] packages on your system. It determines +which packages have no other packages depending on their installation, +and shows you a list of these packages. It intends to be clone of +deborphan Debian tools for rpm packages. + +It will try to help you to remove unused packages, for example: +* after a distribution upgrade +* when you want to suppress packages after some tests + +Several tools are also provided : +* rpmusage - display rpm packages last use date +* rpmdep - display the full dependency of an installed rpm package +* rpmduplicates - find packages with several version installed +* rpmextra - find installed packages not in distribution + +notes : + +For all users : +--------------- +[1] this rpm package will make rpmorphan work in command line mode. +If you want to use a graphical interface, you will need to install extra +rpm packages : perl-Tk or perl-Curses-UI. + +For Fedora users : +------------------ +[2] Note that orphan is used in the sense of Debian's deborphan, and +is NOT the same as Fedora orphaned packages which are packages that +have no current maintainer. + +[3] Yum offers a program called 'package-cleanup' which, +called with the '--leaves' option, can show quickly a list of +unused library packages. Rpmorphan is an extended version with +filters, gui, etc ... + +[4] rpmorphan can be speed up by installing the perl-RPM2 rpm package + +For Mandriva users : +-------------------- +[5] the 'urpme --auto-orphans' command show quickly a a list of +unused library packages. Rpmorphan is an extended version with +filters, gui, etc ... + +%description -l fr +Le logiciel rpmorphan liste les packages rpm qui n'ont plus de +dépendances avec les autres paquets installés sur votre système. +C'est un clone du logiciel deborphan de debian pour les packages rpm. + +Il peut vous aider pour supprimer les packages inutilisés, par exemple : +* après une montée de version système +* lors de la suppression de logiciels après des tests + +plusieurs outils sont également fournis : +* rpmusage : donne la date de la dernière utilisation d'un package +* rpmdep : fournit l'ensemble des dependances (recursive) d'un package +* rpmduplicates : cherche les packages qui ont plusieurs versions installées +* rpmextra : cherche les packages installés hors distribution + +notes : +Pour tous les utilisateurs +-------------------------- +[1] ce package rpm permet de faire fonctionner rpmorphan en mode +ligne de commande. Si vous voulez profiter d'une interface graphique, +vous devez installer des packages rpm suplementaires : perl-Tk ou perl-Curses-UI + +Pour les utilisateurs de Fedora : +--------------------------------- +[2] la notion d'orphelin est à prendre au sens du logiciel debian deborphan, et +non pas au sens général de Fedora (packages sans mainteneur). + +[3] Yum contient le programme 'package-cleanup', qui appelé avec l'option --leaves, +permet d'afficher rapidement les librairies inutilisées. Rpmorphan est une version +étendue, avec notamment les filtres, l'interface graphique. + +[4] rpmorphan peut être accéléré en installant le package rpm perl-RPM2 + +Pour les utilisateurs de Mandriva +--------------------------------- +[5] la commande 'urpme --auto-orphans' permet d'afficher rapidement les librairies inutilisées. +Rpmorphan est une version étendue, avec notamment les filtres, l'interface graphique. + +%prep +%setup -q +#%patch + +%build +# dummy : nothing to build +make %{?_smp_mflags} + + +%install +rm -rf $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT install + +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +%doc rpmorphan.lsm Authors COPYING Changelog NEWS Todo Readme Readme.fr rpmorphanrc.sample +%{_bindir}/rpmorphan-lib.pl +%{_bindir}/rpmorphan-curses-lib.pl +%{_bindir}/rpmorphan-tk-lib.pl +%{_bindir}/rpmorphan.pl +%{_bindir}/rpmorphan +%{_bindir}/grpmorphan +%{_bindir}/rpmusage.pl +%{_bindir}/rpmusage +%{_bindir}/rpmdep.pl +%{_bindir}/rpmdep +%{_bindir}/rpmduplicates.pl +%{_bindir}/rpmduplicates +%{_bindir}/rpmextra.pl +%{_bindir}/rpmextra +%ghost /var/log/rpmorphan.log +%dir /var/lib/rpmorphan +%config(noreplace) %attr(644, root, root) /var/lib/rpmorphan/keep +%dir /usr/lib/rpmorphan +%dir /usr/lib/rpmorphan/locale +%dir /usr/lib/rpmorphan/locale/en +%attr(644, root, root) /usr/lib/rpmorphan/locale/en/rpmorphan_trans.pl +%dir /usr/lib/rpmorphan/locale/fr_FR +%attr(644, root, root) /usr/lib/rpmorphan/locale/fr_FR/rpmorphan_trans.pl +%config(noreplace) /etc/logrotate.d/rpmorphan +%config(noreplace) /etc/rpmorphanrc +%doc %{_mandir}/man1/rpmorphan.1* +%doc %{_mandir}/man1/rpmusage.1* +%doc %{_mandir}/man1/rpmdep.1* +%doc %{_mandir}/man1/rpmduplicates.1* +%doc %{_mandir}/man1/rpmextra.1* + +%changelog +* Thu Aug 03 2012 Eric Gerbier <gerbier@users.sourceforge.net> 1.11 +- add suggests option +- add /etc/rpmorphanrc +- fix bug with space in filenames +- add env RPMORPHAN_METHOD for debugging + +* Thu Jul 8 2011 Eric Gerbier <gerbier@users.sourceforge.net> 1.10 +- bugfix eval for Curses (mandriva) +- fix bad version display +- clean all scripts +- reduce size/duplicated code in rpmorphan/rpmusage +- reduce menu length in curses gui (< 72) +- display package summary in a popup on S key + +* Mon Mar 21 2011 Eric Gerbier <gerbier@users.sourceforge.net> 1.9 +- fix perlcritic warnings +- add grpmorphan as link to rpmorphan -gui +- add rpmextra tool + +* Fri Apr 30 2010 <gerbier@users.sourceforge.net> 1.8-1 +- fix bug on rpmdep ( missing installtime in urpm mode) +- add help translation (gui) + +* Mon Mar 1 2010 <gerbier@users.sourceforge.net> 1.7-1 +- Curses is no more an rpm dependency +- add reload button + +* Thu Jan 14 2010 <gerbier@users.sourceforge.net> 1.6-1 +- curses window size is now variable to use full available size +- split core and gui code (new rpmorphan-curses-lib.pl rpmorphan-tk-lib.pl) + +* Thu Nov 20 2009 <gerbier@users.sourceforge.net> 1.5-1 +- add logrotate config +- remove all matching packages (same name) +- fix a bug on curses interface +- merge spec file with fedora one +- add info for fedora/mandriva users in package description +- french description is now in utf-8 + +* Tue Mar 31 2009 Richard W.M. Jones <rjones@redhat.com> - 1.4-5 +- Clarify the documentation, usage of 'orphan' and other terminology. + +* Wed Mar 25 2009 Richard W.M. Jones <rjones@redhat.com> - 1.4-4 +- Combine all %%doc lines into one. +- Remove Makefile from %%doc section. + +* Mon Jan 05 2009 <gerbier@users.sourceforge.net> 1.4-1 +- improve diagnostic if "rpm -e" return some comments + +* Mon Oct 19 2008 <gerbier@users.sourceforge.net> 1.3-1 +- fix a bug if exclude are set in config file (thanks Szymon Siwek) +- display number of deleted file + +* Mon Apr 7 2008 <gerbier@users.sourceforge.net> 1.2-1 +- write log to /var/log/rpmorphan.log +- add rpmdep.pl tool +- add rpmduplicate.pl tool + +* Thu Nov 22 2007 <gerbier@users.sourceforge.net> 1.1-1 +- (rpmorphan) add a default target : guess-lib +- (rpmorphan) test rpm delete +- fix warnings from perlcritic tool +- (rpmorphan) split too complex code (add read_one_rc, init_cache) +- (rpmorphan) add keep-file option, which allow to cutomize the keep file +- add rpmorphan-lib.pl to store common code for rpmorphan, rpmusage +- (rpmusage) add a default target : all +- use rpmlint to build a better rpm package + +* Wed Apr 27 2007 <gerbier@users.sourceforge.net> 1.0 +- fix bug with tk : core dump because update call before mainloop +- add w_ prefix for widgets variables +- add dry-run (simulation) mode +- (gui) add 3 counters : packages, orphans, selected +- (gui) display_status call debug sub +- new Liste_pac global variable +- (gui) change curses geometry to work on console +- (gui) recursive analysis +- improve virtuals : now check if how many package provide this one + +* Wed Mar 29 2007 <gerbier@users.sourceforge.net> 0.9 +- add curses interface +- test for root access before removing packages +- add optionnal configuration file +- recode debugging system +- apply conway coding rules +- add tk_get_current_elem sub +- add get_from_command_line sub +- add status widget +* Tue Mar 06 2007 <gerbier@users.sourceforge.net> 0.8 +- add simple graphical user iinterface (optionnal) +- remove global variable opt_verbose +- split code in functions : access_time_filter, read_rpm_data, search_orphans +* Tue Feb 28 2007 <gerbier@users.sourceforge.net> 0.4 +- add optionnal cache +* Tue Feb 03 2007 <gerbier@users.sourceforge.net> 0.3 +- add rpmusage tool +- add a link from rpmorphan.pl to rpmorphan +* Tue Jan 30 2007 <gerbier@users.sourceforge.net> 0.2 +- add permanent exclude list +* Tue Jan 23 2007 <gerbier@users.sourceforge.net> 0.1 +- Initial spec file diff --git a/rpmorphanrc.sample b/rpmorphanrc.sample new file mode 100644 index 0000000..db7d300 --- /dev/null +++ b/rpmorphanrc.sample @@ -0,0 +1,54 @@ +# configuration file exemple for rpmorphan +# $Id: rpmorphan-1.11 | rpmorphanrc.sample | Tue Jul 31 17:13:56 2012 +0200 | Eric Gerbier $ +# it can be used as +# - host (general) configuration file as /etc/rpmorphanrc +# - home (user) configuration file as ~/.rpmorphanrc +# - local configuration file as ./.rpmorphanrc +# +# the program will read them all in order from host to local +# +# syntaxe : +# - all line beginning with "#" are comments +# - all parameter have the same name as command line arguments, without dash +# - 0 means false/no, 1 means true/yes + +# for example, to use rpmorphan instead of rpmorphan --all --curses +# you have to use +# all = 1 +# curses = 1 +# + +## list of all available parameters with the default value +#help = 0 +#man = 0 +#verbose = 0 +#dry-run = 0 +#version = 0 +#fullalgo = 0 +#suggests = 0 +#all = 0 +#guess-perl = 0 +#guess-python = 0 +#guess-pike = 0 +#guess-ruby = 0 +#guess-common = 0 +#guess-data = 0 +#guess-doc = 0 +#guess-dev = 0 +#guess-lib = 0 +#guess-all = 0 +#guess-custom = +#package= +#exclude= +#install-time = +#access-time = +#add-keep = +#del-keep = +#list-keep = +#zero-keep = +#keep-file = /var/lib/rpmorphan/keep +#use-cache = 0 +#clear-cache = 0 +#gui = 0 +#tk = 0 +#curses = 0 diff --git a/rpmusage.1 b/rpmusage.1 new file mode 100644 index 0000000..ba53b75 --- /dev/null +++ b/rpmusage.1 @@ -0,0 +1,358 @@ +.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "RPMUSAGE 1" +.TH RPMUSAGE 1 "2012-08-01" "perl v5.14.2" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +rpmusage \- display rpm packages last use +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +rpmusage will display for each package, the last date it was used (in days). It can be used +to find unused packages. It use the atime field of all package's files to do this job. +Note : as it scan all files inodes, the run may be long ... +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +rpmusage.pl [options] [targets] +.PP +options: +.PP +.Vb 3 +\& \-help brief help message +\& \-man full documentation +\& \-V, \-\-version print version +\& +\& \-verbose verbose +\& \-fullalgo force full algorythm +\& \-use\-cache use cache to avoid rpm query +\& \-clear\-cache remove cache file +\& +\& \-exclude pac exclude pac from results +\& \-install\-time +/\-d apply on packages which are installed before (after) d days +\& \-access\-time d apply on packages which are not been accessed for d days (slow) +.Ve +.PP +targets: +.PP +.Vb 10 +\& \-package pac search last access on pac package +\& \-all apply on all packages (this is the default) +\& \-guess\-perl apply on perl packages +\& \-guess\-python apply on python packages +\& \-guess\-pike apply on pike packages +\& \-guess\-ruby apply on ruby packages +\& \-guess\-common apply on common packages +\& \-guess\-data apply on data packages +\& \-guess\-doc apply on documentation packages +\& \-guess\-dev apply on development packages +\& \-guess\-lib apply on library packages +\& \-guess\-all apply all \-guess\-* options (perl, python ...) +\& \-guess\-custom regex apply the given regex to filter to package\*(Aqs names to filter the output +.Ve +.SH "REQUIRED ARGUMENTS" +.IX Header "REQUIRED ARGUMENTS" +it can be used without any argument, and then will apply on all packages +.SH "OPTIONS" +.IX Header "OPTIONS" +.IP "\fB\-help\fR" 8 +.IX Item "-help" +Print a brief help message and exits. +.IP "\fB\-man\fR" 8 +.IX Item "-man" +Print the manual page and exits. +.IP "\fB\-version\fR" 8 +.IX Item "-version" +Print the program release and exit. +.IP "\fB\-verbose\fR" 8 +.IX Item "-verbose" +The program works and print debugging messages. +.IP "\fB\-use\-cache\fR" 8 +.IX Item "-use-cache" +the rpm query may be long (10 to 30 s). If you will run an rpmorphan tool +several time, this option will allow to gain a lot of time : +it save the rpm query on a file cache (first call), then +use this cache instead quering rpm (others calls). +.IP "\fB\-clear\-cache\fR" 8 +.IX Item "-clear-cache" +to remove cache file. Can be used with \-use\-cache to write +a new cache. +.IP "\fB\-exclude\fR" 8 +.IX Item "-exclude" +this option will specify the packages to exclude from the output. +Can be used as '\-\-exclude pac1 \-\-exclude pac2' +or '\-\-exclude \*(L"pac1, pac2\*(R"' +.IP "\fB\-install\-time\fR" 8 +.IX Item "-install-time" +install-time is a filter on the period from the package installation date to now (in days). +if set positive, it only allow packages installed before x days. +if set negative, it only allow packages installed since x days. +.IP "\fB\-access\-time\fR" 8 +.IX Item "-access-time" +access-time is designed to filter packages which have not been used since x days. +.Sp +be careful : this option will slow the program +.IP "\fB\-fullalgo\fR" 8 +.IX Item "-fullalgo" +for a small list of packages, rpmusage use a different quicker methode : rpm \-e \-\-test +.Sp +this option can be used to force the use of the full algo +.IP "\fB\-package\fR" 8 +.IX Item "-package" +search if the given package(s) is(are) orphaned. +Can be used as '\-\-package pac1 \-\-package pac2' +or '\-\-package \*(L"pac1, pac2\*(R"' +.IP "\fB\-all\fR" 8 +.IX Item "-all" +apply on all installed packages. The output should be interpreted. +For example lilo or grub are orphaned packages, but are necessary +to boot ... +.Sp +the \-install\-time and \-access\-time options may be useful to filter the list +.IP "\fB\-guess\-perl\fR" 8 +.IX Item "-guess-perl" +This option tries to find perl modules. It tries to match \*(L"^perl\*(R" +.IP "\fB\-guess\-python\fR" 8 +.IX Item "-guess-python" +This option tries to find python modules. It tries to match \*(L"^python\*(R" +.IP "\fB\-guess\-pike\fR" 8 +.IX Item "-guess-pike" +This option tries to find pike modules. It tries to match \*(L"^pike\*(R" +.IP "\fB\-guess\-ruby\fR" 8 +.IX Item "-guess-ruby" +This option tries to find ruby modules. It tries to match \*(L"^ruby\*(R" +.IP "\fB\-guess\-common\fR" 8 +.IX Item "-guess-common" +This option tries to find common packages. It tries to match \*(L"\-common$\*(R" +.IP "\fB\-guess\-data\fR" 8 +.IX Item "-guess-data" +This option tries to find data packages. It tries to match \*(L"\-data$\*(R" +.IP "\fB\-guess\-doc\fR" 8 +.IX Item "-guess-doc" +This option tries to find documentation packages. It tries to match \*(L"\-doc$\*(R" +.IP "\fB\-guess\-data\fR" 8 +.IX Item "-guess-data" +This option tries to find data packages. It tries to match \*(L"\-data$\*(R" +.IP "\fB\-guess\-dev\fR" 8 +.IX Item "-guess-dev" +This option tries to find development packages. It tries to match \*(L"\-devel$\*(R" +.IP "\fB\-guess\-lib\fR" 8 +.IX Item "-guess-lib" +This option tries to find library packages. It tries to match \*(L"^lib\*(R" +.IP "\fB\-guess\-all\fR" 8 +.IX Item "-guess-all" +This is a short to tell : Try all of the above (perl, python ...) +.IP "\fB\-guess\-custom\fR" 8 +.IX Item "-guess-custom" +this will allow you to specify your own filter. for exemple \*(L"^wh\*(R" +will match whois, whatsnewfm ... +.SH "USAGE" +.IX Header "USAGE" +display all packages in access date order +.PP +rpmusage.pl | sort \-k 2 \-n +.PP +only ask for perl packages +.PP +rpmusage.pl \-\-guess\-perl | sort \-k 2 \-n +.PP +to read the output : +\&\*(L"python-pexpect 97 on /usr/share/doc/python\-pexpect\*(R" +means the package was last used 97 days ago and the more recently used file was the +/usr/share/doc/python\-pexpect file +.SH "FILES" +.IX Header "FILES" +/tmp/rpmorphan.cache : cache file to store rpm query. The cache +file is common to all rpmorphan tools +.SH "DEPENDENCIES" +.IX Header "DEPENDENCIES" +rpmusage uses only standard perl module. +.PP +but it needs the rpm command tool. +.SH "BUGS AND LIMITATIONS" +.IX Header "BUGS AND LIMITATIONS" +the software can only work with one version of each software : +we only treat the first version seen +.SH "CONFIGURATION" +.IX Header "CONFIGURATION" +the program can read rcfile if some exists. +it will load in order +.PP +/etc/rpmorphanrc +.PP +~/.rpmorphanrc +.PP +\&.rpmorphanrc +.PP +In this file, +.PP +# are comments, +.PP +and parameters are stored in the following format : +parameter = value +.PP +example : +.PP +all = 1 +.PP +curses = 1 +.SH "INCOMPATIBILITIES" +.IX Header "INCOMPATIBILITIES" +not known +.SH "DIAGNOSTICS" +.IX Header "DIAGNOSTICS" +to be written +.SH "NOTES" +.IX Header "NOTES" +this program should be used as root superuser, because it needs to access +(in read mode) to all files +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmorphan\fR\|(1) +.PP +\fIrpmdep\fR\|(1) +.PP +\fIrpmduplicates\fR\|(1) +.PP +\fIrpmextra\fR\|(1) +.SH "EXIT STATUS" +.IX Header "EXIT STATUS" +should be allways 0 +.SH "LICENSE AND COPYRIGHT" +.IX Header "LICENSE AND COPYRIGHT" +Copyright (C) 2006 by Eric Gerbier +This program is free software; you can redistribute it and/or modify +it under the terms of the \s-1GNU\s0 General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +.SH "AUTHOR" +.IX Header "AUTHOR" +Eric Gerbier +.PP +you can report any bug or suggest to gerbier@users.sourceforge.net diff --git a/rpmusage.pl b/rpmusage.pl new file mode 100755 index 0000000..5dd71f9 --- /dev/null +++ b/rpmusage.pl @@ -0,0 +1,567 @@ +#!/usr/bin/perl +############################################################################### +# rpmusage.pl +# +# Copyright (C) 2006 by Eric Gerbier +# Bug reports to: gerbier@users.sourceforge.net +# $Id: rpmorphan-1.11 | rpmusage.pl | Tue Jul 5 11:28:04 2011 +0000 | gerbier $ +# +# 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. +# +############################################################################### +use strict; +use warnings; + +use English '-no_match_vars'; + +use Getopt::Long; # arg analysis +use Pod::Usage; # man page +use Data::Dumper; # debug + +# library +use File::Basename; +my $dirname = dirname($PROGRAM_NAME); +require $dirname . '/rpmorphan-lib.pl' + or die "problem to load rpmorphan-lib: $ERRNO\n"; + +# the code should use no "print" calls +# but instead debug, warning, info calls +######################################################### +sub solve($$$$) { + my $name = shift @_; # package name + my $rh_files = shift @_; # general hash of package files + my $access_time = shift @_; # option access-time + my $now = shift @_; # current time + + debug("solve $name"); + + my ( $last_time, $file ) = get_last_access( $rh_files->{$name} ); + if ( !$file ) { + debug("skip $name : no files"); + + # can not give a time access : no files ! + return; + } + my $diff = diff_date( $now, $last_time ); + if ($access_time) { + if ( $diff > $access_time ) { + info("$name $diff on $file"); + } + else { + debug("skip package $name : too recent access ($diff days)"); + } + } + else { + info("$name $diff on $file"); + } + + return; +} +######################################################### +sub quicksolve($$$) { + my $name = shift @_; # package name + my $access_time = shift @_; # option access-time + my $now = shift @_; # current time + + debug("(quicksolve) : $name"); + + my $cmd = 'rpm -ql '; + + ## no critic (ProhibitBacktickOperators); + my @files = `$cmd $name`; + ## use critic; + chomp @files; + + # change structure to call generic call + my %files; + push @{ $files{$name} }, @files; + + solve( $name, \%files, $access_time, $now ); + return; +} +########################################################## +# read all rpm database is long, +# for a small number of packages, it is faster to use +# another algo +sub quick_run($$$) { + my $rh_opt = shift @_; + my $rh_excluded = shift @_; + my $access_time = shift @_; + + debug('quick_run'); + + #allow comma separated options on tags option + my @opt_package = get_from_command_line( @{ $rh_opt->{'package'} } ); + $rh_opt->{'package'} = \@opt_package; + + if ( !is_set( $rh_opt, 'fullalgo' ) ) { + ## no critic (ProhibitMagicNumbers) + my $max_packages = 5; + if ( $#opt_package <= $max_packages ) { + + # use quicksolve + my $now = time; + PAC: foreach my $pac (@opt_package) { + if ( exists $rh_excluded->{$pac} ) { + debug("skip $pac : excluded"); + next PAC; + } + quicksolve( $pac, $access_time, $now ); + } + exit; + } + else { + debug('too much package for quicksolve algo'); + } + } + else { + debug('full algo option'); + } + return; +} +########################################################## +# is to be defined because rpmorphan-lib need it +sub display_status($) { + debug( shift @_ ); + return; +} +######################################################### +# +# main +# +######################################################### +my $version = '0.5'; + +my $opt_verbose = 0; +my $opt_help; +my $opt_man; +my $opt_version; +my $opt_fullalgo; +my $opt_use_cache; +my $opt_clear_cache; + +my $opt_all; + +my $opt_guess_perl; +my $opt_guess_python; +my $opt_guess_pike; +my $opt_guess_ruby; +my $opt_guess_common; +my $opt_guess_data; +my $opt_guess_doc; +my $opt_guess_dev; +my $opt_guess_lib; +my $opt_guess_all; +my $opt_guess_custom; + +my @opt_package; +my @opt_exclude; +my $opt_install_time; +my $opt_access_time; + +my %opt = ( + 'help' => \$opt_help, + 'man' => \$opt_man, + 'verbose' => \$opt_verbose, + 'version' => \$opt_version, + 'fullalgo' => \$opt_fullalgo, + 'all' => \$opt_all, + 'guess-perl' => \$opt_guess_perl, + 'guess-python' => \$opt_guess_python, + 'guess-pike' => \$opt_guess_pike, + 'guess-ruby' => \$opt_guess_ruby, + 'guess-common' => \$opt_guess_common, + 'guess-data' => \$opt_guess_data, + 'guess-doc' => \$opt_guess_doc, + 'guess-dev' => \$opt_guess_dev, + 'guess-lib' => \$opt_guess_lib, + 'guess-all' => \$opt_guess_all, + 'guess-custom' => \$opt_guess_custom, + 'package' => \@opt_package, + 'exclude' => \@opt_exclude, + 'install-time' => \$opt_install_time, + 'access-time' => \$opt_access_time, + 'use-cache' => \$opt_use_cache, + 'clear-cache' => \$opt_clear_cache, +); + +Getopt::Long::Configure('no_ignore_case'); +GetOptions( + \%opt, 'help|?', 'man', 'verbose', + 'fullalgo!', 'version|V', 'all!', 'guess-perl!', + 'guess-python!', 'guess-pike!', 'guess-ruby!', 'guess-common!', + 'guess-data!', 'guess-doc!', 'guess-dev!', 'guess-lib!', + 'guess-all!', 'guess-custom=s', 'package=s', 'exclude=s', + 'install-time=i', 'access-time=i', 'use-cache!', 'clear-cache' +) or pod2usage(2); + +init_debug($opt_verbose); + +if ($opt_help) { + pod2usage(1); +} +elsif ($opt_man) { + pod2usage( -verbose => 2 ); +} +elsif ($opt_version) { + print_version($version); + exit; +} + +ana_guess( \%opt ); + +my %excluded; +build_excluded( \%opt, \%excluded ); + +# install-time and access-time options are only available with the full algo +if ( ($opt_install_time) or ($opt_access_time) ) { + debug('forced full algo'); + $opt_fullalgo = 1; +} + +# if only few packages, the big algo is not necessary +# quicksolve algo will be faster +if (@opt_package) { + + quick_run( \%opt, \%excluded, $opt_access_time ); +} + +# hash structures to be filled with rpm query +my %files; +my %install_time; + +# not really used for new +my %provides; +my %depends; +my %virtual; +my %requires; + +# phase 1 : get data and build structures +read_rpm_data( \%opt, \%provides, \%install_time, \%files, \%depends, \%virtual, + \%requires ); + +# phase 2 : filter +# if a package is set, it will be first used, then it will use in order -all, then (guess*) +# (see filter sub) +debug('2 : filter'); +my @liste_pac; +if (@opt_package) { + @liste_pac = @opt_package; +} +else { + @liste_pac = rpmfilter( \%opt, \%files ); +} + +# needed by access-time and install-time options +my $now = time; + +# phase 3 : solve problem +debug('3 : solve'); +PACKAGE: foreach my $pac (@liste_pac) { + if ( exists $excluded{$pac} ) { + debug("skip $pac : excluded"); + next PACKAGE; + } + + if ($opt_install_time) { + my $diff = diff_date( $now, $install_time{$pac} ); + if ( $opt_install_time > 0 ) { + if ( $diff < $opt_install_time ) { + debug("skip $pac : too recent install ($diff days)"); + next; + } + } + elsif ( $diff > -$opt_install_time ) { + debug("skip $pac : too old install ($diff days)"); + next; + } + } + solve( $pac, \%files, $opt_access_time, $now ); +} + +__END__ + +=head1 NAME + +rpmusage - display rpm packages last use + +=head1 DESCRIPTION + +rpmusage will display for each package, the last date it was used (in days). It can be used +to find unused packages. It use the atime field of all package's files to do this job. +Note : as it scan all files inodes, the run may be long ... + +=head1 SYNOPSIS + +rpmusage.pl [options] [targets] + +options: + + -help brief help message + -man full documentation + -V, --version print version + + -verbose verbose + -fullalgo force full algorythm + -use-cache use cache to avoid rpm query + -clear-cache remove cache file + + -exclude pac exclude pac from results + -install-time +/-d apply on packages which are installed before (after) d days + -access-time d apply on packages which are not been accessed for d days (slow) + +targets: + + -package pac search last access on pac package + -all apply on all packages (this is the default) + -guess-perl apply on perl packages + -guess-python apply on python packages + -guess-pike apply on pike packages + -guess-ruby apply on ruby packages + -guess-common apply on common packages + -guess-data apply on data packages + -guess-doc apply on documentation packages + -guess-dev apply on development packages + -guess-lib apply on library packages + -guess-all apply all -guess-* options (perl, python ...) + -guess-custom regex apply the given regex to filter to package's names to filter the output + +=head1 REQUIRED ARGUMENTS + +it can be used without any argument, and then will apply on all packages + +=head1 OPTIONS + +=over 8 + +=item B<-help> + +Print a brief help message and exits. + +=item B<-man> + +Print the manual page and exits. + +=item B<-version> + +Print the program release and exit. + +=item B<-verbose> + +The program works and print debugging messages. + +=item B<-use-cache> + +the rpm query may be long (10 to 30 s). If you will run an rpmorphan tool +several time, this option will allow to gain a lot of time : +it save the rpm query on a file cache (first call), then +use this cache instead quering rpm (others calls). + +=item B<-clear-cache> + +to remove cache file. Can be used with -use-cache to write +a new cache. + +=item B<-exclude> + +this option will specify the packages to exclude from the output. +Can be used as '--exclude pac1 --exclude pac2' +or '--exclude "pac1, pac2"' + +=item B<-install-time> + +install-time is a filter on the period from the package installation date to now (in days). +if set positive, it only allow packages installed before x days. +if set negative, it only allow packages installed since x days. + +=item B<-access-time> + +access-time is designed to filter packages which have not been used since x days. + +be careful : this option will slow the program + +=item B<-fullalgo> + +for a small list of packages, rpmusage use a different quicker methode : rpm -e --test + +this option can be used to force the use of the full algo + +=item B<-package> + +search if the given package(s) is(are) orphaned. +Can be used as '--package pac1 --package pac2' +or '--package "pac1, pac2"' + +=item B<-all> + +apply on all installed packages. The output should be interpreted. +For example lilo or grub are orphaned packages, but are necessary +to boot ... + +the L<-install-time> and L<-access-time> options may be useful to filter the list + +=item B<-guess-perl> + +This option tries to find perl modules. It tries to match "^perl" + +=item B<-guess-python> + +This option tries to find python modules. It tries to match "^python" + +=item B<-guess-pike> + +This option tries to find pike modules. It tries to match "^pike" + +=item B<-guess-ruby> + +This option tries to find ruby modules. It tries to match "^ruby" + +=item B<-guess-common> + +This option tries to find common packages. It tries to match "-common$" + +=item B<-guess-data> + +This option tries to find data packages. It tries to match "-data$" + +=item B<-guess-doc> + +This option tries to find documentation packages. It tries to match "-doc$" + +=item B<-guess-data> + +This option tries to find data packages. It tries to match "-data$" + +=item B<-guess-dev> + +This option tries to find development packages. It tries to match "-devel$" + +=item B<-guess-lib> + +This option tries to find library packages. It tries to match "^lib" + +=item B<-guess-all> + +This is a short to tell : Try all of the above (perl, python ...) + +=item B<-guess-custom> + +this will allow you to specify your own filter. for exemple "^wh" +will match whois, whatsnewfm ... + +=back + +=head1 USAGE + +display all packages in access date order + +rpmusage.pl | sort -k 2 -n + +only ask for perl packages + +rpmusage.pl --guess-perl | sort -k 2 -n + +to read the output : +"python-pexpect 97 on /usr/share/doc/python-pexpect" +means the package was last used 97 days ago and the more recently used file was the +/usr/share/doc/python-pexpect file + +=head1 FILES + +/tmp/rpmorphan.cache : cache file to store rpm query. The cache +file is common to all rpmorphan tools + +=head1 DEPENDENCIES + +rpmusage uses only standard perl module. + +but it needs the rpm command tool. + +=head1 BUGS AND LIMITATIONS + +the software can only work with one version of each software : +we only treat the first version seen + +=head1 CONFIGURATION + +the program can read rcfile if some exists. +it will load in order + +/etc/rpmorphanrc + +~/.rpmorphanrc + +.rpmorphanrc + +In this file, + +# are comments, + +and parameters are stored in the following format : +parameter = value + +example : + +all = 1 + +curses = 1 + +=head1 INCOMPATIBILITIES + +not known + +=head1 DIAGNOSTICS + +to be written + +=head1 NOTES + +this program should be used as root superuser, because it needs to access +(in read mode) to all files + +=head1 SEE ALSO + +=for man +\fIrpm\fR\|(1) for rpm call +.PP +\fIrpmorphan\fR\|(1) +.PP +\fIrpmdep\fR\|(1) +.PP +\fIrpmduplicates\fR\|(1) +.PP +\fIrpmextra\fR\|(1) + +=for html +<a href="rpmorphan.1.html">rpmorphan(1)</a><br /> +<a href="rpmdep.1.html">rpmdep(1)</a><br /> +<a href="rpmduplicates.1.html">rpmduplicates(1)</a><br /> +<a href="rpmextra.1.html">rpmextra(1)</a><br /> + +=head1 EXIT STATUS + +should be allways 0 + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2006 by Eric Gerbier +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. + +=head1 AUTHOR + +Eric Gerbier + +you can report any bug or suggest to gerbier@users.sourceforge.net + +=cut |