diff options
83 files changed, 15755 insertions, 0 deletions
diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..96e45911 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..c6b9e42d --- /dev/null +++ b/ChangeLog @@ -0,0 +1,268 @@ +VERSION DESCRIPTION +----------------------------------------------------------------------------- +2.9.5 - bugfix: missing files after re-ordering packages wrt + deps with --upgrade + - added "Repository" line to -Si output + - patch from Tommi Rantala to fix trim() behaviour with + empty or whitespace-only strings + - fixed removal order when using -Rc or -Rs +2.9.4 - fixed a bug that was introduced from another bugfix :-/ +2.9.3 - fixed a couple manpage typos + - added --ignore to -S operations, works just like IgnorePkg + - respect IgnorePkg list when pulling in dependencies + - numerous memleak fixes + - some code changes to improve customizability/branding + - Makefile fix for nonstandard lib search paths (Kevin Piche) + - fixed the leftover directories in /tmp + - speed improvement patches from Tommi Rantala +2.9.2 - bugfix for 2.9.1 +2.9.1 - --refresh now only downloads fresh packages lists if they've + been updated (currently only works with FTP) +2.9 - Improved -Rs functionality -- pacman now tracks why a package + is installed: explicitly, or as a dependency for another + package. -Rs will only remove dependencies that were not + explicitly installed. + - Added compressed package size to sync DBs -- shows the total + size of packages before downloading + - Patch from Tommi Rantala: + - Allow --info and --list together in queries + - Patch from Kevin Piche: + - Use list_add_sorted() with -Sg + - Patch from Hegedus Marton Csaba: + - Better manpage compression + - Added checks for additional hyphens in package versions + - mktemp was failing if %pmo_root%/tmp was missing -- fixed +2.8.4 - Added updatesync script from Jason Chu + - Changed the pacman binary to be dynamically linked + - Included a pacman.static binary as well + - Added fakeroot checks when seeing if we're root + - Fixed makepkg to use 'tail -n 1' instead of 'tail -1' + - Added patch from Kevin Piche: + - Cleanup db_loadpkgs(), add list_add_sorted() + - Fixed a memory leak in db_find_conflicts() +2.8.3 - Fixed a little makepkg bug with bash 3.0 + - Fixed resolvedeps to always prefer literals over provisios + - Added --config option to specify an alternate config file + - Added "Include" directive to include repositories from + config files (inspired by Michael Baehr's patch) + - Added patch from Jason Chu: + - Even smarter file-conflict checking +2.8.2 - Fixed a segfault bug in file-conflict checks + - Made --noconfirm actually work. Go me. +2.8.1 - Added a HoldPkg option in pacman.conf, for the more + exploratory users who run things like "pacman -R pacman". It + will ask for confirmation before removing any packages listed + in the HoldPkg list + - Added a --noconfirm switch for use with script automation + - Modified dependency resolution to prefer packages explicitly + requested on the cmdline instead of those pulled in by + resolvedeps(). Example, if neither "xorg" nor "xfree86" is + installed and "blackbox xfree86" is requested, "xfree86" will + be used instead of "xorg" + - Added patch from Jason Chu: + - Smarter file-conflict checking with symlinked paths and + with files that move from one package to another +2.8 - Bugfixes: + - #861: file:/// urls not handled properly with XferCommand + - #1003: set umask before scriptlet calls + - #1027: download problems with http urls using -U/-A + - #1044: segfaults when using -Rs + - #863: "missing post_remove" errors with some packages + - #875: detect low disk space properly + - #986: makepkg -e doesn't validate files + - #1010: add -j option to makepkg + - #1028: make pacman -Sp runnable as non-root + - added pre_install and pre_upgrade scriptlet support + - added an "Architecture" field in the package meta-data + - added patch from Aurelien Foret which improves performance + adding or removing packages + - added implementation of GNU's strverscmp function for better + portability + - added explicit unlink() calls when --force is used, which + prevents those nasty "Text file busy" errors when you + force-upgrade something like pacman or glibc. +2.7.9 - added the "force" option to packages, so --sysupgrade can + downgrade packages when it needs to +2.7.8 - added post_remove scriptlet support + - added -Qs option (bug #854) + - a provisio does not imply conflict, to make a provisio target + conflict with anything else that provides the same thing, you + can now do this by specifying the provisio target as both a + provides and a conflict, eg: + conflicts=('x-server') + provides=('x-server') + - cleaned up the download progress bar a bit + - added %o parameter to XferCommand so wget can resume properly + - fixed a segfault in downloadfiles() (bug #787) + - patches from Oliver Burnett-Hall + - gensync uses a better temp dir (bug #774) + - PKGDEST can be set in makepkg.conf (bug #783) + - patches from Aurelien Foret + - segfault fix, couple memory leaks + - more sanity checks in "provides" searches + - fixed a little display bug in the progress bar + - made -Qip look like -Qi + - -Sc now removes OLD packages from cache, use -Scc for all +2.7.7 - added an XferCommand directive that will make pacman use an + external download utility like wget + - added a license field to package meta-data + - add url support to -A and -U operations (download packages) + - -Ss now searches thru provides fields + - added --dbonly option to -R +2.7.6 - added --print-uris option + - fixed an http download bug (bug #667) + - fixed a segfault related to replaces/conflicts handling +2.7.5 - "replaces" packages were ignoring IgnorePkg in pacman.conf + - fixed another bug in conflict handling + - found an out-dated reference to /usr/abs, fixed + - added a --recursive option for package removal, which removes + all deps of a target that aren't required by other packages +2.7.4 - fixed a bug in conflict handling, where installing a + conflicting package would fail even if the new package + "provided" it + - if pacman sees a newer version of itself during an upgrade, + it will ask to install itself first, then be re-run to upgrade + the other packages. + - You can now use the --info option with --sync to display an + uninstalled package's dependency info. + - Added a sane umask before db writes + - buffer overflow fix (bug #442) +2.7.3 - makepkg not longer strips files with .exe or .dll extensions + - Added Aurelien's patch: + - proxy support (no authentication yet) + - HTTP/1.1 support + - an improved progress bar with transfer rates and ETA + - cleaned up warning output a bit +2.7.2 - Supressed "No such file" messages during stripping + - Removed extra newlines in /var/log/pacman.log + - Added a --noextract option to makepkg to skip source extraction +2.7.1 - Fixed a couple obscure segfaults + - LogFiles were logging incorrect dates - fixed + - Cleaned up md5sum output in makepkg -g + - Added (optional) colorized output messages to makepkg + - Renamed the "stable" repo to "release" in pacman.conf + - Renamed the "unofficial" repo to "extra" in pacman.conf +2.7 - Added build-time dependencies to makepkg (Jason Chu) + - Added md5sum integrity checking to packages in --sync + mode (Aurelien Foret) + - Memory leak fixes (Aurelien Foret) + - Added CARCH variable to makepkg.conf for use in PKGBUILDs + - Added LogFile option for direct-to-file logging + - Added -Qii handling to show modified config files + - Allow --sync targets to specify an explicit repository to + sync from (eg, pacman -S current/patch) +2.6.4 - Altered pacman_upgrade() to allow a package to replace itself +2.6.3 - A couple memory fixes in the new replaces code +2.6.2 - Fixed a memory cleanup bug + - Aurelien's patch: + - bug #159 implemented (for -S and -R) + - fixed a bug with pacman -Sg (pacman was browsing only one + db to get groups) + - fixed a bug with list_merge() + - fixed some MLK (in dumppkg() and with "-Qi --orphans") + - now "pacman -Sg" only displays groups (without content) + whereas "pacman -Sg target1 target2" displays groups + target1 and target2 with content +2.6.1 - Added http download support (Aurelien Foret) + - Improved makepkg's --builddeps behaviour when called via + makeworld + - makepkg's md5 validation now occurs before source extraction + - makepkg delays fakeroot entry until after option parsing + - Fixed an argument-passing bug in fakeroot + - Modified pacman's behaviour wrt provides -- it now allows + multiple packages to be installed, even if they provide the + same thing (they were treated as conflicts before) +2.6 - Added group handling, so one can run 'pacman -S kde' and + install all files from the KDE group + - Fixed a duplication bug in cascade package removal + - Added support for virtual provisions with "provides" tags + - When conflicts are encountered, pacman now offers the chance + to remove the conflicting packages (provides or literals) + - Added support for renamed/combined packages with a "replaces" + tag + - Added --nostrip option to makepkg + - Improved --search to list all packages from all repos when + a search term is omitted + - Added logging support through syslog() + - Added fakeroot support to makepkg (RomanK) + - Added MD5sum generation/validation to makepkg (RomanK) + - Fixed a progress bar bug (Aurelien Foret) + - Sorted makepkg's .FILELISTs (Aurelien Foret) + - Targets are now re-ordered w.r.t. dependencies when + using -A/-U + - Modified --search to work when called as -Sys + - Modified abs to use ABS_ROOT from /etc/abs/abs.conf (Aurelien) + - Other bug fixes +2.5.1 - Minor bug fixes +2.5 - Added an URL tag to package info + - Sped up package load times by about 500% by introducing + a .FILELIST into the package + - Renamed the install scriptlet from ._install to .INSTALL + - Added patch from Aurlien Foret: + - Better lock handling (RW and RO) + - Sorted package order in -Qi's dependency lists + - Added a DBPath option to pacman.conf + - Fixed memory leaks + - Added the --nodeps option to -S +2.4.1 - Fixed a bug in makepkg's option parsing +2.4 - Added getopt-style options to makeworld + - Added -w <destdir> to makepkg + - makeworld now properly handles packages with --builddeps + - Added patches from Aurelien Foret: + - Cascading package removals. -Rc will remove a package and + all packages that require it + - Support for getopt-style options in makepkg + - the REQUIREDBY field is now updated when a package is + removed with --nodeps, then re-installed. + - Avoids duplicate dependency checks with sync + - Added a "NoPassiveFtp" option in pacman.conf + - Improvements to the --query listings +2.3.2 - Added patches from Aurelien Foret: + - FTP resume feature + - removed the hit-^C-and-delete-the-downloading-file safety + - IgnorePkg option in pacman.conf + - FTPAGENT setting in makepkg.conf + - Added --cleancache option to makepkg +2.3.1 - Fixed the progress bar overflow + - Pacman does not ask "Are you sure" when you use --downloadonly + - Switched up a couple makepkg options to be more consistent + with pacman's options + - If you ^C out of a file download, the package will now be + removed from the cache directory +2.3 - The beginnings of source-side dependency resolution, makepkg + can now either A) download/install missing deps with pacman + -S; or B) find missing deps in the /usr/abs tree and + build/install them. + - Added a --nodeps option to makepkg + - Improved the --search output +2.2 - More bugfixes + - Added --downloadonly switch to --sync +2.1 - Lots of bugfixes + - Added support for multiple respositories + - Improved the config file layout + - Improved dependency resolution and sorting +2.0 - Added dependency functionality + - Completely new database format, similar to FreeBSD + - Better internal data structures + - Merged pacsync functionality into pacman + - Now uses libftp for all file retrieval (currently only ftp) + - Automatic dependency resolution, a la apt-get + - Moved config stuff from /etc/pacsync.conf to /etc/pacman.conf + - Much better backup file handling, now uses md5 hashes a la rpm +1.23 - Added install/upgrade/remove scripting control +1.22 - Some manpage typo fixes + - Added --root switch to pacsync + - Added --help and ability to specify a PKGBUILD to makepkg + - Switched default downloader to snarf +1.21 - Added better backup control -- upgrade/add and remove + do different things with the -n switch +1.2 - Added wildcard handling + - Added man pages for makepkg and pacsync + - Added the pacsync utility for remote file fetching/sync +1.1 - Fixed some string-handling bugs + - Added better handling of configuration files and the like. + If "file" is about to be removed, but it is designated to + backed up, then it will be copied to "file.save" + - Changed db_find_conflicts() to ignore directories +1.0 - Initial Release + @@ -0,0 +1,288 @@ +==========================================================================
+README:
+
+Overview and internals of the ALPM library and the PACMAN frontend.
+
+This document describes the state of the implementation before its CVS
+import.
+At this stage, the code is in pre-alpha state, but the design should not
+change that much.
+There's still need for some work to get the current code properly working.
+The tag "ORE" was added in various places in the code, each time a point
+remains unclear or is not yet implemented.
+
+==========================================================================
+
+
+ALPM library overview & internals
+=================================
+
+Here is a list of the main objects and files from the ALPM (i.e. Arch
+Linux Package Management) library.
+This document, whilst not exhaustive, also indicates some limitations
+(on purpose, or sometimes due to its poor design) of the library at the
+present time.
+
+Note: there is one special file ("alpm.h") which is the public interface
+that should be distributed and installed on systems with the library.
+Only structures, data and functions declared within this file are made
+available to the frontend.
+Lots of structures are of an opaque type and their fields are only
+accessible in read-only mode, through some clearly defined functions.
+
+Note: several structures and functions have been renamed compared to
+pacman 2.9 code.
+This was done at first for the sake of naming scheme consistency, and
+then primarily because of potential namespace conflicts between library
+and frontend spaces.
+Indeed, it is not possible to have two different functions with the same
+name declared in both spaces.
+To avoid such conflicts, some function names have been prepended with
+"_alpm_".
+In a general manner, public library functions are named
+"alpm_<type>_<action>" (examples: alpm_trans_commit(),
+alpm_lib_release(), alpm_pkg_getinfo(), ...).
+Internal (and thus private) functions should be named "_alpm_XXX" for
+instance (examples: _alpm_needbackup(), _alpm_runscriplet(), ...).
+As of now, this scheme is only applied to most sensitive functions
+(mainly the ones from util.c), which have generic names, and thus, which
+are likely to be redefined in the frontend.
+One can consider that the frontend should have the priority in function
+names choice, and that it is up to the library to hide its symbols to
+avoid conflicts with the frontend ones.
+Finally, functions defined and used inside a single file should be
+defined as "static".
+
+
+[HANDLE] (see handle.c)
+
+The "handle" object is the heart of the library. It is a global
+structure available from almost all other objects (althought some very
+low level objects should not be aware of the handle object, like chained
+list, package or groups structures.
+
+There is only one instance, created by the frontend upon
+"alpm_lib_init()" call, and destroyed upon "alpm_lib_release()" call.
+
+alpm_lib_init() is used to initialize library internals and to create
+the handle object (handle != NULL).
+Before its call, the library can't be used.
+alpm_lib_release() just does the opposite (memory used by the library is
+freed, and handle is set to NULL).
+After its call, the library is no more available.
+
+The aim of the handle is to provide a central placeholder for essential
+library parameters (filesystem root, pointers to database objects,
+configuration parameters, ...)
+
+The handle also allows to register a log callback usable by the frontend
+to catch all sort of notifications from the library.
+The frontend can choose the level of verbosity (i.e. the mask), or can
+simply choose to not use the log callback.
+A friendly frontend should care at least for WARNING and ERROR
+notifications.
+Other notifications can safely be ignored and are mainly available for
+troubleshooting purpose.
+
+Last, but not least, the handle holds a _unique_ transaction object.
+
+
+[TRANSACTION] (see trans.c, and also alpm.c)
+
+The transaction sturcture permits easy manipulations of several package
+at a time (i.e. adding, upgrade and removal operations).
+
+A transaction can be initiatied with a type (ADD, UPGRADE or REMOVE),
+and some flags (NODEPS, FORCE, CASCADE, ...).
+
+Note: there can only be one type at a time: a transaction is either
+created to add packages to the system, or either created to remove packages.
+The frontend can't request for mixed operations: it has to run several
+transactions, one at a time, in such a case.
+
+The flags allow to tweak the library behaviour during its resolution.
+Note, that some options of the handle can also modify the behavior of a
+transaction (NOUPGRADE, IGNOREPKG, ...).
+
+Note: once a transaction has been initiated, it is not possible anymore
+to modify its type or its flags.
+
+One can also add some targets to a transaction (alpm_trans_addtarget()).
+These targets represent the list of packages to be handled.
+
+Then, a transaction needs to be prepared (alpm_trans_prepare()). It
+means that the various targets added, will be inspected and challenged
+against the set of alreayd installed packages (dependency checkings,
+
+Last, a callback is associated with each transaction. During the
+transaction resolution, each time a new step is started or done (i.e
+dependency or conflict checkings, package adding or removal, ...), the
+callback is called, allowing the frontend to be aware of the progress of
+the resolution. Can be useful to implement a progress bar.
+
+
+[CONFIGURATION/OPTIONS] (see handle.c)
+
+The library does not use any configuration file. The handle holds a
+number of configuration options instead (IGNOREPKG, SYSLOG usage,
+log file name, registered databases, ...).
+It is up to the frontend to set the options of the library.
+Options can be manipulated using calls to
+alpm_set_option()/alpm_get_option().
+
+Note: the file system root is a special option which can only be defined
+when calling alpm_lib_init(). It can't be modified afterwards.
+
+
+[CACHE] (see cache.c)
+
+Compared to pacman 2.9, there is now one cache object connected to each
+database object.
+There are both a package and a group cache.
+The cache is loaded only on demand (i.e the cache is loaded the first
+time data from it should used).
+
+Note: the cache of a database is always destroyed by the library after
+an operation changing the database content (adding and/or removal of
+packages).
+Beware frontends ;)
+
+
+[PACKAGE] (see package.c, and also db.c)
+
+The package structure is using three new fields, namely: origin, data,
+infolevel.
+The purpose of these fields is to know some extra info about data stored
+in package structures.
+
+For instance, where is the package coming from (i.e origin)?
+Was it loaded from a file or loaded from the cache?
+If it's coming from a file, then the field data holds the full path and
+name of the file, and infolevel is set to the highest possible value
+(all package fields are reputed to be known).
+Otherwise, if the package comes from a database, data is a pointer to
+the database structure hosting the package, and infolevel is set
+according to the db_read() infolevel parameter (it is possible using
+db_read() to only read a part of the package datas).
+
+Indeed, to reduce database access, all packages data requested by the
+frontend are comming from the cache. As a consequence, the library needs
+to know exactly the level of information about packages it holds, and
+then decide if more data needs to be fetched from the database.
+
+In file alpm.c, have a look at alpm_pkg_getinfo() function to get an
+overview.
+
+
+[ERRORS] (error.c)
+
+The library provides a global variable pm_errno.
+It aims at being to the library what errno is for C system calls.
+
+Almost all public library functions are returning an integer value: 0
+indicating success, whereas -1 would indicate a failure.
+If -1 is returned, the variable pm_errno is set to a meaningful value
+(not always yet, but it should improve ;).
+Wise frontends should always care for these returned values.
+
+Note: the helper function alpm_strerror() can also be used to translate
+the error code into a more friendly sentence.
+
+
+[LIST] (see list.c, and especially list wrappers in alpm.c)
+
+It is a double chained list structure, use only for the internal needs
+of the library.
+A frontend should be free to use its own data structures to manipulate
+packages.
+For instance, consider a graphical frontend using the gtk toolkit (and
+as a consequence the glib library). The frontend will make use of the
+glib chained lists or trees.
+As a consequence, the library only provides a simple and very small
+interface to retrieve pointers to its internal data (see functions
+alpm_list_first(), alpm_list_next() and alpm_list_getdata()), giving to
+the frontend the responsibility to copy and store the data retrieved
+from the library in its own data structures.
+
+
+PACMAN frontend overview & internals
+====================================
+
+Here are some words about the frontend responsibilities.
+The library can operate only a small set of well defined operations and
+dumy operations.
+
+High level features are left to the frontend ;)
+
+For instance, during a sysupgrade, the library returns the whole list of
+packages to be upgraded, without any care for its content.
+The frontend can inspect the list and perhaps notice that "pacman"
+itself has to be upgraded. In such a case, the frontend can choose to
+perform a special action.
+
+
+[MAIN] (see pacman.c)
+
+Calls for alpm_lib_init(), and alpm_lib_release().
+Read the configuration file, and parse command line arguments.
+Based on the action requested, it initiates the appropriate transactions
+(see pacman_add(), pacman_remove(), pacman_sync() in files add.c,
+remove.c and sync.c).
+
+
+[CONFIGURATION] (see conf.c)
+
+The frontend is using a configuration file, usually "/etc/pacman.conf".
+Part of these options are only usefull for the frontend only (mainly,
+the download stuffs, and some options like HOLDPKG).
+The rest is used to configure the library.
+
+
+[ADD/UPGRADE/REMOVE/SYNC]
+
+Nothing new here, excepted some reorganization.
+
+The file pacman.c has been divided into several smaller files, namely
+add.c, remove.c, sync.c and query.c, to hold the big parts: pacman_add,
+pacman_remove, pacman_sync.
+These 3 functions have been splitted too to ease the code reading.
+
+
+[DONWLOAD] (see download.c)
+
+The library is not providing download facilities. As a consequence, it
+is up the the frontend to retrieve packages from Arch Linux servers.
+To do so, pacman is linked against an improved version of libftp
+supporting both http and ftp donwloads.
+As a consequence, the frontend is repsonsible for the directory
+/var/cache/pacman/pkgs.
+One can consider that this cache is a facility provided by pacman.
+
+Note: other frontends have to download packages by themselves too,
+although the cache directory can be shared by several frontends.
+
+
+[LIST] (see list.c)
+
+Single chained list.
+A minimalistic chained list implementation to store options from the
+configuration file, and targets passed to pacman on the command line.
+
+
+LIMITATIONS/BEHAVIOR CHANGES COMPARED TO PACMAN 2.9
+===================================================
+
+Excepted missing features still needing to be implemented, one can
+notice the following limitations:
+
+- When trying to add a package that conflicts with an already installed
+one, pacman won't ask for removing the latter one prior to install the
+former.
+It will stop with an error code mentionning a conflict.
+
+The library can handle only one transaction at a time, and as a consequence,
+it is not easily possible to remove a conflicting package while holding
+still the on-going transaction...
+
+- ...
+
@@ -0,0 +1,23 @@ +TODO:
+
+- get ride of "ORE" tags.
+
+- implement missing functionnalities (mainly pacman -S <pkg1> <pkg2>, and
+packages replacement)
+
+- pacman_deptest() is not implemented
+
+- backport new functionnalities from pacman >= 2.9
+
+- bring back the autoconf/automake stuff
+
+- review how things are displayed in the frontend (normal display,
+verbose mode, which usage for the library log callback?)
+
+- review errors handling in the library (globalise pm_errno usage,
+improve error meanings)
+
+- library documentation (doxygen? man pages for public functions? ...)
+
+- import libtar code into CVS too?
+
diff --git a/config.h b/config.h new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/config.h diff --git a/doc/makepkg.8.in b/doc/makepkg.8.in new file mode 100644 index 00000000..569f3596 --- /dev/null +++ b/doc/makepkg.8.in @@ -0,0 +1,407 @@ +.TH makepkg 8 "August 3, 2004" "makepkg #VERSION#" "" +.SH NAME +makepkg \- package build utility +.SH SYNOPSIS +\fBmakepkg [options]\fP +.SH DESCRIPTION +\fBmakepkg\fP will build packages for you. All it needs is +a build-capable linux platform, wget, and some build scripts. The advantage +to a script-based build is that you only really do the work once. Once you +have the build script for a package, you just need to run makepkg and it +will do the rest: download and validate source files, check dependencies, +configure the buildtime settings, build the package, install the package +into a temporary root, make customizations, generate meta-info, and package +the whole thing up for \fBpacman\fP to use. + +\fBmakeworld\fP can be used to rebuild an entire package group or the +entire build tree. See \fBmakeworld --help\fP for syntax. +.SH BUILD PROCESS (or How To Build Your Own Packages) +Start in an isolated directory (ie, it's not used for anything other +than building this package). The build script should be called PKGBUILD +and it should bear resemblance to the example below. + +\fBNOTE:\fP If you have a local copy of the Arch Build System (ABS) tree +on your computer, you can copy the PKGBUILD.proto file to your new package +build directory and edit it from there. To acquire/sync the ABS tree, use +the \fBabs\fP script included with pacman/makepkg. + +.TP +.TP +.SH PKGBUILD Example: +.RS +.nf +pkgname=modutils +pkgver=2.4.25 +pkgrel=1 +pkgdesc="Utilities for inserting and removing modules from the linux kernel" +url="http://www.kernel.org" +backup=(etc/modules.conf) +makedepends=('bash' 'mawk') +depends=('glibc' 'zlib') +source=(ftp://ftp.kernel.org/pub/linux/utils/kernel/$pkgname/v2.4/$pkgname-$pkgver.tar.bz2 \\ + modules.conf) +md5sums=('2c0cca3ef6330a187c6ef4fe41ecaa4d' \\ + '35175bee593a7cc7d6205584a94d8625') + +build() { + cd $startdir/src/$pkgname-$pkgver + ./configure --prefix=/usr --enable-insmod-static + make || return 1 + make prefix=$startdir/pkg/usr install + mv $startdir/pkg/usr/sbin $startdir/pkg + mkdir -p $startdir/pkg/etc + cp ../modules.conf $startdir/pkg/etc +} +.fi +.RE + +As you can see, the setup is fairly simple. The first three lines define +the package name and version info. They also define the final package name +which will be of the form \fI$pkgname-$pkgver-$pkgrel.pkg.tar.gz\fP. The fourth +line provides a brief description of the package. These four lines should +be present in every PKGBUILD script. + +The line with \fIbackup=\fP specifies files that should be treated specially +when removing or upgrading packages. See \fBHANDLING CONFIG FILES\fP in +the \fIpacman\fP manpage for more information on this. + +Lines 7 and 8 list the dependencies for this package. The \fIdepends\fP array +specifies the run-time dependencies and \fImakedepends\fP specifies the build-time +dependencies. In order to run the package, \fIdepends\fP must be satisfied. To +build the package, \fBall\fP dependencies must be satisifed first. makepkg +will check this before attempting to build the package. + +The \fIsource\fP array tells makepkg which files to download/extract before compiling +begins. The \fImd5sums\fP array provides md5sums for each of these files. These +are used to validate the integrity of the source files. + +Once your PKGBUILD is created, you can run \fImakepkg\fP from the build directory. +makepkg will then check dependencies and look for the source files required to +build. If some are missing it will attempt to download them, provided there is +a fully-qualified URL in the \fIsource\fP array. + +The sources are then extracted into a directory called ./src and +the \fIbuild\fP function is called. This is where all package configuration, +building, and installing should be done. Any customization will likely take +place here. + +After a package is built, the \fIbuild\fP function must install the package +files into a special package root, which can be referenced by \fB$startdir/pkg\fP +in the \fIbuild\fP function. The typical way to do this is one of the following: +.RS +.nf + +make DESTDIR=$startdir/pkg install + +or + +make prefix=$startdir/pkg/usr install + +.fi +.RE +Notice that the "/usr" portion should be present with "prefix", but not "DESTDIR". +"DESTDIR" is the favorable option to use, but not all Makefiles support it. Use +"prefix" only when "DESTDIR" is unavailable. + +Once the package is successfully installed into the package root, \fImakepkg\fP +will remove some directories (as per Arch Linux package guidelines; if you use +this elsewhere, feel free to change it) like /usr/doc and /usr/info. It will +then strip debugging info from libraries and binaries and generate a meta-info +file. Finally, it will compress everything into a .pkg.tar.gz file and leave it +in the directory you ran \fBmakepkg\fP from. + +At this point you should have a package file in the current directory, named +something like name-version-release.pkg.tar.gz. Done! + +.SH Install/Upgrade/Remove Scripting +Pacman has the ability to store and execute a package-specific script when it +installs, removes, or upgrades a package. This allows a package to "configure +itself" after installation and do the opposite right before it is removed. + +The exact time the script is run varies with each operation: +.TP +.B pre_install +script is run right before files are extracted. + +.TP +.B post_install +script is run right after files are extracted. + +.TP +.B pre_upgrade +script is run right before files are extracted. + +.TP +.B post_upgrade +script is run after files are extracted. + +.TP +.B pre_remove +script is run right before files are removed. + +.TP +.B post_remove +script is run right after files are removed. + +.RE +To use this feature, just create a file (eg, pkgname.install) and put it in +the same directory as the PKGBUILD script. Then use the \fIinstall\fP directive: +.RS +.nf +install=pkgname.install +.fi +.RE + +The install script does not need to be specified in the \fIsource\fP array. + +.TP +.TP +.SH Install scripts must follow this format: +.RS +.nf +# arg 1: the new package version +pre_install() { + # + # do pre-install stuff here + # + /bin/true +} + +# arg 1: the new package version +post_install() { + # + # do post-install stuff here + # + /bin/true +} + +# arg 1: the new package version +# arg 2: the old package version +pre_upgrade() { + # + # do pre-upgrade stuff here + # + /bin/true +} + +# arg 1: the new package version +# arg 2: the old package version +post_upgrade() { + # + # do post-upgrade stuff here + # + /bin/true +} + +# arg 1: the old package version +pre_remove() { + # + # do pre-remove stuff here + # + /bin/true +} + +# arg 1: the old package version +post_remove() { + # + # do post-remove stuff here + # + /bin/true +} + +op=$1 +shift +$op $* +.fi +.RE + +This template is also available in your ABS tree (/var/abs/install.proto). + +.SH PKGBUILD Directives +.TP +.B pkgname +The name of the package. This has be a unix-friendly name as it will be +used in the package filename. + +.TP +.B pkgver +This is the version of the software as released from the author (eg, 2.7.1). + +.TP +.B pkgrel +This is the release number specific to Arch Linux packages. + +.TP +.B pkgdesc +This should be a brief description of the package and its functionality. + +.TP +.B force +This is used to force the package to be upgraded by \fB--sysupgrade\fP, even +if its an older version. + +.TP +.B url +This field contains an optional URL that is associated with the piece of software +being packaged. This is typically the project's website. + +.TP +.B license +Sets the license type (eg, "GPL", "BSD", "NON-FREE"). (\fBNote\fP: This +option is still in development and may change in the future) + +.TP +.B install +Specifies a special install script that is to be included in the package. +This file should reside in the same directory as the PKGBUILD, and will be +copied into the package by makepkg. It does not need to be included in the +\fIsource\fP array. (eg, install=modutils.install) + +.TP +.B source \fI(array)\fP +The \fIsource\fP line is an array of source files required to build the +package. Source files must reside in the same directory as the PKGBUILD +file, unless they have a fully-qualified URL. Then if the source file +does not already exist in /var/cache/pacman/src, the file is downloaded +by wget. + +.TP +.B md5sums \fI(array)\fP +If this field is present, it should contain an MD5 hash for every source file +specified in the \fIsource\fP array (in the same order). makepkg will use +this to verify source file integrity during subsequent builds. To easily +generate md5sums, first build using the PKGBUILD then run +\fBmakepkg -g >>PKGBILD\fP. Then you can edit the PKGBUILD and move the +\fImd5sums\fP line from the bottom to an appropriate location. + +.TP +.B groups \fI(array)\fP +This is an array of symbolic names that represent groups of packages, allowing +you to install multiple packages by requesting a single target. For example, +one could install all KDE packages by installing the 'kde' group. + +.TP +.B backup \fI(array)\fP +A space-delimited array of filenames (without a preceding slash). The +\fIbackup\fP line will be propagated to the package meta-info file for +pacman. This will designate all files listed there to be backed up if this +package is ever removed from a system. See \fBHANDLING CONFIG FILES\fP in +the \fIpacman\fP manpage for more information. + +.TP +.B depends \fI(array)\fP +An array of packages that this package depends on to build and run. Packages +in this list should be surrounded with single quotes and contain at least the +package name. They can also include a version requirement of the form +\fBname<>version\fP, where <> is one of these three comparisons: \fB>=\fP +(greater than equal to), \fB<=\fP (less than or equal to), or \fB=\fP (equal to). +See the PKGBUILD example above for an example of the \fIdepends\fP directive. + +.TP +.B makedepends \fI(array)\fP +An array of packages that this package depends on to build (ie, not required +to run). Packages in this list should follow the same format as \fIdepends\fP. + +.TP +.B conflicts \fI(array)\fP +An array of packages that will conflict with this package (ie, they cannot both +be installed at the same time). This directive follows the same format as +\fIdepends\fP except you cannot specify versions here, only package names. + +.TP +.B provides \fI(array)\fP +An array of "virtual provisions" that this package provides. This allows a package +to provide dependency names other than it's own package name. For example, the +kernel-scsi and kernel-ide packages can each provide 'kernel' which allows packages +to simply depend on 'kernel' rather than "kernel-scsi OR kernel-ide OR ..." + +.TP +.B replaces \fI(array)\fP +This is an array of packages that this package should replace, and can be used to handle +renamed/combined packages. For example, if the kernel package gets renamed +to kernel-ide, then subsequent 'pacman -Syu' calls will not pick up the upgrade, due +to the differing package names. \fIreplaces\fP handles this. + +.SH MAKEPKG OPTIONS +.TP +.B "\-b, \-\-builddeps" +Build missing dependencies from source. When makepkg finds missing build-time or +run-time dependencies, it will look for the dependencies' PKGBUILD files under +$ABSROOT (set in your /etc/makepkg.conf). If it finds them it will +run another copy of makepkg to build and install the missing dependencies. +The child makepkg calls will be made with the \fB-b\fP and \fB-i\fP options. +.TP +.B "\-c, \-\-clean" +Clean up leftover work files/directories after a successful build. +.TP +.B "\-C, \-\-cleancache" +Removes all source files from the cache directory to free up diskspace. +.TP +.B "\-d, \-\-nodeps" +Do not perform any dependency checks. This will let you override/ignore any +dependencies required. There's a good chance this option will break the build +process if all of the dependencies aren't installed. +.TP +.B "\-f, \-\-force" +\fBmakepkg\fP will not build a package if a \fIpkgname-pkgver-pkgrel.pkg.tar.gz\fP +file already exists in the build directory. You can override this behaviour with +the \fB--force\fP switch. +.TP +.B "\-g, \-\-genmd5" +Download all source files (if required) and use \fImd5sum\fP to generate md5 hashes +for each of them. You can then redirect the output into your PKGBUILD for source +validation (makepkg -g >>PKGBUILD). +.TP +.B "\-h, \-\-help" +Output syntax and commandline options. +.TP +.B "\-i, \-\-install" +Install/Upgrade the package after a successful build. +.TP +.B "\-j <jobs>" +Sets MAKEFLAGS="-j<jobs>" before building the package. This is useful for overriding +the MAKEFLAGS setting in /etc/makepkg.conf. +.TP +.B "\-m, \-\-nocolor" +Disable color in output messages +.TP +.B "\-n, \-\-nostrip" +Do not strip binaries and libraries. +.TP +.B "\-o, \-\-nobuild" +Download and extract files only, do not build. +.TP +.B "\-p <buildscript>" +Read the package script \fI<buildscript>\fP instead of the default (\fIPKGBUILD\fP). +.TP +.B "\-r, \-\-rmdeps" +Upon successful build, remove any dependencies installed by makepkg/pacman during +dependency auto-resolution (using \fB-b\fP or \fB-s\fP). +.TP +.B "\-s, \-\-syncdeps" +Install missing dependencies using pacman. When makepkg finds missing build-time +or run-time dependencies, it will run pacman to try and resolve them. If successful, +pacman will download the missing packages from a package repository and +install them for you. +.TP +.B "\-w <destdir>" +Write the resulting package file to the directory \fI<destdir>\fP instead of the +current working directory. + +.SH CONFIGURATION +Configuration options are stored in \fI/etc/makepkg.conf\fP. This file is parsed +as a bash script, so you can export any special compiler flags you wish +to use. This is helpful for building for different architectures, or with +different optimizations. + +\fBNOTE:\fP This does not guarantee that all package Makefiles will use +your exported variables. Some of them are flaky... +.SH SEE ALSO +\fBpacman\fP is the package manager that uses packages built by makepkg. + +See the Arch Linux Documentation for package-building guidelines if you wish +to contribute packages to the Arch Linux project. +.SH AUTHOR +.nf +Judd Vinet <jvinet@zeroflux.org> +.fi diff --git a/doc/pacman.8.in b/doc/pacman.8.in new file mode 100644 index 00000000..27787c15 --- /dev/null +++ b/doc/pacman.8.in @@ -0,0 +1,312 @@ +.TH pacman 8 "September 17, 2004" "pacman #VERSION#" "" +.SH NAME +pacman \- package manager utility +.SH SYNOPSIS +\fBpacman <operation> [options] <package> [package] ...\fP +.SH DESCRIPTION +\fBpacman\fP is a \fIpackage management\fP utility that tracks installed +packages on a linux system. It has simple dependency support and the ability +to connect to a remote ftp server and automatically upgrade packages on +the local system. pacman package are \fIgzipped tar\fP format. +.SH OPERATIONS +.TP +.B "\-A, \-\-add" +Add a package to the system. Package will be uncompressed +into the installation root and the database will be updated. +.TP +.B "\-F, \-\-freshen" +This is like --upgrade except that, unlike --upgrade, this will only +upgrade packages that are already installed on your system. +.TP +.B "\-Q, \-\-query" +Query the package database. This operation allows you to +view installed packages and their files, as well as meta-info +about individual packages (dependencies, conflicts, install date, +build date, size). This can be run against the local package +database or can be used on individual .tar.gz packages. See +\fBQUERY OPTIONS\fP below. +.TP +.B "\-R, \-\-remove" +Remove a package from the system. Files belonging to the +specified package will be deleted, and the database will +be updated. Most configuration files will be saved with a +\fI.pacsave\fP extension unless the \fB--nosave\fP option was +used. +.TP +.B "\-S, \-\-sync" +Synchronize packages. With this function you can install packages +directly from the ftp servers, complete with all dependencies required +to run the packages. For example, \fBpacman -S qt\fP will download +qt and all the packages it depends on and install them. You could also use +\fBpacman -Su\fP to upgrade all packages that are out of date (see below). +.TP +.B "\-U, \-\-upgrade" +Upgrade a package. This is essentially a "remove-then-add" +process. See \fBHANDLING CONFIG FILES\fP for an explanation +on how pacman takes care of config files. +.TP +.B "\-V, \-\-version" +Display version and exit. +.TP +.B "\-h, \-\-help" +Display syntax for the given operation. If no operation was +supplied then the general syntax is shown. +.SH OPTIONS +.TP +.B "\-d, \-\-nodeps" +Skips all dependency checks. Normally, pacman will always check +a package's dependency fields to ensure that all dependencies are +installed and there are no package conflicts in the system. This +switch disables these checks. +.TP +.B "\-f, \-\-force" +Bypass file conflict checks, overwriting conflicting files. If the +package that is about to be installed contains files that are already +installed, this option will cause all those files to be overwritten. +This option should be used with care, ideally not at all. +.TP +.B "\-r, \-\-root <path>" +Specify alternative installation root (default is "/"). This +should \fInot\fP be used as a way to install software into +e.g. /usr/local instead of /usr. Instead this should be used +if you want to install a package on a temporary mounted partition, +which is "owned" by another system. By using this option you not only +specify where the software should be installed, but you also +specify which package database to use. +.TP +.B "\-v, \-\-verbose" +Output more status and error messages. +.TP +.B "\-\-config <path>" +Specify an alternate configuration file. +.TP +.B "\-\-noconfirm" +Bypass any and all "Are you sure?" messages. It's not a good to do this +unless you want to run pacman from a script. +.SH SYNC OPTIONS +.TP +.B "\-c, \-\-clean" +Remove old packages from the cache. When pacman downloads packages, +it saves them in \fI/var/cache/pacman/pkg\fP. If you need to free up +diskspace, you can remove these packages by using the --clean option. +Using one --clean (or -c) switch will only remove \fIold\fP packages. +Use it twice to remove \fIall\fP packages from the cache. +.TP +.B "\-g, \-\-groups" +Display all the members for each package group specified. If no group +names are provided, all groups will be listed. +.TP +.B "\-i, \-\-info" +Display dependency information for a given package. This will search +through all repositories for a matching package and display the +dependencies, conflicts, etc. +.TP +.B "\-l, \-\-list" +List all files in the specified repositories. Multiple repositories can +be specified on the command line. +.TP +.B "\-p, \-\-print-uris" +Print out URIs for each specified package and its dependencies. These +can be piped to a file and downloaded at a later time, using a program +like wget. +.TP +.B "\-s, \-\-search <string>" +This will search each package in the package list for names or descriptions +that contains <string>. +.TP +.B "\-u, \-\-sysupgrade" +Upgrades all packages that are out of date. pacman will examine every +package installed on the system, and if a newer package exists on the +server it will upgrade. pacman will present a report of all packages +it wants to upgrade and will not proceed without user confirmation. +Dependencies are automatically resolved at this level and will be +installed/upgraded if necessary. +.TP +.B "\-w, \-\-downloadonly" +Retrieve all packages from the server, but do not install/upgrade anything. +.TP +.B "\-y, \-\-refresh" +Download a fresh copy of the master package list from the ftp server +defined in \fI/etc/pacman.conf\fP. This should typically be used each +time you use \fB--sysupgrade\fP. +.TP +.B "\-\-ignore <pkg>" +This option functions exactly the same as the \fBIgnorePkg\fP configuration +directive. Sometimes it can be handy to skip some package updates without +having to edit \fIpacman.conf\fP each time. +.SH REMOVE OPTIONS +.TP +.B "\-c, \-\-cascade" +Remove all target packages, as well as all packages that depend on one +or more target packages. This operation is recursive. +.TP +.B "\-k, \-\-keep" +Removes the database entry only. Leaves all files in place. +.TP +.B "\-n, \-\-nosave" +Instructs pacman to ignore file backup designations. Normally, when +a file is about to be \fIremoved\fP from the system the database is first +checked to see if the file should be renamed to a .pacsave extension. If +\fB--nosave\fP is used, these designations are ignored and the files are +removed. +.TP +.B "\-s, \-\-recursive" +For each target specified, remove it and all its dependencies, provided +that (A) they are not required by other packages; and (B) they were not +explicitly installed by the user. +This option is analagous to a backwards --sync operation. +.SH QUERY OPTIONS +.TP +.B "\-e, \-\-orphans" +List all packages that were explicitly installed (ie, not pulled in +as a dependency by other packages) and are not required by any other +packages. +.TP +.B "\-g, \-\-groups" +Display all groups that a specified package is part of. If no package +names are provided, all groups and members will be listed. +.TP +.B "\-i, \-\-info" +Display information on a given package. If it is used with the \fB-p\fP +option then the .PKGINFO file will be printed. +.TP +.B "\-l, \-\-list" +List all files owned by <package>. Multiple packages can be specified on +the command line. +.TP +.B "\-o, \-\-owns <file>" +Search for the package that owns <file>. +.TP +.B "\-p, \-\-file" +Tells pacman that the package supplied on the command line is a +file, not an entry in the database. Pacman will decompress the +file and query it. This is useful with \fB--info\fP and \fB--list\fP. +.TP +.B "\-s, \-\-search <string>" +This will search each locally-installed package for names or descriptions +that contains <string>. +.SH HANDLING CONFIG FILES +pacman uses the same logic as rpm to determine action against files +that are designated to be backed up. During an upgrade, it uses 3 +md5 hashes for each backup file to determine the required action: +one for the original file installed, one for the new file that's about +to be installed, and one for the actual file existing on the filesystem. +After comparing these 3 hashes, the follow scenarios can result: +.TP +original=\fBX\fP, current=\fBX\fP, new=\fBX\fP +All three files are the same, so we win either way. Install the new file. +.TP +original=\fBX\fP, current=\fBX\fP, new=\fBY\fP +The current file is un-altered from the original but the new one is +different. Since the user did not ever modify the file, and the new +one may contain improvements/bugfixes, we install the new file. +.TP +original=\fBX\fP, current=\fBY\fP, new=\fBX\fP +Both package versions contain the exact same file, but the one +on the filesystem has been modified since. In this case, we leave +the current file in place. +.TP +original=\fBX\fP, current=\fBY\fP, new=\fBY\fP +The new one is identical to the current one. Win win. Install the new file. +.TP +original=\fBX\fP, current=\fBY\fP, new=\fBZ\fP +All three files are different. So we install the new file, but back up the +old one to a .pacsave extension. This way the user can move the old configuration +file back into place if he wishes. +.SH CONFIGURATION +pacman will attempt to read \fI/etc/pacman.conf\fP each time it is invoked. This +configuration file is divided into sections or \fIrepositories\fP. Each section +defines a package repository that pacman can use when searching for packages in +--sync mode. The exception to this is the \fIoptions\fP section, which defines +global options. +.TP +.SH Example: +.RS +.nf +[options] +NoUpgrade = etc/passwd etc/group etc/shadow +NoUpgrade = etc/fstab + +Include = /etc/pacman.d/current + +[custom] +Server = file:///home/pkgs + +.fi +.RE +.SH CONFIG: OPTIONS +.TP +.B "DBPath = path/to/db/dir" +Overrides the default location of the toplevel database directory. The default is +\fIvar/lib/pacman\fP. +.TP +.B "HoldPkg = <package> [package] ..." +If a user tries to \fB--remove\fP a package that's listed in HoldPkg, pacman +will ask for confirmation before proceeding. +.TP +.B "IgnorePkg = <package> [package] ..." +Instructs pacman to ignore any upgrades for this package when performing a +\fB--sysupgrade\fP. +.TP +.B "Include = <path>" +Include another config file. This config file can include repositories or +general configuration options. +.TP +.B "ProxyServer = <host|ip>[:port]" +If set, pacman will use this proxy server for all ftp/http transfers. +.TP +.B "XferCommand = /path/to/command %u" +If set, pacman will use this external program to download all remote files. +All instances of \fB%u\fP will be replaced with the URL to be downloaded. If +present, instances of \fB%o\fP will be replaced with the local filename, plus a +".part" extension, which allows programs like wget to do file resumes properly. + +This option is useful for users who experience problems with pacman's built-in http/ftp +support, or need the more advanced proxy support that comes with utilities like +wget. +.TP +.B "NoPassiveFtp" +Disables passive ftp connections when downloading packages. (aka Active Mode) +.TP +.B "NoUpgrade = <file> [file] ..." +All files listed with a \fBNoUpgrade\fP directive will never be touched during a package +install/upgrade. \fINote:\fP do not include the leading slash when specifying files. +.TP +.B "UseSyslog" +Log action messages through syslog(). This will insert pacman log entries into your +/var/log/messages or equivalent. +.TP +.B "LogFile = /path/to/file" +Log actions directly to a file, usually /var/log/pacman.log. + +.SH CONFIG: REPOSITORIES +Each repository section defines a section name and at least one location where the packages +can be found. The section name is defined by the string within square brackets (eg, the two +above are 'current' and 'custom'). Locations are defined with the \fIServer\fP directive and +follow a URL naming structure. Currently only ftp is supported for remote servers. If you +want to use a local directory, you can specify the full path with a 'file://' prefix, as +shown above. +.SH USING YOUR OWN REPOSITORY +Let's say you have a bunch of custom packages in \fI/home/pkgs\fP and their respective PKGBUILD +files are all in \fI/var/abs/local\fP. All you need to do is generate a compressed package database +in the \fI/home/pkgs\fP directory so pacman can find it when run with --refresh. + +.RS +.nf +# gensync /var/abs/local /home/pkgs/custom.db.tar.gz +.fi +.RE + +The above command will read all PKGBUILD files in /var/abs/local and generate a compressed +database called /home/pkgs/custom.db.tar.gz. Note that the database must be of the form +\fI{treename}.db.tar.gz\fP, where {treename} is the name of the section defined in the +configuration file. +That's it! Now configure your \fIcustom\fP section in the configuration file as shown in the +config example above. Pacman will now use your package repository. If you add new packages to +the repository, remember to re-generate the database and use pacman's --refresh option. +.SH SEE ALSO +\fBmakepkg\fP is the package-building tool that comes with pacman. +.SH AUTHOR +.nf +Judd Vinet <jvinet@zeroflux.org> +.fi diff --git a/etc/makepkg.conf b/etc/makepkg.conf new file mode 100644 index 00000000..bb48ec8d --- /dev/null +++ b/etc/makepkg.conf @@ -0,0 +1,36 @@ +# +# /etc/makepkg.conf +# + +# The FTP/HTTP download utility that makepkg should use to acquire sources +export FTPAGENT="/usr/bin/wget --continue --passive-ftp --tries=3 --waitretry=3" +#export FTPAGENT="/usr/bin/snarf" +#export FTPAGENT="/usr/bin/lftpget -c" + +export CARCH="i686" +export CHOST="i686-pc-linux-gnu" + +# Pentium Pro/Pentium II/Pentium III+/Pentium 4/Athlon exclusive (binaries +# will use the P6 instruction set and only run on P6+ systems) +export CFLAGS="-march=i686 -O2 -pipe -Wl,-O1" +export CXXFLAGS="-march=i686 -O2 -pipe -Wl,-O1" +# Pentium Pro/Pentium II/Pentium III+/Pentium 4/Athlon optimized (but binaries +# will run on any x86 system) +#export CFLAGS="-mcpu=i686 -O2 -pipe" +#export CXXFLAGS="-mcpu=i686 -O2 -pipe" + +# SMP Systems +#export MAKEFLAGS="-j 2" + +# Enable fakeroot for building packages as a non-root user +export USE_FAKEROOT="y" + +# Enable colorized output messages +export USE_COLOR="n" + +# Specify a fixed directory where all packages will be placed +#export PKGDEST=/home/packages + +# If you want your name to show up in the packages you build, set this. +#export PACKAGER="John Doe <john@doe.com>" + diff --git a/etc/pacman.conf b/etc/pacman.conf new file mode 100644 index 00000000..42578338 --- /dev/null +++ b/etc/pacman.conf @@ -0,0 +1,53 @@ +# +# /etc/pacman.conf +# +# NOTE: If you find a mirror that is geographically close to you, please +# move it to the top of the server list, so pacman will choose it +# first. +# +# To re-sort your mirror lists by ping/traceroute results, use the +# /usr/bin/sortmirrors.pl script. It requires the "netselect" package. +# +# # sortmirrors.pl </etc/pacman.conf >pacman.conf.new +# + +# See the pacman manpage for option directives + +# +# GENERAL OPTIONS +# +[options] +LogFile = /var/log/pacman.log +NoUpgrade = etc/passwd etc/group etc/shadow etc/sudoers +NoUpgrade = etc/fstab etc/raidtab etc/ld.so.conf +NoUpgrade = etc/rc.conf etc/rc.local +NoUpgrade = etc/modprobe.conf etc/modules.conf +NoUpgrade = etc/lilo.conf boot/grub/menu.lst +HoldPkg = pacman glibc +#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u + +# +# REPOSITORIES +# - can be defined here or included from another file +# - pacman will search repositories in the order defined here. +# - local/custom mirrors can be added here or in separate files +# + +#[testing] +#Server = ftp://ftp.archlinux.org/testing/os/i686 + +[current] +# Add your preferred servers here, they will be used first +Include = /etc/pacman.d/current + +[extra] +# Add your preferred servers here, they will be used first +Include = /etc/pacman.d/extra + +#Include = /etc/pacman.d/unstable + +# An example of a custom package repository. See the pacman manpage for +# tips on creating your own repositories. +#[custom] +#Server = file:///home/custompkgs + diff --git a/lib/libalpm/Makefile b/lib/libalpm/Makefile new file mode 100644 index 00000000..9a517b3b --- /dev/null +++ b/lib/libalpm/Makefile @@ -0,0 +1,39 @@ + +CXX=gcc +CFLAGS=-g -Wall -pedantic -D_GNU_SOURCE -I. -I../.. +AR=ar rc +RAN=ranlib + +OBJS=md5driver.o \ + md5.o \ + util.o \ + list.o \ + log.o \ + error.o \ + package.o \ + group.o \ + db.o \ + cache.o \ + deps.o \ + provide.o \ + rpmvercmp.o \ + backup.o \ + trans.o \ + add.o \ + remove.o \ + sync.o \ + handle.o \ + alpm.o + +all: libalpm.a + +%.o: %.c %.h + $(CXX) -c $(CFLAGS) -o $@ $< + +libalpm.a: $(OBJS) alpm.h + $(AR) $@ $(OBJS) + $(RAN) $@ + +clean: + rm -f *.o *~ core + rm -f libalpm.a diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c new file mode 100644 index 00000000..2c883f10 --- /dev/null +++ b/lib/libalpm/add.c @@ -0,0 +1,580 @@ +/* + * add.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <string.h> +#include <zlib.h> +#include <libtar.h> +/* pacman */ +#include "util.h" +#include "error.h" +#include "list.h" +#include "cache.h" +#include "rpmvercmp.h" +#include "md5.h" +#include "log.h" +#include "backup.h" +#include "package.h" +#include "db.h" +#include "provide.h" +#include "trans.h" +#include "deps.h" +#include "add.h" +#include "remove.h" +#include "handle.h" + +extern pmhandle_t *handle; + +int add_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name) +{ + pmpkg_t *info, *dummy; + PMList *j; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(name != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* ORE + load_pkg should be done only if pkg has to be added to the transaction */ + + _alpm_log(PM_LOG_FLOW2, "reading %s...", name); + info = pkg_load(name); + if(info == NULL) { + /* pm_errno is already set by pkg_load() */ + return(-1); + } + + /* no additional hyphens in version strings */ + if(strchr(info->version, '-') != strrchr(info->version, '-')) { + FREEPKG(info); + pm_errno = PM_ERR_INVALID_NAME; + return(-1); + } + + dummy = db_get_pkgfromcache(db, info->name); + /* only freshen this package if it is already installed and at a lesser version */ + if(trans->flags & PM_TRANS_FLAG_FRESHEN) { + if(dummy == NULL || rpmvercmp(dummy->version, info->version) >= 0) { + FREEPKG(info); + PM_RET_ERR(PM_ERR_PKG_CANT_FRESH, -1); + } + } + /* only install this package if it is not already installed */ + if(trans->type != PM_TRANS_TYPE_UPGRADE) { + if(dummy) { + FREEPKG(info); + PM_RET_ERR(PM_ERR_PKG_INSTALLED, -1); + } + } + + /* check if an older version of said package is already in transaction packages. + * if so, replace it in the list */ + /* ORE + we'd better do it before load_pkg. */ + for(j = trans->packages; j; j = j->next) { + pmpkg_t *pkg = j->data; + + if(strcmp(pkg->name, info->name) != 0) { + if(rpmvercmp(pkg->version, info->version) < 0) { + _alpm_log(PM_LOG_WARNING, "replacing older version of %s in target list", pkg->name); + FREEPKG(j->data); + j->data = info; + } + } + } + + /* add the package to the transaction */ + trans->packages = pm_list_add(trans->packages, info); + + return(0); +} + +int add_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data) +{ + PMList *lp; + + *data = NULL; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* ORE ??? + No need to check deps if pacman_add was called during a sync: + it is already done in pacman_sync */ + + /* Check dependencies + */ + + if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) { + PMList *j; + + TRANS_CB(trans, PM_TRANS_CB_DEPS_START, NULL, NULL); + + lp = checkdeps(db, trans->type, trans->packages); + if(lp != NULL) { + int errorout = 0; + + /* look for unsatisfied dependencies */ + _alpm_log(PM_LOG_FLOW2, "looking for unsatisfied dependencies..."); + for(j = lp; j; j = j->next) { + pmdepmissing_t* miss = j->data; + + if(miss->type == PM_DEP_DEPEND || miss->type == PM_DEP_REQUIRED) { + if(!errorout) { + errorout = 1; + } + if((miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t))) == NULL) { + FREELIST(lp); + /* ORE, needed or not ? + FREELIST(*data);*/ + PM_RET_ERR(PM_ERR_MEMORY, -1); + } + *miss = *(pmdepmissing_t*)j->data; + *data = pm_list_add(*data, miss); + } + } + if(errorout) { + FREELIST(lp); + PM_RET_ERR(PM_ERR_UNSATISFIED_DEPS, -1); + } + + /* no unsatisfied deps, so look for conflicts */ + _alpm_log(PM_LOG_FLOW2, "looking for conflicts..."); + for(j = lp; j; j = j->next) { + pmdepmissing_t* miss = (pmdepmissing_t *)j->data; + if(miss->type == PM_DEP_CONFLICT) { + if(!errorout) { + errorout = 1; + } + MALLOC(miss, sizeof(pmdepmissing_t)); + *miss = *(pmdepmissing_t*)j->data; + *data = pm_list_add(*data, miss); + } + } + if(errorout) { + FREELIST(lp); + PM_RET_ERR(PM_ERR_CONFLICTING_DEPS, -1); + } + FREELIST(lp); + } + + /* re-order w.r.t. dependencies */ + _alpm_log(PM_LOG_FLOW2, "sorting by dependencies..."); + lp = sortbydeps(trans->packages); + /* free the old alltargs */ + for(j = trans->packages; j; j = j->next) { + j->data = NULL; + } + FREELIST(trans->packages); + trans->packages = lp; + + TRANS_CB(trans, PM_TRANS_CB_DEPS_DONE, NULL, NULL); + } + + /* Check for file conflicts + */ + if(!(trans->flags & PM_TRANS_FLAG_FORCE)) { + TRANS_CB(trans, PM_TRANS_CB_CONFLICTS_START, NULL, NULL); + + lp = db_find_conflicts(db, trans->packages, handle->root); + if(lp != NULL) { + *data = lp; + PM_RET_ERR(PM_ERR_FILE_CONFLICTS, -1); + } + + TRANS_CB(trans, PM_TRANS_CB_CONFLICTS_DONE, NULL, NULL); + } + + return(0); +} + +int add_commit(pmdb_t *db, pmtrans_t *trans) +{ + int i, ret = 0, errors = 0; + TAR *tar = NULL; + char expath[PATH_MAX]; + time_t t; + pmpkg_t *info = NULL; + PMList *targ, *lp; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + + if(trans->packages == NULL) { + return(0); + } + + for(targ = trans->packages; targ; targ = targ->next) { + tartype_t gztype = { + (openfunc_t)_alpm_gzopen_frontend, + (closefunc_t)gzclose, + (readfunc_t)gzread, + (writefunc_t)gzwrite + }; + unsigned short pmo_upgrade = (trans->type == PM_TRANS_TYPE_UPGRADE) ? 1 : 0; + char pm_install[PATH_MAX]; + pmpkg_t *oldpkg = NULL; + info = (pmpkg_t *)targ->data; + errors = 0; + + /* see if this is an upgrade. if so, remove the old package first */ + if(pmo_upgrade) { + if(pkg_isin(info, db_get_pkgcache(db))) { + TRANS_CB(trans, PM_TRANS_CB_UPGRADE_START, info, NULL); + + /* we'll need the full record for backup checks later */ + if((oldpkg = db_scan(db, info->name, INFRQ_ALL)) != NULL) { + pmtrans_t *tr; + + _alpm_log(PM_LOG_FLOW2, "removing old package first...\n"); + /* ORE + set flags to something, but what (nodeps?) ??? */ + tr = trans_new(); + if(tr == NULL) { + PM_RET_ERR(PM_ERR_TRANS_ABORT, -1); + } + if(trans_init(tr, PM_TRANS_TYPE_UPGRADE, 0, NULL) == -1) { + FREETRANS(tr); + PM_RET_ERR(PM_ERR_TRANS_ABORT, -1); + } + if(remove_loadtarget(db, tr, info->name) == -1) { + FREETRANS(tr); + PM_RET_ERR(PM_ERR_TRANS_ABORT, -1); + } + if(remove_commit(db, tr) == -1) { + FREETRANS(tr); + PM_RET_ERR(PM_ERR_TRANS_ABORT, -1); + } + FREETRANS(tr); + } + } else { + /* no previous package version is installed, so this is actually just an + * install + */ + pmo_upgrade = 0; + } + } + if(!pmo_upgrade) { + TRANS_CB(trans, PM_TRANS_CB_ADD_START, info, NULL); + } + + /* Add the package to the database */ + t = time(NULL); + + /* Update the requiredby field by scaning the whole database + * looking for packages depending on the package to add */ + for(lp = db_get_pkgcache(db); lp; lp = lp->next) { + pmpkg_t *tmpp = NULL; + PMList *tmppm = NULL; + + tmpp = db_scan(db, ((pmpkg_t *)lp->data)->name, INFRQ_DEPENDS); + if(tmpp == NULL) { + continue; + } + for(tmppm = tmpp->depends; tmppm; tmppm = tmppm->next) { + pmdepend_t depend; + splitdep(tmppm->data, &depend); + if(tmppm->data && !strcmp(depend.name, info->name)) { + info->requiredby = pm_list_add(info->requiredby, strdup(tmpp->name)); + continue; + } + } + } + + _alpm_log(PM_LOG_FLOW2, "updating database..."); + /* Figure out whether this package was installed explicitly by the user + * or installed as a dependency for another package + */ + /* ORE + info->reason = PM_PKG_REASON_EXPLICIT; + if(pm_list_is_strin(dependonly, info->data)) { + info->reason = PM_PKG_REASON_DEPEND; + }*/ + /* make an install date (in UTC) */ + strncpy(info->installdate, asctime(gmtime(&t)), sizeof(info->installdate)); + if(db_write(db, info, INFRQ_ALL)) { + _alpm_log(PM_LOG_ERROR, "could not update database for %s", info->name); + alpm_logaction(NULL, "error updating database for %s!", info->name); + PM_RET_ERR(PM_ERR_DB_WRITE, -1); + } + + /* update dependency packages' REQUIREDBY fields */ + for(lp = info->depends; lp; lp = lp->next) { + pmpkg_t *depinfo = NULL; + pmdepend_t depend; + + splitdep(lp->data, &depend); + depinfo = db_scan(db, depend.name, INFRQ_DESC|INFRQ_DEPENDS); + if(depinfo == NULL) { + /* look for a provides package */ + PMList *provides = _alpm_db_whatprovides(db, depend.name); + if(provides) { + /* use the first one */ + depinfo = db_scan(db, ((pmpkg_t *)provides->data)->name, INFRQ_DEPENDS); + if(depinfo == NULL) { + PMList *lp; + /* wtf */ + for(lp = provides; lp; lp = lp->next) { + lp->data = NULL; + } + pm_list_free(provides); + continue; + } + } else { + continue; + } + } + depinfo->requiredby = pm_list_add(depinfo->requiredby, strdup(info->name)); + db_write(db, depinfo, INFRQ_DEPENDS); + FREEPKG(depinfo); + } + + /* Extract the .tar.gz package */ + if(tar_open(&tar, info->data, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + PM_RET_ERR(PM_ERR_PKG_OPEN, -1); + } + _alpm_log(PM_LOG_DEBUG, "extracting files..."); + for(i = 0; !th_read(tar); i++) { + int nb = 0; + int notouch = 0; + char *md5_orig = NULL; + char pathname[PATH_MAX]; + struct stat buf; + + strncpy(pathname, th_get_pathname(tar), PATH_MAX); + + if(!strcmp(pathname, ".PKGINFO") || !strcmp(pathname, ".FILELIST")) { + tar_skip_regfile(tar); + continue; + } + + if(!strcmp(pathname, "._install") || !strcmp(pathname, ".INSTALL")) { + /* the install script goes inside the db */ + snprintf(expath, PATH_MAX, "%s/%s-%s/install", db->path, info->name, info->version); + } else { + /* build the new pathname relative to handle->root */ + snprintf(expath, PATH_MAX, "%s%s", handle->root, pathname); + } + + if(!stat(expath, &buf) && !S_ISDIR(buf.st_mode)) { + /* file already exists */ + if(pm_list_is_strin(pathname, handle->noupgrade)) { + notouch = 1; + } else { + if(!pmo_upgrade || oldpkg == NULL) { + nb = pm_list_is_strin(pathname, info->backup) ? 1 : 0; + } else { + /* op == PM_UPGRADE */ + if((md5_orig = _alpm_needbackup(pathname, oldpkg->backup)) != 0) { + nb = 1; + } + } + } + } + + if(nb) { + char *temp; + char *md5_local, *md5_pkg; + + md5_local = MDFile(expath); + /* extract the package's version to a temporary file and md5 it */ + temp = strdup("/tmp/pacman_XXXXXX"); + mkstemp(temp); + if(tar_extract_file(tar, temp)) { + alpm_logaction("could not extract %s: %s", pathname, strerror(errno)); + errors++; + continue; + } + md5_pkg = MDFile(temp); + /* append the new md5 hash to it's respective entry in info->backup + * (it will be the new orginal) + */ + for(lp = info->backup; lp; lp = lp->next) { + char *fn; + + if(!lp->data) continue; + if(!strcmp((char*)lp->data, pathname)) { + /* 32 for the hash, 1 for the terminating NULL, and 1 for the tab delimiter */ + MALLOC(fn, strlen(lp->data)+34); + sprintf(fn, "%s\t%s", (char*)lp->data, md5_pkg); + FREE(lp->data); + lp->data = fn; + } + } + + _alpm_log(PM_LOG_FLOW2, " checking md5 hashes for %s", expath); + _alpm_log(PM_LOG_FLOW2, " current: %s", md5_local); + _alpm_log(PM_LOG_FLOW2, " new: %s", md5_pkg); + if(md5_orig) { + _alpm_log(PM_LOG_FLOW2, " original: %s", md5_orig); + } + + if(!pmo_upgrade) { + /* PM_ADD */ + + /* if a file already exists with a different md5 hash, + * then we rename it to a .pacorig extension and continue */ + if(strcmp(md5_local, md5_pkg)) { + char newpath[PATH_MAX]; + snprintf(newpath, PATH_MAX, "%s.pacorig", expath); + if(rename(expath, newpath)) { + _alpm_log(PM_LOG_ERROR, "could not rename %s: %s", expath, strerror(errno)); + alpm_logaction("error: could not rename %s: %s", expath, strerror(errno)); + } + if(_alpm_copyfile(temp, expath)) { + _alpm_log(PM_LOG_ERROR, "could not copy %s to %s: %s", temp, expath, strerror(errno)); + alpm_logaction("error: could not copy %s to %s: %s", temp, expath, strerror(errno)); + errors++; + } else { + _alpm_log(PM_LOG_WARNING, "warning: %s saved as %s", expath, newpath); + alpm_logaction("warning: %s saved as %s", expath, newpath); + } + } + } else if(md5_orig) { + /* PM_UPGRADE */ + int installnew = 0; + + /* the fun part */ + if(!strcmp(md5_orig, md5_local)) { + if(!strcmp(md5_local, md5_pkg)) { + _alpm_log(PM_LOG_FLOW2, " action: installing new file"); + installnew = 1; + } else { + _alpm_log(PM_LOG_FLOW2, " action: installing new file"); + installnew = 1; + } + } else if(!strcmp(md5_orig, md5_pkg)) { + _alpm_log(PM_LOG_FLOW2, " action: leaving existing file in place"); + } else if(!strcmp(md5_local, md5_pkg)) { + _alpm_log(PM_LOG_FLOW2, " action: installing new file"); + installnew = 1; + } else { + char newpath[PATH_MAX]; + _alpm_log(PM_LOG_FLOW2, " action: saving current file and installing new one"); + installnew = 1; + snprintf(newpath, PATH_MAX, "%s.pacsave", expath); + if(rename(expath, newpath)) { + _alpm_log(PM_LOG_ERROR, "could not rename %s: %s", expath, strerror(errno)); + alpm_logaction("error: could not rename %s: %s", expath, strerror(errno)); + } else { + _alpm_log(PM_LOG_WARNING, "warning: %s saved as %s", expath, newpath); + alpm_logaction("warning: %s saved as %s", expath, newpath); + } + } + + if(installnew) { + /*_alpm_log(PM_LOG_FLOW2, " %s", expath);*/ + if(_alpm_copyfile(temp, expath)) { + _alpm_log(PM_LOG_ERROR, "could not copy %s to %s: %s", temp, expath, strerror(errno)); + errors++; + } + } + } + + FREE(md5_local); + FREE(md5_pkg); + FREE(md5_orig); + unlink(temp); + FREE(temp); + } else { + if(!notouch) { + _alpm_log(PM_LOG_FLOW2, "%s", pathname); + } else { + _alpm_log(PM_LOG_FLOW2, "%s is in NoUpgrade - skipping", pathname); + strncat(expath, ".pacnew", PATH_MAX); + _alpm_log(PM_LOG_WARNING, "warning: extracting %s%s as %s", handle->root, pathname, expath); + alpm_logaction("warning: extracting %s%s as %s", handle->root, pathname, expath); + tar_skip_regfile(tar); + } + if(trans->flags & PM_TRANS_FLAG_FORCE) { + /* if FORCE was used, then unlink() each file (whether it's there + * or not) before extracting. this prevents the old "Text file busy" + * error that crops up if one tries to --force a glibc or pacman + * upgrade. + */ + unlink(expath); + } + if(tar_extract_file(tar, expath)) { + _alpm_log(PM_LOG_ERROR, "could not extract %s: %s", pathname, strerror(errno)); + alpm_logaction("could not extract %s: %s", pathname, strerror(errno)); + errors++; + } + /* calculate an md5 hash if this is in info->backup */ + for(lp = info->backup; lp; lp = lp->next) { + char *fn, *md5; + char path[PATH_MAX]; + + if(!lp->data) continue; + if(!strcmp((char*)lp->data, pathname)) { + snprintf(path, PATH_MAX, "%s%s", handle->root, (char*)lp->data); + md5 = MDFile(path); + /* 32 for the hash, 1 for the terminating NULL, and 1 for the tab delimiter */ + MALLOC(fn, strlen(lp->data)+34); + sprintf(fn, "%s\t%s", (char*)lp->data, md5); + FREE(lp->data); + lp->data = fn; + } + } + } + } + tar_close(tar); + + if(errors) { + ret = 1; + _alpm_log(PM_LOG_ERROR, "errors occurred while %s %s", + (pmo_upgrade ? "upgrading" : "installing"), info->name); + alpm_logaction("errors occurred while %s %s", + (pmo_upgrade ? "upgrading" : "installing"), info->name); + } + + /* run the post-install script if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version); + if(pmo_upgrade) { + _alpm_runscriptlet(handle->root, pm_install, "post_upgrade", info->version, oldpkg ? oldpkg->version : NULL); + } else { + _alpm_runscriptlet(handle->root, pm_install, "post_install", info->version, NULL); + } + + if(pmo_upgrade && oldpkg) { + TRANS_CB(trans, PM_TRANS_CB_UPGRADE_DONE, info, NULL); + alpm_logaction("upgraded %s (%s -> %s)", info->name, + oldpkg->version, info->version); + } else { + TRANS_CB(trans, PM_TRANS_CB_ADD_DONE, info, NULL); + alpm_logaction("installed %s (%s)", info->name, info->version); + } + + FREEPKG(oldpkg); + } + + /* run ldconfig if it exists */ + _alpm_log(PM_LOG_FLOW2, "running \"%ssbin/ldconfig -r %s\"", handle->root, handle->root); + _alpm_ldconfig(handle->root); + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/add.h b/lib/libalpm/add.h new file mode 100644 index 00000000..cc578537 --- /dev/null +++ b/lib/libalpm/add.h @@ -0,0 +1,34 @@ +/* + * add.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_ADD_H +#define _ALPM_ADD_H + +#include "list.h" +#include "db.h" +#include "trans.h" + +int add_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name); +int add_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data); +int add_commit(pmdb_t *db, pmtrans_t *trans); + +#endif /* _ALPM_ADD_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/alpm.c b/lib/libalpm/alpm.c new file mode 100644 index 00000000..23528ae4 --- /dev/null +++ b/lib/libalpm/alpm.c @@ -0,0 +1,577 @@ +/* + * alpm.c + * + * Copyright (c) 2002 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <time.h> +#include <syslog.h> +#include <limits.h> /* PATH_MAX */ +#include <stdarg.h> +/* pacman */ +#include "log.h" +#include "error.h" +#include "rpmvercmp.h" +#include "md5.h" +#include "list.h" +#include "package.h" +#include "group.h" +#include "util.h" +#include "db.h" +#include "cache.h" +#include "deps.h" +#include "backup.h" +#include "add.h" +#include "remove.h" +#include "sync.h" +#include "handle.h" +#include "alpm.h" + +pmhandle_t *handle = NULL; + +enum __pmerrno_t pm_errno; + +/* + * Library + */ + +int alpm_initialize(char *root) +{ + char str[PATH_MAX]; + + ASSERT(handle == NULL, PM_RET_ERR(PM_ERR_HANDLE_NOT_NULL, -1)); + + handle = handle_new(); + + /* lock db */ + if(handle->access == PM_ACCESS_RW) { + if(_alpm_lckmk(PACLOCK) == -1) { + FREE(handle); + PM_RET_ERR(PM_ERR_HANDLE_LOCK, -1); + } + } + + strncpy(str, (root) ? root : PACROOT, PATH_MAX); + /* add a trailing '/' if there isn't one */ + if(str[strlen(str)-1] != '/') { + strcat(str, "/"); + } + handle->root = strdup(str); + + return(0); +} + +int alpm_release() +{ + PMList *i; + + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + /* unlock db */ + if(handle->access == PM_ACCESS_RW) { + if(_alpm_lckrm(PACLOCK)) { + _alpm_log(PM_LOG_WARNING, "could not remove lock file %s", PACLOCK); + alpm_logaction("warning: could not remove lock file %s", PACLOCK); + } + } + + /* close local database */ + if(handle->db_local) { + db_close(handle->db_local); + handle->db_local = NULL; + } + /* and also sync ones */ + for(i = handle->dbs_sync; i; i = i->next) { + if(i->data) { + db_close(i->data); + i->data = NULL; + } + } + + FREEHANDLE(handle); + + return(0); +} + +/* + * Options + */ + +int alpm_set_option(unsigned char parm, unsigned long data) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + return(handle_set_option(handle, parm, data)); +} + +int alpm_get_option(unsigned char parm, long *data) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + return(handle_get_option(handle, parm, data)); +} + +/* + * Databases + */ + +int alpm_db_register(char *treename, PM_DB **db) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(treename != NULL && strlen(treename) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* check if the db if already registered */ + *db = db_open(handle->root, handle->dbpath, treename); + if(*db == NULL) { + /* couldn't open the db directory - try creating it */ + if(db_create(handle->root, handle->dbpath, treename) == -1) { + PM_RET_ERR(PM_ERR_DB_CREATE, -1); + } + *db = db_open(handle->root, handle->dbpath, treename); + if(*db == NULL) { + /* couldn't open the db directory, try creating it */ + PM_RET_ERR(PM_ERR_DB_OPEN, -1); + } + } + + if(strcmp(treename, "local") == 0) { + handle->db_local = *db; + } else { + handle->dbs_sync = pm_list_add(handle->dbs_sync, *db); + } + + return(0); +} + +int alpm_db_unregister(PM_DB *db) +{ + PMList *i; + int found = 0; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if(db == handle->db_local) { + db_close(handle->db_local); + handle->db_local = NULL; + found = 1; + } else { + for(i = handle->dbs_sync; i && !found; i = i->next) { + if(db == i->data) { + db_close(i->data); + i->data = NULL; + /* ORE + it should be _alpm_list_removed instead */ + found = 1; + } + } + } + + if(!found) { + PM_RET_ERR(PM_ERR_DB_NOT_FOUND, -1); + } + + return(0); +} + +PM_PKG *alpm_db_readpkg(PM_DB *db, char *name) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(db != NULL, return(NULL)); + ASSERT(name != NULL && strlen(name) != 0, return(NULL)); + + return(db_get_pkgfromcache(db, name)); +} + +PM_LIST *alpm_db_getpkgcache(PM_DB *db) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(db != NULL, return(NULL)); + + return(db_get_pkgcache(db)); +} + +PM_GRP *alpm_db_readgrp(PM_DB *db, char *name) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(db != NULL, return(NULL)); + ASSERT(name != NULL && strlen(name) != 0, return(NULL)); + + return(db_get_grpfromcache(db, name)); +} + +PM_LIST *alpm_db_getgrpcache(PM_DB *db) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(db != NULL, return(NULL)); + + return(db_get_grpcache(db)); +} + +PM_LIST *alpm_db_nextgrp(PM_LIST *cache) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(cache != NULL, return(NULL)); + + return(cache->next); +} + +PM_GRP *alpm_db_getgrp(PM_LIST *cache) +{ + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(cache != NULL, return(NULL)); + + return(cache->data); +} + +/* + * Packages + */ + +void *alpm_pkg_getinfo(PM_PKG *pkg, unsigned char parm) +{ + void *data; + + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(pkg != NULL, return(NULL)); + + /* Update the cache package entry if needed */ + if(pkg->origin == PKG_FROM_CACHE && pkg->data == handle->db_local) { + switch(parm) { + case PM_PKG_FILES: + case PM_PKG_BACKUP: + if(!(pkg->infolevel & INFRQ_FILES)) { + char target[321]; /* 256+1+64 */ + + snprintf(target, 321, "%s-%s", pkg->name, pkg->version); + db_read(pkg->data, target, INFRQ_FILES, pkg); + } + break; + + case PM_PKG_SCRIPLET: + if(!(pkg->infolevel & INFRQ_SCRIPLET)) { + char target[321]; + + snprintf(target, 321, "%s-%s", pkg->name, pkg->version); + db_read(pkg->data, target, INFRQ_SCRIPLET, pkg); + } + break; + } + } + + switch(parm) { + case PM_PKG_NAME: data = pkg->name; break; + case PM_PKG_VERSION: data = pkg->version; break; + case PM_PKG_DESC: data = pkg->desc; break; + case PM_PKG_GROUPS: data = pkg->groups; break; + case PM_PKG_URL: data = pkg->url; break; + case PM_PKG_LICENSE: data = pkg->license; break; + case PM_PKG_ARCH: data = pkg->arch; break; + case PM_PKG_BUILDDATE: data = pkg->builddate; break; + case PM_PKG_INSTALLDATE: data = pkg->installdate; break; + case PM_PKG_PACKAGER: data = pkg->packager; break; + case PM_PKG_SIZE: data = (void *)pkg->size; break; + case PM_PKG_REASON: data = (void *)(int)pkg->reason; break; + case PM_PKG_REPLACES: data = pkg->replaces; break; + case PM_PKG_MD5SUM: data = pkg->md5sum; break; + case PM_PKG_DEPENDS: data = pkg->depends; break; + case PM_PKG_REQUIREDBY: data = pkg->requiredby; break; + case PM_PKG_PROVIDES: data = pkg->provides; break; + case PM_PKG_CONFLICTS: data = pkg->conflicts; break; + case PM_PKG_FILES: data = pkg->files; break; + case PM_PKG_BACKUP: data = pkg->backup; break; + case PM_PKG_SCRIPLET: data = (void *)(int)pkg->scriptlet; break; + default: + data = NULL; + break; + } + + return(data); +} + +int alpm_pkg_load(char *filename, PM_PKG **pkg) +{ + /* Sanity checks */ + ASSERT(filename != NULL && strlen(filename) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + ASSERT(pkg != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + *pkg = pkg_load(filename); + if(*pkg == NULL) { + /* pm_errno is set by pkg_load */ + return(-1); + } + + return(0); +} + +int alpm_pkg_free(PM_PKG *pkg) +{ + /* Sanity checks */ + ASSERT(pkg != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + pkg_free(pkg); + + return(0); +} + +int alpm_pkg_vercmp(const char *ver1, const char *ver2) +{ + return(rpmvercmp(ver1, ver2)); +} + +/* + * Groups + */ + +void *alpm_grp_getinfo(PM_GRP *grp, unsigned char parm) +{ + void *data; + + /* Sanity checks */ + ASSERT(grp != NULL, return(NULL)); + + switch(parm) { + case PM_GRP_NAME: data = grp->name; break; + case PM_GRP_PKGNAMES: data = grp->packages; break; + default: + data = NULL; + break; + } + + return(data); +} + +/* + * Sync operations + */ + +void *alpm_sync_getinfo(PM_SYNC *sync, unsigned char parm) +{ + void *data; + + /* Sanity checks */ + ASSERT(sync != NULL, return(NULL)); + + switch(parm) { + case PM_SYNC_TYPE: data = (void *)(int)sync->type; break; + case PM_SYNC_LOCALPKG: data = sync->lpkg; break; + case PM_SYNC_SYNCPKG: data = sync->spkg; break; + default: + data = NULL; + break; + } + + return(data); +} + +int alpm_sync_sysupgrade(PM_LIST **data) +{ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + return(sync_sysupgrade(data)); +} + +/* + * Transactions + */ + +void *alpm_trans_getinfo(unsigned char parm) +{ + pmtrans_t *trans; + void *data; + + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + + trans = handle->trans; + + switch(parm) { + case PM_TRANS_TYPE: data = (void *)(int)trans->type; break; + case PM_TRANS_FLAGS: data = (void *)(int)trans->flags; break; + case PM_TRANS_TARGETS: data = trans->targets; break; + default: + data = NULL; + break; + } + + return(data); +} + +int alpm_trans_init(unsigned char type, unsigned char flags, alpm_trans_cb cb) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + ASSERT(handle->trans == NULL, PM_RET_ERR(PM_ERR_TRANS_NOT_NULL, -1)); + + handle->trans = trans_new(); + if(handle->trans == NULL) { + PM_RET_ERR(PM_ERR_MEMORY, -1); + } + + return(trans_init(handle->trans, type, flags, cb)); +} + +int alpm_trans_addtarget(char *target) +{ + pmtrans_t *trans; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(target != NULL && strlen(target) != 0, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + trans = handle->trans; + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(trans->state == STATE_INITIALIZED, PM_RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); + + return(trans_addtarget(trans, target)); +} + +int alpm_trans_prepare(PMList **data) +{ + pmtrans_t *trans; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + trans = handle->trans; + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(trans->state == STATE_INITIALIZED, PM_RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); + + return(trans_prepare(handle->trans, data)); +} + +int alpm_trans_commit() +{ + pmtrans_t *trans; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + trans = handle->trans; + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(trans->state == STATE_PREPARED, PM_RET_ERR(PM_ERR_TRANS_NOT_PREPARED, -1)); + + /* ORE + ASSERT(handle->access != PM_ACCESS_RW, PM_RET_ERR(PM_ERR_BAD_PERMS, -1));*/ + + return(trans_commit(handle->trans)); +} + +int alpm_trans_release() +{ + pmtrans_t *trans; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + trans = handle->trans; + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(trans->state != STATE_IDLE, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + + FREETRANS(handle->trans); + + return(0); +} + +/* + * Log facilities + */ + +int alpm_logaction(char *fmt, ...) +{ + char str[256]; + va_list args; + + va_start(args, fmt); + vsnprintf(str, 256, fmt, args); + va_end(args); + + return(_alpm_log_action(handle->usesyslog, handle->logfd, str)); +} + +/* + * Lists wrappers + */ + +PM_LIST *alpm_list_first(PM_LIST *list) +{ + ASSERT(list != NULL, return(NULL)); + + return(list); +} + +PM_LIST *alpm_list_next(PM_LIST *entry) +{ + ASSERT(entry != NULL, return(NULL)); + + return(entry->next); +} + +void *alpm_list_getdata(PM_LIST *entry) +{ + ASSERT(entry != NULL, return(NULL)); + + return(entry->data); +} + +int alpm_list_free(PM_LIST *entry) +{ + if(entry) { + /* ORE + does not free all memory for packages... */ + pm_list_free(entry); + } + + return(0); +} + +/* + * Misc wrappers + */ + +char *alpm_get_md5sum(char *name) +{ + ASSERT(name != NULL, return(NULL)); + + return(MDFile(name)); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h new file mode 100644 index 00000000..85442196 --- /dev/null +++ b/lib/libalpm/alpm.h @@ -0,0 +1,330 @@ +/* + * alpm.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_H +#define _ALPM_H + +/* + * Arch Linux Package Management library + */ + +/* + * Structures (opaque) + */ + +typedef struct __pmlist_t PM_LIST; +typedef struct __pmdb_t PM_DB; +typedef struct __pmpkg_t PM_PKG; +typedef struct __pmgrp_t PM_GRP; +typedef struct __pmsync_t PM_SYNC; +typedef struct __pmtrans_t PM_TRANS; +/* ORE +typedef struct __pmdepend_t PM_DEP; +typedef struct __pmdepmissing_t PM_DEPMISS; */ + +/* + * Library + */ + +/* Version */ +#define ALPM_VERSION "0.1.0" + +int alpm_initialize(char *root); +int alpm_release(); + +/* + * Logging facilities + */ + +/* Levels */ +#define PM_LOG_DEBUG 0x01 +#define PM_LOG_ERROR 0x02 +#define PM_LOG_WARNING 0x04 +#define PM_LOG_FLOW1 0x08 +#define PM_LOG_FLOW2 0x10 +#define PM_LOG_FUNCTION 0x20 + +int alpm_logaction(char *fmt, ...); + +/* + * Options + */ + +/* Parameters */ +enum { + PM_OPT_LOGCB = 1, + PM_OPT_LOGMASK, + PM_OPT_USESYSLOG, + PM_OPT_ROOT, + PM_OPT_DBPATH, + PM_OPT_LOGFILE, + PM_OPT_LOCALDB, + PM_OPT_SYNCDB, + PM_OPT_NOUPGRADE, + PM_OPT_IGNOREPKG, + PM_OPT_HOLDPKG +}; + +int alpm_set_option(unsigned char parm, unsigned long data); +int alpm_get_option(unsigned char parm, long *data); + +/* + * Databases + */ + +int alpm_db_register(char *treename, PM_DB **db); +int alpm_db_unregister(PM_DB *db); + +PM_PKG *alpm_db_readpkg(PM_DB *db, char *name); +PM_LIST *alpm_db_getpkgcache(PM_DB *db); + +PM_GRP *alpm_db_readgrp(PM_DB *db, char *name); +PM_LIST *alpm_db_getgrpcache(PM_DB *db); + +/* + * Packages + */ + +/* Info parameters */ +enum { + /* Desc entry */ + PM_PKG_NAME = 1, + PM_PKG_VERSION, + PM_PKG_DESC, + PM_PKG_GROUPS, + PM_PKG_URL, + PM_PKG_LICENSE, + PM_PKG_ARCH, + PM_PKG_BUILDDATE, + PM_PKG_INSTALLDATE, + PM_PKG_PACKAGER, + PM_PKG_SIZE, + PM_PKG_REASON, + PM_PKG_REPLACES, /* Sync DB only */ + PM_PKG_MD5SUM, /* Sync DB only */ + /* Depends entry */ + PM_PKG_DEPENDS, + PM_PKG_REQUIREDBY, + PM_PKG_CONFLICTS, + PM_PKG_PROVIDES, + /* Files entry */ + PM_PKG_FILES, + PM_PKG_BACKUP, + /* Sciplet */ + PM_PKG_SCRIPLET +}; + +/* reasons -- ie, why the package was installed */ +#define PM_PKG_REASON_EXPLICIT 0 /* explicitly requested by the user */ +#define PM_PKG_REASON_DEPEND 1 /* installed as a dependency for another package */ + +void *alpm_pkg_getinfo(PM_PKG *pkg, unsigned char parm); +int alpm_pkg_load(char *filename, PM_PKG **pkg); +int alpm_pkg_free(PM_PKG *pkg); +int alpm_pkg_vercmp(const char *ver1, const char *ver2); + +/* + * Groups + */ + +/* Info parameters */ +enum { + PM_GRP_NAME = 1, + PM_GRP_PKGNAMES +}; + +void *alpm_grp_getinfo(PM_GRP *grp, unsigned char parm); + +/* + * Sync + */ + +/* Types */ +enum { + PM_SYSUPG_REPLACE = 1, + PM_SYSUPG_UPGRADE, + PM_SYSUPG_DEPEND +}; +/* Info parameters */ +enum { + PM_SYNC_TYPE = 1, + PM_SYNC_LOCALPKG, + PM_SYNC_SYNCPKG +}; + +void *alpm_sync_getinfo(PM_SYNC *sync, unsigned char parm); +int alpm_sync_sysupgrade(PM_LIST **data); + +/* + * Transactions + */ + +/* Types */ +enum { + PM_TRANS_TYPE_ADD = 1, + PM_TRANS_TYPE_REMOVE, + PM_TRANS_TYPE_UPGRADE, + PM_TRANS_TYPE_SYNC +}; + +/* Flags */ +#define PM_TRANS_FLAG_NODEPS 0x01 +#define PM_TRANS_FLAG_FORCE 0x02 +#define PM_TRANS_FLAG_NOSAVE 0x04 +#define PM_TRANS_FLAG_FRESHEN 0x08 +#define PM_TRANS_FLAG_CASCADE 0x10 +#define PM_TRANS_FLAG_RECURSE 0x20 +#define PM_TRANS_FLAG_DBONLY 0x40 + +/* Callback events */ +enum { + PM_TRANS_CB_DEPS_START = 1, + PM_TRANS_CB_DEPS_DONE, + PM_TRANS_CB_CONFLICTS_START, + PM_TRANS_CB_CONFLICTS_DONE, + PM_TRANS_CB_ADD_START, + PM_TRANS_CB_ADD_DONE, + PM_TRANS_CB_REMOVE_START, + PM_TRANS_CB_REMOVE_DONE, + PM_TRANS_CB_UPGRADE_START, + PM_TRANS_CB_UPGRADE_DONE +}; + +/* Callback */ +typedef void (*alpm_trans_cb)(unsigned short, void *, void *); + +/* Info parameters */ +enum { + PM_TRANS_TYPE = 1, + PM_TRANS_FLAGS, + PM_TRANS_TARGETS +}; + +/* Dependencies */ +enum { + PM_DEP_ANY = 1, + PM_DEP_EQ, + PM_DEP_GE, + PM_DEP_LE +}; +enum { + PM_DEP_DEPEND = 1, + PM_DEP_REQUIRED, + PM_DEP_CONFLICT +}; + +/* ORE +to be deprecated in favor of PM_DEP and PM_DEPMISS (opaque) */ +typedef struct __pmdepend_t { + unsigned short mod; + char name[256]; + char version[64]; +} pmdepend_t; + +typedef struct __pmdepmissing_t { + unsigned char type; + char target[256]; + pmdepend_t depend; +} pmdepmissing_t; + +void *alpm_trans_getinfo(unsigned char parm); +int alpm_trans_init(unsigned char type, unsigned char flags, alpm_trans_cb cb); +int alpm_trans_addtarget(char *target); +int alpm_trans_prepare(PM_LIST **data); +int alpm_trans_commit(); +int alpm_trans_release(); + +/* + * PM_LIST helpers + */ +PM_LIST *alpm_list_first(PM_LIST *list); +PM_LIST *alpm_list_next(PM_LIST *entry); +void *alpm_list_getdata(PM_LIST *entry); +int alpm_list_free(PM_LIST *entry); + +/* + * Helpers + */ + +char *alpm_get_md5sum(char *name); + +/* + * Errors + */ + +extern enum __pmerrno_t { + PM_ERR_NOERROR = 1, + PM_ERR_MEMORY, + PM_ERR_SYSTEM, + PM_ERR_BADPERMS, + PM_ERR_NOT_A_FILE, + PM_ERR_WRONG_ARGS, + /* Interface */ + PM_ERR_HANDLE_NULL, + PM_ERR_HANDLE_NOT_NULL, + PM_ERR_HANDLE_LOCK, + /* Databases */ + PM_ERR_DB_OPEN, + PM_ERR_DB_CREATE, + PM_ERR_DB_NULL, + PM_ERR_DB_NOT_FOUND, + PM_ERR_DB_NOT_NULL, + PM_ERR_DB_WRITE, + /* Cache */ + PM_ERR_CACHE_NULL, + /* Configuration */ + PM_ERR_OPT_LOGFILE, + PM_ERR_OPT_DBPATH, + PM_ERR_OPT_LOCALDB, + PM_ERR_OPT_SYNCDB, + PM_ERR_OPT_USESYSLOG, + /* Transactions */ + PM_ERR_TRANS_NOT_NULL, + PM_ERR_TRANS_NULL, + PM_ERR_TRANS_DUP_TARGET, + PM_ERR_TRANS_INITIALIZED, + PM_ERR_TRANS_NOT_INITIALIZED, + PM_ERR_TRANS_NOT_PREPARED, + PM_ERR_TRANS_ABORT, + /* Packages */ + PM_ERR_PKG_NOT_FOUND, + PM_ERR_PKG_INVALID, + PM_ERR_PKG_OPEN, + PM_ERR_PKG_LOAD, + PM_ERR_PKG_INSTALLED, + PM_ERR_PKG_CANT_FRESH, + PM_ERR_INVALID_NAME, + /* Groups */ + PM_ERR_GRP_NOT_FOUND, + /* Dependencies */ + PM_ERR_UNSATISFIED_DEPS, + PM_ERR_CONFLICTING_DEPS, + PM_ERR_UNRESOLVABLE_DEPS, + PM_ERR_FILE_CONFLICTS, + /* Misc */ + PM_ERR_USER_ABORT, + PM_ERR_INTERNAL_ERROR +} pm_errno; + +char *alpm_strerror(int err); + +#endif /* _ALPM_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/backup.c b/lib/libalpm/backup.c new file mode 100644 index 00000000..f664277e --- /dev/null +++ b/lib/libalpm/backup.c @@ -0,0 +1,63 @@ +/* + * backup.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <string.h> +/* pacman */ +#include "backup.h" + +/* Look for a filename in a pkginfo_t.backup list. If we find it, + * then we return the md5 hash (parsed from the same line) + */ +char *_alpm_needbackup(char* file, PMList *backup) +{ + PMList *lp; + + if(file == NULL || backup == NULL) { + return(NULL); + } + + /* run through the backup list and parse out the md5 hash for our file */ + for(lp = backup; lp; lp = lp->next) { + char *str = strdup(lp->data); + char *ptr; + + /* tab delimiter */ + ptr = strchr(str, '\t'); + if(ptr == NULL) { + free(str); + continue; + } + *ptr = '\0'; + ptr++; + /* now str points to the filename and ptr points to the md5 hash */ + if(!strcmp(file, str)) { + free(str); + return(strdup(ptr)); + } + free(str); + } + + return(NULL); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/backup.h b/lib/libalpm/backup.h new file mode 100644 index 00000000..af22e1e5 --- /dev/null +++ b/lib/libalpm/backup.h @@ -0,0 +1,30 @@ +/* + * backup.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_BACKUP_H +#define _ALPM_BACKUP_H + +#include "list.h" + +char *_alpm_needbackup(char* file, PMList *backup); + +#endif /* _ALPM_BACKUP_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/cache.c b/lib/libalpm/cache.c new file mode 100644 index 00000000..c6b298eb --- /dev/null +++ b/lib/libalpm/cache.c @@ -0,0 +1,203 @@ +/* + * cache.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +/* pacman */ +#include "list.h" +#include "package.h" +#include "group.h" +#include "db.h" +#include "cache.h" + +/* Returns a new package cache from db. + * It frees the cache if it already exists. + */ +int db_load_pkgcache(pmdb_t *db) +{ + pmpkg_t *info; + + if(db == NULL) { + return(-1); + } + + db_free_pkgcache(db); + + db_rewind(db); + while((info = db_scan(db, NULL, INFRQ_DESC|INFRQ_DEPENDS)) != NULL) { + info->origin = PKG_FROM_CACHE; + info->data = db; + /* add to the collective */ + db->pkgcache = pm_list_add_sorted(db->pkgcache, info, pkg_cmp); + } + + return(0); +} + +void db_free_pkgcache(pmdb_t *db) +{ + if(db == NULL || db->pkgcache == NULL) { + return; + } + + FREELISTPKGS(db->pkgcache); + + if(db->grpcache) { + db_free_grpcache(db); + } +} + +PMList *db_get_pkgcache(pmdb_t *db) +{ + if(db == NULL) { + return(NULL); + } + + if(db->pkgcache == NULL) { + db_load_pkgcache(db); + } + + return(db->pkgcache); +} + +pmpkg_t *db_get_pkgfromcache(pmdb_t *db, char *target) +{ + PMList *i; + + if(db == NULL || target == NULL || strlen(target) == 0) { + return(NULL); + } + + for(i = db_get_pkgcache(db); i; i = i->next) { + pmpkg_t *info = i->data; + + if(strcmp(info->name, target) == 0) { + return(info); + } + } + + return(NULL); +} + +/* Returns a new group cache from db. + * It frees the cache if it already exists. + */ +int db_load_grpcache(pmdb_t *db) +{ + PMList *lp; + + if(db == NULL) { + return(-1); + } + + if(db->pkgcache == NULL) { + db_load_pkgcache(db); + } + + for(lp = db->pkgcache; lp; lp = lp->next) { + PMList *i; + pmpkg_t *pkg = lp->data; + + for(i = pkg->groups; i; i = i->next) { + if(!pm_list_is_strin(i->data, db->grpcache)) { + pmgrp_t *grp = grp_new(); + + strncpy(grp->name, (char *)i->data, 256); + grp->packages = pm_list_add_sorted(grp->packages, pkg->name, grp_cmp); + db->grpcache = pm_list_add_sorted(db->grpcache, grp, grp_cmp); + } else { + PMList *j; + + for(j = db->grpcache; j; j = j->next) { + pmgrp_t *grp = j->data; + + if(strcmp(grp->name, i->data) == 0) { + if(!pm_list_is_strin(pkg->name, grp->packages)) { + grp->packages = pm_list_add_sorted(grp->packages, (char *)pkg->name, grp_cmp); + } + } + } + } + } + } + + return(0); +} + +void db_free_grpcache(pmdb_t *db) +{ + PMList *lg; + + if(db == NULL || db->grpcache == NULL) { + return; + } + + for(lg = db->grpcache; lg; lg = lg->next) { + PMList *lp; + pmgrp_t *grp = lg->data; + + for(lp = grp->packages; lp; lp = lp->next) { + lp->data = NULL; + } + FREELIST(grp->packages); + FREEGRP(lg->data); + } + FREELIST(db->grpcache); +} + +PMList *db_get_grpcache(pmdb_t *db) +{ + if(db == NULL) { + return(NULL); + } + + if(db->grpcache == NULL) { + db_load_grpcache(db); + } + + return(db->grpcache); +} + +pmgrp_t *db_get_grpfromcache(pmdb_t *db, char *target) +{ + PMList *i; + + if(db == NULL || target == NULL || strlen(target) == 0) { + return(NULL); + } + + for(i = db_get_grpcache(db); i; i = i->next) { + pmgrp_t *info = i->data; + + if(strcmp(info->name, target) == 0) { + return(info); + } + } + + return(NULL); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/cache.h b/lib/libalpm/cache.h new file mode 100644 index 00000000..b8897322 --- /dev/null +++ b/lib/libalpm/cache.h @@ -0,0 +1,42 @@ +/* + * cache.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_CACHE_H +#define _ALPM_CACHE_H + +#include "list.h" +#include "package.h" +#include "group.h" +#include "db.h" + +/* packages */ +int db_load_pkgcache(pmdb_t *db); +void db_free_pkgcache(pmdb_t *db); +PMList *db_get_pkgcache(pmdb_t *db); +pmpkg_t *db_get_pkgfromcache(pmdb_t *db, char *target); +/* groups */ +int db_load_grpcache(pmdb_t *db); +void db_free_grpcache(pmdb_t *db); +PMList *db_get_grpcache(pmdb_t *db); +pmgrp_t *db_get_grpfromcache(pmdb_t *db, char *target); + +#endif /* _ALPM_CACHE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/db.c b/lib/libalpm/db.c new file mode 100644 index 00000000..3fad4bb6 --- /dev/null +++ b/lib/libalpm/db.c @@ -0,0 +1,651 @@ +/* + * db.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +/* pacman */ +#include "util.h" +#include "group.h" +#include "cache.h" +#include "db.h" + +/* Open a database and return a pmdb_t handle */ +pmdb_t *db_open(char *root, char *dbpath, char *treename) +{ + pmdb_t *db; + + if(root == NULL || dbpath == NULL || treename == NULL) { + return(NULL); + } + + MALLOC(db, sizeof(pmdb_t)); + + MALLOC(db->path, strlen(root)+strlen(dbpath)+strlen(treename)+2); + sprintf(db->path, "%s%s/%s", root, dbpath, treename); + + db->dir = opendir(db->path); + if(db->dir == NULL) { + FREE(db->path); + FREE(db); + return(NULL); + } + + strncpy(db->treename, treename, sizeof(db->treename)-1); + + db->pkgcache = NULL; + db->grpcache = NULL; + + return(db); +} + +void db_close(pmdb_t *db) +{ + if(db == NULL) { + return; + } + + if(db->dir) { + closedir(db->dir); + db->dir = NULL; + } + FREE(db->path); + + db_free_pkgcache(db); + db_free_grpcache(db); + + free(db); + + return; +} + +int db_create(char *root, char *dbpath, char *treename) +{ + char path[PATH_MAX]; + + if(root == NULL || dbpath == NULL || treename == NULL) { + return(-1); + } + + snprintf(path, PATH_MAX, "%s%s/local", root, dbpath); + if(_alpm_makepath(path) != 0) { + return(-1); + } + + return(0); +} + +void db_rewind(pmdb_t *db) +{ + if(db == NULL || db->dir == NULL) { + return; + } + + rewinddir(db->dir); +} + +pmpkg_t *db_scan(pmdb_t *db, char *target, unsigned int inforeq) +{ + struct dirent *ent = NULL; + char name[256]; + char *ptr = NULL; + int ret, found = 0; + pmpkg_t *pkg; + + if(db == NULL) { + return(NULL); + } + + if(target != NULL) { + /* search for a specific package (by name only) */ + rewinddir(db->dir); + while(!found && (ent = readdir(db->dir)) != NULL) { + if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + continue; + } + strncpy(name, ent->d_name, 255); + /* truncate the string at the second-to-last hyphen, */ + /* which will give us the package name */ + if((ptr = rindex(name, '-'))) { + *ptr = '\0'; + } + if((ptr = rindex(name, '-'))) { + *ptr = '\0'; + } + if(!strcmp(name, target)) { + found = 1; + } + } + if(!found) { + return(NULL); + } + } else { + /* normal iteration */ + ent = readdir(db->dir); + if(ent == NULL) { + return(NULL); + } + if(!strcmp(ent->d_name, ".")) { + ent = readdir(db->dir); + if(ent == NULL) { + return(NULL); + } + } + if(!strcmp(ent->d_name, "..")) { + ent = readdir(db->dir); + if(ent == NULL) { + return(NULL); + } + } + } + + pkg = pkg_new(); + if(pkg == NULL) { + return(NULL); + } + ret = db_read(db, ent->d_name, inforeq, pkg); + if(ret == -1) { + FREEPKG(pkg); + } + + return(ret == 0 ? pkg : NULL); +} + +int db_read(pmdb_t *db, char *name, unsigned int inforeq, pmpkg_t *info) +{ + FILE *fp = NULL; + struct stat buf; + char path[PATH_MAX]; + char line[512]; + + if(db == NULL || name == NULL || info == NULL) { + return(-1); + } + + snprintf(path, PATH_MAX, "%s/%s", db->path, name); + if(stat(path, &buf)) { + /* directory doesn't exist or can't be opened */ + return(-1); + } + + /* DESC */ + if(inforeq & INFRQ_DESC) { + snprintf(path, PATH_MAX, "%s/%s/desc", db->path, name); + fp = fopen(path, "r"); + if(fp == NULL) { + fprintf(stderr, "error: %s: %s\n", path, strerror(errno)); + return(-1); + } + while(!feof(fp)) { + if(fgets(line, 256, fp) == NULL) { + break; + } + _alpm_strtrim(line); + if(!strcmp(line, "%NAME%")) { + if(fgets(info->name, sizeof(info->name), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->name); + } else if(!strcmp(line, "%VERSION%")) { + if(fgets(info->version, sizeof(info->version), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->version); + } else if(!strcmp(line, "%DESC%")) { + if(fgets(info->desc, sizeof(info->desc), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->desc); + } else if(!strcmp(line, "%GROUPS%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + char *s = strdup(line); + info->groups = pm_list_add(info->groups, s); + } + } else if(!strcmp(line, "%URL%")) { + if(fgets(info->url, sizeof(info->url), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->url); + } else if(!strcmp(line, "%LICENSE%")) { + if(fgets(info->license, sizeof(info->license), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->license); + } else if(!strcmp(line, "%ARCH%")) { + if(fgets(info->arch, sizeof(info->arch), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->arch); + } else if(!strcmp(line, "%BUILDDATE%")) { + if(fgets(info->builddate, sizeof(info->builddate), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->builddate); + } else if(!strcmp(line, "%INSTALLDATE%")) { + if(fgets(info->installdate, sizeof(info->installdate), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->installdate); + } else if(!strcmp(line, "%PACKAGER%")) { + if(fgets(info->packager, sizeof(info->packager), fp) == NULL) { + return(-1); + } + _alpm_strtrim(info->packager); + } else if(!strcmp(line, "%REASON%")) { + char tmp[32]; + if(fgets(tmp, sizeof(tmp), fp) == NULL) { + return(-1); + } + _alpm_strtrim(tmp); + info->reason = atol(tmp); + } else if(!strcmp(line, "%SIZE%")) { + char tmp[32]; + if(fgets(tmp, sizeof(tmp), fp) == NULL) { + return(-1); + } + _alpm_strtrim(tmp); + info->size = atol(tmp); + } else if(!strcmp(line, "%CSIZE%")) { + /* NOTE: the CSIZE and SIZE fields both share the "size" field + * in the pkginfo_t struct. This can be done b/c CSIZE + * is currently only used in sync databases, and SIZE is + * only used in local databases. + */ + char tmp[32]; + if(fgets(tmp, sizeof(tmp), fp) == NULL) { + return(-1); + } + _alpm_strtrim(tmp); + info->size = atol(tmp); + } else if(!strcmp(line, "%REPLACES%")) { + /* the REPLACES tag is special -- it only appears in sync repositories, + * not the local one. */ + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->replaces = pm_list_add(info->replaces, strdup(line)); + } + } else if(!strcmp(line, "%MD5SUM%")) { + /* MD5SUM tag only appears in sync repositories, + * not the local one. */ + if(fgets(info->md5sum, sizeof(info->md5sum), fp) == NULL) { + return(-1); + } + } else if(!strcmp(line, "%FORCE%")) { + /* FORCE tag only appears in sync repositories, + * not the local one. */ + info->force = 1; + } + } + fclose(fp); + } + + /* FILES */ + if(inforeq & INFRQ_FILES) { + snprintf(path, PATH_MAX, "%s/%s/files", db->path, name); + fp = fopen(path, "r"); + if(fp == NULL) { + fprintf(stderr, "error: %s: %s\n", path, strerror(errno)); + return(-1); + } + while(fgets(line, 256, fp)) { + _alpm_strtrim(line); + if(!strcmp(line, "%FILES%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->files = pm_list_add(info->files, strdup(line)); + } + } else if(!strcmp(line, "%BACKUP%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->backup = pm_list_add(info->backup, strdup(line)); + } + } + } + fclose(fp); + } + + /* DEPENDS */ + if(inforeq & INFRQ_DEPENDS) { + snprintf(path, PATH_MAX, "%s/%s/depends", db->path, name); + fp = fopen(path, "r"); + if(fp == NULL) { + fprintf(stderr, "error: %s: %s\n", path, strerror(errno)); + return(-1); + } + while(!feof(fp)) { + fgets(line, 255, fp); + _alpm_strtrim(line); + if(!strcmp(line, "%DEPENDS%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->depends = pm_list_add(info->depends, strdup(line)); + } + } else if(!strcmp(line, "%REQUIREDBY%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->requiredby = pm_list_add(info->requiredby, strdup(line)); + } + } else if(!strcmp(line, "%CONFLICTS%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->conflicts = pm_list_add(info->conflicts, strdup(line)); + } + } else if(!strcmp(line, "%PROVIDES%")) { + while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) { + info->provides = pm_list_add(info->provides, strdup(line)); + } + } + } + fclose(fp); + } + + /* INSTALL */ + if(inforeq & INFRQ_SCRIPLET) { + snprintf(path, PATH_MAX, "%s/%s/install", db->path, name); + if(!stat(path, &buf)) { + info->scriptlet = 1; + } + } + + /* internal */ + info->infolevel |= inforeq; + + return(0); +} + +int db_write(pmdb_t *db, pmpkg_t *info, unsigned int inforeq) +{ + char topdir[PATH_MAX]; + FILE *fp = NULL; + char path[PATH_MAX]; + mode_t oldmask; + PMList *lp = NULL; + + if(db == NULL || info == NULL) { + return(-1); + } + + snprintf(topdir, PATH_MAX, "%s/%s-%s", db->path, + info->name, info->version); + oldmask = umask(0000); + mkdir(topdir, 0755); + /* make sure we have a sane umask */ + umask(0022); + + /* DESC */ + if(inforeq & INFRQ_DESC) { + snprintf(path, PATH_MAX, "%s/desc", topdir); + if((fp = fopen(path, "w")) == NULL) { + perror("db_write"); + umask(oldmask); + return(-1); + } + fputs("%NAME%\n", fp); + fprintf(fp, "%s\n\n", info->name); + fputs("%VERSION%\n", fp); + fprintf(fp, "%s\n\n", info->version); + fputs("%DESC%\n", fp); + fprintf(fp, "%s\n\n", info->desc); + fputs("%GROUPS%\n", fp); + for(lp = info->groups; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%URL%\n", fp); + fprintf(fp, "%s\n\n", info->url); + fputs("%LICENSE%\n", fp); + fprintf(fp, "%s\n\n", info->license); + fputs("%ARCH%\n", fp); + fprintf(fp, "%s\n\n", info->arch); + fputs("%BUILDDATE%\n", fp); + fprintf(fp, "%s\n\n", info->builddate); + fputs("%INSTALLDATE%\n", fp); + fprintf(fp, "%s\n\n", info->installdate); + fputs("%PACKAGER%\n", fp); + fprintf(fp, "%s\n\n", info->packager); + fputs("%SIZE%\n", fp); + fprintf(fp, "%ld\n\n", info->size); + fputs("%REASON%\n", fp); + fprintf(fp, "%ld\n\n", info->size); + fclose(fp); + } + + /* FILES */ + if(inforeq & INFRQ_FILES) { + snprintf(path, PATH_MAX, "%s/files", topdir); + if((fp = fopen(path, "w")) == NULL) { + perror("db_write"); + umask(oldmask); + return(-1); + } + fputs("%FILES%\n", fp); + for(lp = info->files; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%BACKUP%\n", fp); + for(lp = info->backup; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fclose(fp); + } + + /* DEPENDS */ + if(inforeq & INFRQ_DEPENDS) { + snprintf(path, PATH_MAX, "%s/depends", topdir); + if((fp = fopen(path, "w")) == NULL) { + perror("db_write"); + umask(oldmask); + return(-1); + } + fputs("%DEPENDS%\n", fp); + for(lp = info->depends; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%REQUIREDBY%\n", fp); + for(lp = info->requiredby; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%CONFLICTS%\n", fp); + for(lp = info->conflicts; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fputs("%PROVIDES%\n", fp); + for(lp = info->provides; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char*)lp->data); + } + fprintf(fp, "\n"); + fclose(fp); + } + + /* INSTALL */ + /* nothing needed here (script is automatically extracted) */ + + umask(oldmask); + + return(0); +} + +int db_remove(pmdb_t *db, pmpkg_t *info) +{ + char topdir[PATH_MAX]; + char file[PATH_MAX]; + + if(db == NULL || info == NULL) { + return(-1); + } + + snprintf(topdir, PATH_MAX, "%s/%s-%s", db->path, info->name, info->version); + + /* DESC */ + snprintf(file, PATH_MAX, "%s/desc", topdir); + unlink(file); + /* FILES */ + snprintf(file, PATH_MAX, "%s/files", topdir); + unlink(file); + /* DEPENDS */ + snprintf(file, PATH_MAX, "%s/depends", topdir); + unlink(file); + /* INSTALL */ + snprintf(file, PATH_MAX, "%s/install", topdir); + unlink(file); + /* Package directory */ + if(rmdir(topdir) == -1) { + return(-1); + } + + return(0); +} + +PMList *db_find_conflicts(pmdb_t *db, PMList *targets, char *root) +{ + PMList *i, *j, *k; + char *filestr = NULL; + char path[PATH_MAX+1]; + char *str = NULL; + struct stat buf, buf2; + PMList *conflicts = NULL; + + if(db == NULL || targets == NULL || root == NULL) { + return(NULL); + } + + /* CHECK 1: check every db package against every target package */ + /* XXX: I've disabled the database-against-targets check for now, as the + * many many strcmp() calls slow it down heavily and most of the + * checking is redundant to the targets-against-filesystem check. + * This will be re-enabled if I can improve performance significantly. + * + pmpkg_t *info = NULL; + char *dbstr = NULL; + rewinddir(db->dir); + while((info = db_scan(db, NULL, INFRQ_DESC | INFRQ_FILES)) != NULL) { + for(i = info->files; i; i = i->next) { + if(i->data == NULL) continue; + dbstr = (char*)i->data; + for(j = targets; j; j = j->next) { + pmpkg_t *targ = (pmpkg_t*)j->data; + if(strcmp(info->name, targ->name)) { + for(k = targ->files; k; k = k->next) { + filestr = (char*)k->data; + if(!strcmp(dbstr, filestr)) { + if(rindex(k->data, '/') == filestr+strlen(filestr)-1) { + continue; + } + MALLOC(str, 512); + snprintf(str, 512, "%s: exists in \"%s\" (target) and \"%s\" (installed)", dbstr, + targ->name, info->name); + conflicts = pm_list_add(conflicts, str); + } + } + } + } + } + }*/ + + /* CHECK 2: check every target against every target */ + for(i = targets; i; i = i->next) { + pmpkg_t *p1 = (pmpkg_t*)i->data; + for(j = i; j; j = j->next) { + pmpkg_t *p2 = (pmpkg_t*)j->data; + if(strcmp(p1->name, p2->name)) { + for(k = p1->files; k; k = k->next) { + filestr = k->data; + if(!strcmp(filestr, "._install") || !strcmp(filestr, ".INSTALL")) { + continue; + } + if(rindex(filestr, '/') == filestr+strlen(filestr)-1) { + /* this filename has a trailing '/', so it's a directory -- skip it. */ + continue; + } + if(pm_list_is_strin(filestr, p2->files)) { + MALLOC(str, 512); + snprintf(str, 512, "%s: exists in \"%s\" (target) and \"%s\" (target)", + filestr, p1->name, p2->name); + conflicts = pm_list_add(conflicts, str); + } + } + } + } + } + + /* CHECK 3: check every target against the filesystem */ + for(i = targets; i; i = i->next) { + pmpkg_t *p = (pmpkg_t*)i->data; + pmpkg_t *dbpkg = NULL; + for(j = p->files; j; j = j->next) { + filestr = (char*)j->data; + snprintf(path, PATH_MAX, "%s%s", root, filestr); + if(!stat(path, &buf) && !S_ISDIR(buf.st_mode)) { + int ok = 0; + if(dbpkg == NULL) { + dbpkg = db_scan(db, p->name, INFRQ_DESC | INFRQ_FILES); + } + if(dbpkg && pm_list_is_strin(j->data, dbpkg->files)) { + ok = 1; + } + /* Make sure that the supposedly-conflicting file is not actually just + * a symlink that points to a path that used to exist in the package. + */ + /* Check if any part of the conflicting file's path is a symlink */ + if(dbpkg && !ok) { + char str[PATH_MAX]; + for(k = dbpkg->files; k; k = k->next) { + snprintf(str, PATH_MAX, "%s%s", root, (char*)k->data); + stat(str, &buf2); + if(buf.st_ino == buf2.st_ino) { + ok = 1; + } + } + } + /* Check if the conflicting file has been moved to another package/target */ + if(!ok) { + /* Look at all the targets */ + for(k = targets; k && !ok; k = k->next) { + pmpkg_t *p1 = (pmpkg_t *)k->data; + /* As long as they're not the current package */ + if(strcmp(p1->name, p->name)) { + pmpkg_t *dbpkg2 = NULL; + dbpkg2 = db_scan(db, p1->name, INFRQ_DESC | INFRQ_FILES); + /* If it used to exist in there, but doesn't anymore */ + if(dbpkg2 && !pm_list_is_strin(filestr, p1->files) && pm_list_is_strin(filestr, dbpkg2->files)) { + ok = 1; + } + FREEPKG(dbpkg2); + } + } + } + if(!ok) { + MALLOC(str, 512); + snprintf(str, 512, "%s: exists in filesystem", path); + conflicts = pm_list_add(conflicts, str); + } + } + } + FREEPKG(dbpkg); + } + + return(conflicts); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h new file mode 100644 index 00000000..b4161871 --- /dev/null +++ b/lib/libalpm/db.h @@ -0,0 +1,60 @@ +/* + * db.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_DB_H +#define _ALPM_DB_H + +#include <dirent.h> + +#include "list.h" +#include "package.h" + +/* Database entries */ +#define INFRQ_NONE 0x00 +#define INFRQ_DESC 0x01 +#define INFRQ_DEPENDS 0x02 +#define INFRQ_FILES 0x04 +#define INFRQ_SCRIPLET 0x08 +#define INFRQ_ALL 0xFF + +/* Database */ +typedef struct __pmdb_t { + char *path; + char treename[128]; + DIR *dir; + PMList *pkgcache; + PMList *grpcache; +} pmdb_t; + +pmdb_t *db_open(char *root, char *dbpath, char *treename); +void db_close(pmdb_t *db); +int db_create(char *root, char *dbpath, char *treename); + +void db_rewind(pmdb_t *db); +pmpkg_t *db_scan(pmdb_t *db, char *target, unsigned int inforeq); +int db_read(pmdb_t *db, char *name, unsigned int inforeq, pmpkg_t *info); +int db_write(pmdb_t *db, pmpkg_t *info, unsigned int inforeq); +int db_remove(pmdb_t *db, pmpkg_t *info); + +PMList *db_find_conflicts(pmdb_t *db, PMList *targets, char *root); + +#endif /* _ALPM_DB_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c new file mode 100644 index 00000000..d8b79974 --- /dev/null +++ b/lib/libalpm/deps.c @@ -0,0 +1,685 @@ +/* + * deps.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +/* pacman */ +#include "util.h" +#include "log.h" +#include "list.h" +#include "package.h" +#include "db.h" +#include "cache.h" +#include "provide.h" +#include "deps.h" +#include "rpmvercmp.h" + +/* Re-order a list of target packages with respect to their dependencies. + * + * Example: + * A depends on C + * B depends on A + * Target order is A,B,C,D + * + * Should be re-ordered to C,A,B,D + * + * This function returns the new PMList* target list. + * + */ +PMList *sortbydeps(PMList *targets) +{ + PMList *newtargs = NULL; + PMList *i, *j, *k; + int change = 1; + int numscans = 0; + int numtargs = 0; + int clean = 0; + + if(targets == NULL) { + return(NULL); + } + + /* count the number of targets */ + numtargs = pm_list_count(targets); + + while(change) { + change = 0; + if(numscans > numtargs) { + _alpm_log(PM_LOG_FLOW2, "warning: possible dependency cycle detected\n"); + change = 0; + continue; + } + newtargs = NULL; + numscans++; + /* run thru targets, moving up packages as necessary */ + for(i = targets; i; i = i->next) { + pmpkg_t *p = (pmpkg_t*)i->data; + for(j = p->depends; j; j = j->next) { + pmdepend_t dep; + int found = 0; + pmpkg_t *q = NULL; + + splitdep(j->data, &dep); + /* look for dep.name -- if it's farther down in the list, then + * move it up above p + */ + for(k = i->next; k && !found; k = k->next) { + q = (pmpkg_t*)k->data; + if(!strcmp(dep.name, q->name)) { + found = 1; + } + } + if(found) { + if(!pkg_isin(q, newtargs)) { + change = 1; + newtargs = pm_list_add(newtargs, q); + } + } + } + if(!pkg_isin(p, newtargs)) { + newtargs = pm_list_add(newtargs, p); + } + } + if(clean && change) { + /* free up targets -- it's local now */ + for(i = targets; i; i = i->next) { + i->data = NULL; + } + pm_list_free(targets); + } + targets = newtargs; + clean = 1; + } + return(targets); +} + +/* Returns a PMList* of missing_t pointers. + * + * conflicts are always name only, but dependencies can include versions + * with depmod operators. + * + */ +PMList *checkdeps(pmdb_t *db, unsigned short op, PMList *packages) +{ + pmpkg_t *info = NULL; + pmdepend_t depend; + PMList *i, *j, *k; + int cmp; + int found = 0; + PMList *baddeps = NULL; + pmdepmissing_t *miss = NULL; + + if(db == NULL) { + return(NULL); + } + + if(op == PM_TRANS_TYPE_UPGRADE) { + /* PM_TRANS_TYPE_UPGRADE handles the backwards dependencies, ie, the packages + * listed in the requiredby field. + */ + for(i = packages; i; i = i->next) { + pmpkg_t *tp, *oldpkg; + if(i->data == NULL) { + continue; + } + tp = (pmpkg_t *)i->data; + + if((oldpkg = db_scan(db, tp->name, INFRQ_DESC | INFRQ_DEPENDS)) == NULL) { + continue; + } + for(j = oldpkg->requiredby; j; j = j->next) { + char *ver; + pmpkg_t *p; + found = 0; + if((p = db_scan(db, j->data, INFRQ_DESC | INFRQ_DEPENDS)) == NULL) { + /* hmmm... package isn't installed.. */ + continue; + } + if(pkg_isin(p, packages)) { + /* this package is also in the upgrade list, so don't worry about it */ + continue; + } + for(k = p->depends; k && !found; k = k->next) { + /* find the dependency info in p->depends */ + splitdep(k->data, &depend); + if(!strcmp(depend.name, oldpkg->name)) { + found = 1; + } + } + if(found == 0) { + PMList *lp; + /* look for packages that list depend.name as a "provide" */ + PMList *provides = _alpm_db_whatprovides(db, depend.name); + if(provides == NULL) { + /* not found */ + continue; + } + /* we found an installed package that provides depend.name */ + for(lp = provides; lp; lp = lp->next) { + lp->data = NULL; + } + pm_list_free(provides); + } + found = 0; + if(depend.mod == PM_DEP_ANY) { + found = 1; + } else { + /* note that we use the version from the NEW package in the check */ + ver = strdup(tp->version); + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case PM_DEP_EQ: found = (cmp == 0); break; + case PM_DEP_GE: found = (cmp >= 0); break; + case PM_DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + if(!found) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_REQUIRED; + miss->depend.mod = depend.mod; + strncpy(miss->target, p->name, 256); + strncpy(miss->depend.name, depend.name, 256); + strncpy(miss->depend.version, depend.version, 64); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + pkg_free(oldpkg); + } + } + if(op == PM_TRANS_TYPE_ADD || op == PM_TRANS_TYPE_UPGRADE) { + for(i = packages; i; i = i->next) { + pmpkg_t *tp = i->data; + if(tp == NULL) { + continue; + } + + /* CONFLICTS */ + for(j = tp->conflicts; j; j = j->next) { + /* check targets against database */ + for(k = db_get_pkgcache(db); k; k = k->next) { + pmpkg_t *dp = (pmpkg_t *)k->data; + if(!strcmp(j->data, dp->name)) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_CONFLICT; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, dp->name, 256); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + /* check targets against targets */ + for(k = packages; k; k = k->next) { + pmpkg_t *a = (pmpkg_t *)k->data; + if(!strcmp(a->name, (char *)j->data)) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_CONFLICT; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, a->name, 256); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + } + /* check database against targets */ + for(k = db_get_pkgcache(db); k; k = k->next) { + info = k->data; + for(j = info->conflicts; j; j = j->next) { + if(!strcmp((char *)j->data, tp->name)) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_CONFLICT; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, info->name, 256); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + } + + /* PROVIDES -- check to see if another package already provides what + * we offer + */ + /* XXX: disabled -- we allow multiple packages to provide the same thing. + * list packages in conflicts if they really do conflict. + for(j = tp->provides; j; j = j->next) { + PMList *provs = whatprovides(db, j->data); + for(k = provs; k; k = k->next) { + if(!strcmp(tp->name, k->data->name)) { + // this is the same package -- skip it + continue; + } + // we treat this just like a conflict + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = CONFLICT; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, k->data, 256); + if(!pm_list_is_in(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + k->data = NULL; + } + pm_list_free(provs); + }*/ + + /* DEPENDENCIES -- look for unsatisfied dependencies */ + for(j = tp->depends; j; j = j->next) { + /* split into name/version pairs */ + splitdep((char *)j->data, &depend); + found = 0; + /* check database for literal packages */ + for(k = db_get_pkgcache(db); k && !found; k = k->next) { + pmpkg_t *p = (pmpkg_t *)k->data; + if(!strcmp(p->name, depend.name)) { + if(depend.mod == PM_DEP_ANY) { + /* accept any version */ + found = 1; + } else { + char *ver = strdup(p->version); + /* check for a release in depend.version. if it's + * missing remove it from p->version as well. + */ + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case PM_DEP_EQ: found = (cmp == 0); break; + case PM_DEP_GE: found = (cmp >= 0); break; + case PM_DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + } + /* check other targets */ + for(k = packages; k && !found; k = k->next) { + pmpkg_t *p = (pmpkg_t *)k->data; + /* see if the package names match OR if p provides depend.name */ + if(!strcmp(p->name, depend.name) || pm_list_is_strin(depend.name, p->provides)) { + if(depend.mod == PM_DEP_ANY) { + /* accept any version */ + found = 1; + } else { + char *ver = strdup(p->version); + /* check for a release in depend.version. if it's + * missing remove it from p->version as well. + */ + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case PM_DEP_EQ: found = (cmp == 0); break; + case PM_DEP_GE: found = (cmp >= 0); break; + case PM_DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + } + /* check database for provides matches */ + if(!found){ + PMList *lp; + k = _alpm_db_whatprovides(db, depend.name); + if(k) { + /* grab the first one (there should only really be one, anyway) */ + pmpkg_t *p = db_scan(db, ((pmpkg_t *)k->data)->name, INFRQ_DESC); + if(p == NULL) { + /* wtf */ + fprintf(stderr, "data error: %s supposedly provides %s, but it was not found in db\n", + ((pmpkg_t *)k->data)->name, depend.name); + for(lp = k; lp; lp = lp->next) { + lp->data = NULL; + } + pm_list_free(k); + continue; + } + if(depend.mod == PM_DEP_ANY) { + /* accept any version */ + found = 1; + } else { + char *ver = strdup(p->version); + /* check for a release in depend.version. if it's + * missing remove it from p->version as well. + */ + if(!index(depend.version,'-')) { + char *ptr; + for(ptr = ver; *ptr != '-'; ptr++); + *ptr = '\0'; + } + cmp = rpmvercmp(ver, depend.version); + switch(depend.mod) { + case PM_DEP_EQ: found = (cmp == 0); break; + case PM_DEP_GE: found = (cmp >= 0); break; + case PM_DEP_LE: found = (cmp <= 0); break; + } + FREE(ver); + } + } + for(lp = k; lp; lp = lp->next) { + lp->data = NULL; + } + pm_list_free(k); + } + /* else if still not found... */ + if(!found) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_DEPEND; + miss->depend.mod = depend.mod; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, depend.name, 256); + strncpy(miss->depend.version, depend.version, 64); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + } + } else if(op == PM_TRANS_TYPE_REMOVE) { + /* check requiredby fields */ + for(i = packages; i; i = i->next) { + pmpkg_t *tp; + if(i->data == NULL) { + continue; + } + tp = (pmpkg_t*)i->data; + for(j = tp->requiredby; j; j = j->next) { + if(!pm_list_is_strin((char *)j->data, packages)) { + MALLOC(miss, sizeof(pmdepmissing_t)); + miss->type = PM_DEP_REQUIRED; + miss->depend.mod = PM_DEP_ANY; + miss->depend.version[0] = '\0'; + strncpy(miss->target, tp->name, 256); + strncpy(miss->depend.name, (char *)j->data, 256); + if(!pm_list_is_ptrin(baddeps, miss)) { + baddeps = pm_list_add(baddeps, miss); + } + } + } + } + } + + return(baddeps); +} + +void splitdep(char *depstr, pmdepend_t *depend) +{ + char *str = NULL; + char *ptr = NULL; + + if(depstr == NULL) { + return; + } + + str = strdup(depstr); + + if((ptr = strstr(str, ">="))) { + depend->mod = PM_DEP_GE; + } else if((ptr = strstr(str, "<="))) { + depend->mod = PM_DEP_LE; + } else if((ptr = strstr(str, "="))) { + depend->mod = PM_DEP_EQ; + } else { + /* no version specified - accept any */ + depend->mod = PM_DEP_ANY; + strncpy(depend->name, str, sizeof(depend->name)); + strncpy(depend->version, "", sizeof(depend->version)); + } + + if(ptr == NULL) { + FREE(str); + return; + } + *ptr = '\0'; + strncpy(depend->name, str, sizeof(depend->name)); + ptr++; + if(depend->mod != PM_DEP_EQ) { + ptr++; + } + strncpy(depend->version, ptr, sizeof(depend->version)); + FREE(str); + + return; +} + +/* return a new PMList target list containing all packages in the original + * target list, as well as all their un-needed dependencies. By un-needed, + * I mean dependencies that are *only* required for packages in the target + * list, so they can be safely removed. This function is recursive. + */ +PMList* removedeps(pmdb_t *db, PMList *targs) +{ + PMList *i, *j, *k; + PMList *newtargs = targs; + + if(db == NULL) { + return(newtargs); + } + + for(i = targs; i; i = i->next) { + pmpkg_t *pkg = (pmpkg_t*)i->data; + for(j = pkg->depends; j; j = j->next) { + pmdepend_t depend; + pmpkg_t *dep; + int needed = 0; + splitdep(j->data, &depend); + dep = db_scan(db, depend.name, INFRQ_DESC | INFRQ_DEPENDS); + if(pkg_isin(dep, targs)) { + continue; + } + /* see if it was explicitly installed */ + if(dep->reason == PM_PKG_REASON_EXPLICIT) { + /* ORE + vprint("excluding %s -- explicitly installed\n", dep->name);*/ + needed = 1; + } + /* see if other packages need it */ + for(k = dep->requiredby; k && !needed; k = k->next) { + pmpkg_t *dummy = db_scan(db, k->data, INFRQ_DESC); + if(!pkg_isin(dummy, targs)) { + needed = 1; + } + } + if(!needed) { + /* add it to the target list */ + pkg_free(dep); + dep = db_scan(db, depend.name, INFRQ_ALL); + newtargs = pm_list_add(newtargs, dep); + newtargs = removedeps(db, newtargs); + } + } + } + + return(newtargs); +} + +/* populates *list with packages that need to be installed to satisfy all + * dependencies (recursive) for *syncpkg->pkg + * + * make sure *list and *trail are already initialized + */ +int resolvedeps(pmdb_t *local, PMList *databases, pmsync_t *sync, PMList *list, PMList *trail, PMList **data) +{ + PMList *i, *j; + PMList *targ = NULL; + PMList *deps = NULL; + + targ = pm_list_add(targ, sync->spkg); + deps = checkdeps(local, PM_TRANS_TYPE_ADD, targ); + targ->data = NULL; + pm_list_free(targ); + + if(deps == NULL) { + return(0); + } + + for(i = deps; i; i = i->next) { + int found = 0; + pmdepmissing_t *miss = i->data; + + /* XXX: conflicts are now treated specially in the _add and _sync functions */ + + /*if(miss->type == CONFLICT) { + fprintf(stderr, "error: cannot resolve dependencies for \"%s\":\n", miss->target); + fprintf(stderr, " %s conflicts with %s\n", miss->target, miss->depend.name); + return(1); + } else*/ + + if(miss->type == PM_DEP_DEPEND) { + pmsync_t *sync = NULL; + + /* find the package in one of the repositories */ + + /* check literals */ + for(j = databases; !sync && j; j = j->next) { + PMList *k; + pmdb_t *dbs = j->data; + + for(k = db_get_pkgcache(dbs); !sync && k; k = k->next) { + pmpkg_t *pkg = k->data; + + if(!strcmp(miss->depend.name, pkg->name)) { + sync = sync_new(PM_SYSUPG_DEPEND, NULL, k->data); + if(sync == NULL) { + pm_errno = PM_ERR_MEMORY; + goto error; + } + /* ORE + sync->pkg->reason = PM_PKG_REASON_DEPEND;*/ + } + } + } + + /* check provides */ + /* ORE + for(j = databases; !s && j; j = j->next) { + PMList *provides; + + provides = _alpm_db_whatprovides(j->data, miss->depend.name); + if(provides) { + s = sync_new(PM_SYSUPG_DEPEND, NULL, !!!provides->data!!!); + if(s == NULL) { + pm_errno = PM_ERR_MEMORY; + FREELIST(deps); + return(-1); + } + sync->pkg->reason = PM_PKG_REASON_DEPEND; + } + FREELIST(provides); + }*/ + + if(sync == NULL) { + pmdepmissing_t *m = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t)); + if(m == NULL) { + /* ORE + Free memory before leaving */ + pm_errno = PM_ERR_MEMORY; + goto error; + } + *m = *(pmdepmissing_t *)i->data; + *data = pm_list_add(*data, m); + continue; + } + + if(*data) { + /* there is at least an unresolvable dep... so we only + * continue to get the whole list of unresolvable deps */ + continue; + } + + found = 0; + for(j = list; j && !found; j = j->next) { + pmsync_t *tmp = j->data; + + if(tmp && !strcmp(tmp->spkg->name, sync->spkg->name)) { + found = 1; + } + } + + if(found) { + /* this dep is already in the target list */ + FREE(sync); + continue; + } + + _alpm_log(PM_LOG_FLOW2, "resolving %s", sync->spkg->name); + found = 0; + for(j = trail; j; j = j->next) { + pmsync_t *tmp = j->data; + + if(tmp && !strcmp(tmp->spkg->name, sync->spkg->name)) { + found = 1; + } + } + + if(!found) { + trail = pm_list_add(trail, sync); + if(resolvedeps(local, databases, sync, list, trail, data)) { + goto error; + } + _alpm_log(PM_LOG_FLOW2, "adding %s-%s", sync->spkg->name, sync->spkg->version); + list = pm_list_add(list, sync); + } else { + /* cycle detected -- skip it */ + _alpm_log(PM_LOG_FLOW2, "dependency cycle detected: %s", sync->spkg->name); + FREE(sync); + } + } + } + + FREELIST(deps); + + if(*data) { + pm_errno = PM_ERR_UNRESOLVABLE_DEPS; + return(-1); + } + + return(0); + +error: + FREELIST(deps); + return(-1); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/deps.h b/lib/libalpm/deps.h new file mode 100644 index 00000000..60b3a6d8 --- /dev/null +++ b/lib/libalpm/deps.h @@ -0,0 +1,35 @@ +/* + * deps.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_DEPS_H +#define _ALPM_DEPS_H + +#include "db.h" +#include "sync.h" + +PMList *sortbydeps(PMList *targets); +PMList *checkdeps(pmdb_t *db, unsigned short op, PMList *packages); +void splitdep(char *depstr, pmdepend_t *depend); +PMList *removedeps(pmdb_t *db, PMList *targs); +int resolvedeps(pmdb_t *local, PMList *databases, pmsync_t *sync, PMList *list, PMList *trail, PMList **data); + +#endif /* _ALPM_DEPS_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c new file mode 100644 index 00000000..266368ef --- /dev/null +++ b/lib/libalpm/error.c @@ -0,0 +1,90 @@ +/* + * error.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "alpm.h" + +char *alpm_strerror(int err) +{ + switch(err) { + /* System */ + case PM_ERR_NOT_A_FILE: + return "could not find or read file"; + /* Interface */ + case PM_ERR_HANDLE_NULL: + return "library not initialized"; + case PM_ERR_HANDLE_NOT_NULL: + return "library already initialized"; + case PM_ERR_WRONG_ARGS: + return "wrong or NULL argument"; + /* Databases */ + case PM_ERR_DB_OPEN: + return "could not open database"; + case PM_ERR_DB_CREATE: + return "could not create database"; + case PM_ERR_DB_NULL: + return "database not initialized"; + case PM_ERR_DB_NOT_NULL: + return "database already registered"; + case PM_ERR_DB_NOT_FOUND: + return "could not find database"; + /* Configuration */ + case PM_ERR_OPT_LOGFILE: + case PM_ERR_OPT_DBPATH: + case PM_ERR_OPT_SYNCDB: + case PM_ERR_OPT_USESYSLOG: + return "could not set parameter"; + /* Transactions */ + case PM_ERR_TRANS_NULL: + return "transaction not initialized"; + case PM_ERR_TRANS_NOT_NULL: + return "transaction already initialized"; + case PM_ERR_TRANS_DUP_TARGET: + return "duplicated target"; + case PM_ERR_TRANS_INITIALIZED: + return "transaction already initialized"; + case PM_ERR_TRANS_NOT_INITIALIZED: + return "transaction not initialized"; + /* Packages */ + case PM_ERR_PKG_NOT_FOUND: + return "could not find or read package"; + case PM_ERR_PKG_INVALID: + return "invalid or corrupted package"; + case PM_ERR_PKG_INSTALLED: + return "package already installed"; + case PM_ERR_PKG_CANT_FRESH: + return "package not installed or lesser version"; + case PM_ERR_INVALID_NAME: + return "package name is not valid"; + /* Dependencies */ + case PM_ERR_UNSATISFIED_DEPS: + return "could not satisfy dependencies"; + case PM_ERR_CONFLICTING_DEPS: + return "conflicting dependencies"; + case PM_ERR_UNRESOLVABLE_DEPS: + return "could not resolve dependencies"; + case PM_ERR_FILE_CONFLICTS: + return "conflicting files"; + default: + return "unexpected error"; + } +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/error.h b/lib/libalpm/error.h new file mode 100644 index 00000000..f96f2cb4 --- /dev/null +++ b/lib/libalpm/error.h @@ -0,0 +1,30 @@ +/* + * error.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_ERROR_H +#define _ALPM_ERROR_H + +#include "alpm.h" + +#define PM_RET_ERR(err, ret) do { pm_errno = (err); return(ret); } while(0) + +#endif /* _ALPM_ERROR_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/group.c b/lib/libalpm/group.c new file mode 100644 index 00000000..295be2f3 --- /dev/null +++ b/lib/libalpm/group.c @@ -0,0 +1,67 @@ +/* + * group.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +/* pacman */ +#include "util.h" +#include "group.h" + +pmgrp_t *grp_new() +{ + pmgrp_t* grp = NULL; + + grp = (pmgrp_t *)malloc(sizeof(pmgrp_t)); + if(grp == NULL) { + return(NULL); + } + + grp->name[0] = '\0'; + grp->packages = NULL; + + return(grp); +} + +void grp_free(pmgrp_t *grp) +{ + if(grp == NULL) { + return; + } + + FREELIST(grp->packages); + FREE(grp); + + return; +} + +/* Helper function for sorting groups + */ +int grp_cmp(const void *g1, const void *g2) +{ + pmgrp_t *grp1 = (pmgrp_t *)g1; + pmgrp_t *grp2 = (pmgrp_t *)g2; + + return(strcmp(grp1->name, grp2->name)); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/group.h b/lib/libalpm/group.h new file mode 100644 index 00000000..a2328e0f --- /dev/null +++ b/lib/libalpm/group.h @@ -0,0 +1,48 @@ +/* + * group.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_GROUP_H +#define _ALPM_GROUP_H + +#include "list.h" + +/* Groups structure */ +typedef struct __pmgrp_t { + char name[256]; + PMList *packages; /* List of strings */ +} pmgrp_t; + +#define FREEGRP(p) do { if(p) { grp_free(p); p = NULL; } } while(0) + +#define FREELISTGRPS(p) do { \ + PMList *i; \ + for(i = p; i; i = i->next) { \ + FREEGRP(i->data); \ + } \ + FREELIST(p); \ +} while(0) + +pmgrp_t *grp_new(); +void grp_free(pmgrp_t *grp); +int grp_cmp(const void *g1, const void *g2); + +#endif /* _ALPM_GROUP_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c new file mode 100644 index 00000000..281eb96c --- /dev/null +++ b/lib/libalpm/handle.c @@ -0,0 +1,229 @@ +/* + * handle.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <sys/types.h> +#include <stdarg.h> +#include <syslog.h> +/* pacman */ +#include "util.h" +#include "log.h" +#include "list.h" +#include "error.h" +#include "trans.h" +#include "alpm.h" +#include "handle.h" + +/* log */ +extern alpm_cb_log __pm_logcb; +extern unsigned char __pm_logmask; + +pmhandle_t *handle_new() +{ + pmhandle_t *handle; + + handle = (pmhandle_t *)malloc(sizeof(pmhandle_t)); + if(handle == NULL) { + PM_RET_ERR(PM_ERR_MEMORY, NULL); + } + + /* see if we're root or not */ + handle->uid = geteuid(); + if(!handle->uid && getenv("FAKEROOTKEY")) { + /* fakeroot doesn't count, we're non-root */ + handle->uid = 99; + } + + /* see if we're root or not (fakeroot does not count) */ + if(getuid() == 0 && !getenv("FAKEROOTKEY")) { + handle->access = PM_ACCESS_RW; + } else { + handle->access = PM_ACCESS_RO; + } + + handle->trans = NULL; + + handle->db_local = NULL; + handle->dbs_sync = NULL; + + handle->logfd = NULL; + + handle->root = NULL; + handle->dbpath = NULL; + handle->logfile = NULL; + handle->noupgrade = NULL; + handle->ignorepkg = NULL; + handle->usesyslog = 0; + + return(handle); +} + +int handle_free(pmhandle_t *handle) +{ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + /* close logfiles */ + if(handle->logfd) { + fclose(handle->logfd); + handle->logfd = NULL; + } + if(handle->usesyslog) { + handle->usesyslog = 0; + closelog(); + } + + /* free memory */ + FREETRANS(handle->trans); + FREE(handle->root); + FREE(handle->dbpath); + FREE(handle->logfile); + FREELIST(handle->dbs_sync); + FREELIST(handle->noupgrade); + FREELIST(handle->ignorepkg); + free(handle); + + return(0); +} + +int handle_set_option(pmhandle_t *handle, unsigned char val, unsigned long data) +{ + PMList *lp; + char str[PATH_MAX]; + + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + switch(val) { + case PM_OPT_DBPATH: + if(handle->db_local) { + PM_RET_ERR(PM_ERR_DB_NOT_NULL, -1); + } + for(lp = handle->dbs_sync; lp; lp = lp->next) { + if(lp->data) { + PM_RET_ERR(PM_ERR_DB_NOT_NULL, -1); + } + } + + if(handle->trans && handle->trans->state != STATE_IDLE) { + PM_RET_ERR(PM_ERR_TRANS_INITIALIZED, -1); + } + + strncpy(str, ((char *)data) ? (char *)data : PACDBPATH, PATH_MAX); + handle->dbpath = strdup(str); + _alpm_log(PM_LOG_FLOW2, "PM_OPT_DBPATH set to '%s'", handle->dbpath); + break; + case PM_OPT_LOGFILE: + if((char *)data == NULL || getuid() != 0) { + return(0); + } + if(handle->logfile) { + FREE(handle->logfile); + } + if(handle->logfd) { + if(fclose(handle->logfd) != 0) { + handle->logfd = NULL; + PM_RET_ERR(PM_ERR_OPT_LOGFILE, -1); + } + handle->logfd = NULL; + } + if((handle->logfd = fopen((char *)data, "a")) == NULL) { + _alpm_log(PM_LOG_ERROR, "can't open log file %s", (char *)data); + PM_RET_ERR(PM_ERR_OPT_LOGFILE, -1); + } + handle->logfile = strdup((char *)data); + _alpm_log(PM_LOG_FLOW2, "PM_OPT_LOGFILE set to '%s'", (char *)data); + break; + case PM_OPT_NOUPGRADE: + if((char *)data && strlen((char *)data) != 0) { + handle->noupgrade = pm_list_add(handle->noupgrade, strdup((char *)data)); + _alpm_log(PM_LOG_FLOW2, "'%s' added to PM_OPT_NOUPGRADE", (char *)data); + } else { + FREELIST(handle->noupgrade); + _alpm_log(PM_LOG_FLOW2, "PM_OPT_NOUPGRADE flushed"); + } + break; + case PM_OPT_IGNOREPKG: + if((char *)data && strlen((char *)data) != 0) { + handle->ignorepkg = pm_list_add(handle->ignorepkg, strdup((char *)data)); + _alpm_log(PM_LOG_FLOW2, "'%s' added to PM_OPT_IGNOREPKG", (char *)data); + } else { + FREELIST(handle->ignorepkg); + _alpm_log(PM_LOG_FLOW2, "PM_OPT_IGNOREPKG flushed"); + } + break; + case PM_OPT_USESYSLOG: + if(data != 0 && data != 1) { + PM_RET_ERR(PM_ERR_OPT_USESYSLOG, -1); + } + if(handle->usesyslog == data) { + return(0); + } + if(handle->usesyslog) { + closelog(); + } else { + openlog("alpm", 0, LOG_USER); + } + handle->usesyslog = (unsigned short)data; + _alpm_log(PM_LOG_FLOW2, "PM_OPT_USESYSLOG set to '%d'", handle->usesyslog); + break; + case PM_OPT_LOGCB: + __pm_logcb = (alpm_cb_log)data; + break; + case PM_OPT_LOGMASK: + __pm_logmask = (unsigned char)data; + _alpm_log(PM_LOG_FLOW2, "PM_OPT_LOGMASK set to '%02x'", (unsigned char)data); + break; + default: + PM_RET_ERR(PM_ERR_WRONG_ARGS, -1); + } + + return(0); +} + +int handle_get_option(pmhandle_t *handle, unsigned char val, long *data) +{ + /* Sanity checks */ + ASSERT(handle != NULL, PM_RET_ERR(PM_ERR_HANDLE_NULL, -1)); + + switch(val) { + case PM_OPT_ROOT: *data = (long)handle->root; break; + case PM_OPT_DBPATH: *data = (long)handle->dbpath; break; + case PM_OPT_LOCALDB: *data = (long)handle->db_local; break; + case PM_OPT_SYNCDB: *data = (long)handle->dbs_sync; break; + case PM_OPT_LOGFILE: *data = (long)handle->logfile; break; + case PM_OPT_NOUPGRADE: *data = (long)handle->noupgrade; break; + case PM_OPT_IGNOREPKG: *data = (long)handle->ignorepkg; break; + case PM_OPT_USESYSLOG: *data = handle->usesyslog; break; + case PM_OPT_LOGCB: *data = (long)__pm_logcb; break; + case PM_OPT_LOGMASK: *data = __pm_logmask; break; + default: + PM_RET_ERR(PM_ERR_WRONG_ARGS, -1); + break; + } + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h new file mode 100644 index 00000000..e8f3dbbb --- /dev/null +++ b/lib/libalpm/handle.h @@ -0,0 +1,63 @@ +/* + * handle.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_HANDLE_H +#define _ALPM_HANDLE_H + +#include "list.h" +#include "db.h" +#include "trans.h" +#include "alpm.h" + +#define PACROOT "/" +#define PACDBPATH "var/lib/pacman" +#define PACLOCK "/tmp/pacman.lck" + +typedef enum __pmaccess_t { + PM_ACCESS_RO, + PM_ACCESS_RW +} pmaccess_t; + +typedef struct __pmhandle_t { + pmaccess_t access; + uid_t uid; + pmdb_t *db_local; + PMList *dbs_sync; /* List of (pmdb_t *) */ + FILE *logfd; + pmtrans_t *trans; + /* parameters */ + char *root; + char *dbpath; + char *logfile; + PMList *noupgrade; /* List of strings */ + PMList *ignorepkg; /* List of strings */ + unsigned char usesyslog; +} pmhandle_t; + +#define FREEHANDLE(p) do { if (p) { handle_free(p); p = NULL; } } while (0) + +pmhandle_t *handle_new(); +int handle_free(pmhandle_t *handle); +int handle_set_option(pmhandle_t *handle, unsigned char val, unsigned long data); +int handle_get_option(pmhandle_t *handle, unsigned char val, long *data); + +#endif /* _ALPM_HANDLE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/list.c b/lib/libalpm/list.c new file mode 100644 index 00000000..286076d9 --- /dev/null +++ b/lib/libalpm/list.c @@ -0,0 +1,210 @@ +/* + * list.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +/* pacman */ +#include "list.h" + +PMList* pm_list_new() +{ + PMList *list = NULL; + + list = (PMList *)malloc(sizeof(PMList)); + if(list == NULL) { + return(NULL); + } + list->data = NULL; + list->prev = NULL; + list->next = NULL; + return(list); +} + +void pm_list_free(PMList *list) +{ + if(list == NULL) { + return; + } + if(list->data != NULL) { + free(list->data); + list->data = NULL; + } + if(list->next != NULL) { + pm_list_free(list->next); + } + free(list); + return; +} + +PMList* pm_list_add(PMList *list, void *data) +{ + PMList *ptr, *lp; + + ptr = list; + if(ptr == NULL) { + ptr = pm_list_new(); + } + + lp = pm_list_last(ptr); + if(lp == ptr && lp->data == NULL) { + /* nada */ + } else { + lp->next = pm_list_new(); + if(lp->next == NULL) { + return(NULL); + } + lp->next->prev = lp; + lp = lp->next; + } + lp->data = data; + return(ptr); +} + +/* Add items to a list in sorted order. Use the given comparision func to + * determine order. + */ +PMList* pm_list_add_sorted(PMList *list, void *data, pm_fn_cmp fn) +{ + PMList *add; + PMList *prev = NULL; + PMList *iter = list; + + add = pm_list_new(); + add->data = data; + + /* Find insertion point. */ + while(iter) { + if(fn(add->data, iter->data) <= 0) break; + prev = iter; + iter = iter->next; + } + + /* Insert node before insertion point. */ + add->prev = prev; + add->next = iter; + if(iter != NULL) { + /* Not at end. */ + iter->prev = add; + } + if(prev != NULL) { + /* In middle. */ + prev->next = add; + } else { + /* Start or empty, new list head. */ + list = add; + } + + return(list); +} + +/* Remove an item in a list. Use the given comparaison function to find the + * item. + * If found, 'ptr' is set to point to the removed element, so that the caller + * can free it. Otherwise, ptr is NULL. + * Return the new list (without the removed element). + */ +PMList *_alpm_list_remove(PMList *list, void *data, pm_fn_cmp fn, void **ptr) +{ + PMList *i = list; + + while(i) { + if(fn(data, i->data) == 0) { + break; + } + i = i->next; + } + + if(ptr) { + *ptr = NULL; + } + + if(i) { + /* we found a matching item */ + if(i->next) { + i->next->prev = i->prev; + } + if(i->prev) { + i->prev->next = i->next; + } + if(i == list) { + /* The item found is the first in the chain, + * so we move the header to the next element. + */ + list = list->next; + } + + if(ptr) { + *ptr = i->data; + } + + free(i); + } + + return(list); +} + +int pm_list_count(PMList *list) +{ + int i; + PMList *lp; + + for(lp = list, i = 0; lp; lp = lp->next, i++); + + return(i); +} + +int pm_list_is_ptrin(PMList *haystack, void *needle) +{ + PMList *lp; + + for(lp = haystack; lp; lp = lp->next) { + if(lp->data == needle) { + return(1); + } + } + return(0); +} + +/* Test for existence of a string in a PMList + */ +PMList *pm_list_is_strin(char *needle, PMList *haystack) +{ + PMList *lp; + + for(lp = haystack; lp; lp = lp->next) { + if(lp->data && !strcmp(lp->data, needle)) { + return(lp); + } + } + return(NULL); +} + +PMList* pm_list_last(PMList *list) +{ + PMList *ptr; + + for(ptr = list; ptr && ptr->next; ptr = ptr->next); + return(ptr); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/list.h b/lib/libalpm/list.h new file mode 100644 index 00000000..c5de46d0 --- /dev/null +++ b/lib/libalpm/list.h @@ -0,0 +1,50 @@ +/* + * list.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_LIST_H +#define _ALPM_LIST_H + +/* Chained list struct */ +typedef struct __pmlist_t { + void *data; + struct __pmlist_t *prev; + struct __pmlist_t *next; +} pmlist_t; + +typedef struct __pmlist_t PMList; + +#define FREELIST(p) do { if(p) { pm_list_free(p); p = NULL; } } while(0) + +/* Sort comparison callback function declaration */ +typedef int (*pm_fn_cmp) (const void *, const void *); + +PMList *pm_list_new(); +void pm_list_free(PMList *list); +PMList *pm_list_add(PMList *list, void *data); +PMList *pm_list_add_sorted(PMList *list, void *data, pm_fn_cmp fn); +PMList *_alpm_list_remove(PMList *list, void *data, pm_fn_cmp fn, void **ptr); +int pm_list_count(PMList *list); +int pm_list_is_ptrin(PMList *haystack, void *needle); +PMList *pm_list_is_strin(char *needle, PMList *haystack); +PMList *pm_list_last(PMList *list); + +#endif /* _ALPM_LIST_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/log.c b/lib/libalpm/log.c new file mode 100644 index 00000000..dd4d34a0 --- /dev/null +++ b/lib/libalpm/log.c @@ -0,0 +1,52 @@ +/* + * log.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdio.h> +#include <stdarg.h> +#include <time.h> +/* pacman */ +#include "log.h" + +/* Internal library log mechanism */ + +alpm_cb_log __pm_logcb = NULL; +unsigned char __pm_logmask = 0; + +void _alpm_log(unsigned char flag, char *fmt, ...) +{ + char str[256]; + va_list args; + + if(__pm_logcb == NULL) { + return; + } + + if(flag & __pm_logmask) { + va_start(args, fmt); + vsnprintf(str, 256, fmt, args); + va_end(args); + + __pm_logcb(flag, str); + } +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/log.h b/lib/libalpm/log.h new file mode 100644 index 00000000..852202ab --- /dev/null +++ b/lib/libalpm/log.h @@ -0,0 +1,32 @@ +/* + * log.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_LOG_H +#define _ALPM_LOG_H + +typedef void (*alpm_cb_log)(unsigned short, char *); + +void _alpm_log(unsigned char flag, char *fmt, ...); + +int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...); + +#endif /* _ALPM_LOG_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/md5.c b/lib/libalpm/md5.c new file mode 100644 index 00000000..fcb1611e --- /dev/null +++ b/lib/libalpm/md5.c @@ -0,0 +1,338 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#include "md5.h" + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4 [4], unsigned char [64]); +static void Encode(unsigned char *, UINT4 *, unsigned int); +static void Decode(UINT4 *, unsigned char *, unsigned int); +static void MD5_memcpy(POINTER, POINTER, unsigned int); +static void MD5_memset(POINTER, int, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/md5.h b/lib/libalpm/md5.h new file mode 100644 index 00000000..e6e4ea64 --- /dev/null +++ b/lib/libalpm/md5.h @@ -0,0 +1,51 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init(MD5_CTX *); +void MD5Update(MD5_CTX *, unsigned char *, unsigned int); +void MD5Final(unsigned char [16], MD5_CTX *); + +char* MDFile(char *); +void MDPrint(unsigned char [16]); + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/md5driver.c b/lib/libalpm/md5driver.c new file mode 100644 index 00000000..30b37051 --- /dev/null +++ b/lib/libalpm/md5driver.c @@ -0,0 +1,81 @@ +/* MD5DRIVER.C - taken and modified from MDDRIVER.C (license below) */ +/* for use in pacman. */ +/*********************************************************************/ + +/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All +rights reserved. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* The following makes MD default to MD5 if it has not already been + defined with C compiler flags. + */ +#define MD MD5 + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <string.h> +#include "md5.h" + +/* Length of test block, number of test blocks. + */ +#define TEST_BLOCK_LEN 1000 +#define TEST_BLOCK_COUNT 1000 + +#define MD_CTX MD5_CTX +#define MDInit MD5Init +#define MDUpdate MD5Update +#define MDFinal MD5Final + +char* MDFile(char *filename) +{ + FILE *file; + MD_CTX context; + int len; + unsigned char buffer[1024], digest[16]; + + if((file = fopen(filename, "rb")) == NULL) { + printf ("%s can't be opened\n", filename); + } else { + char *ret; + int i; + + MDInit(&context); + while((len = fread(buffer, 1, 1024, file))) { + MDUpdate(&context, buffer, len); + } + MDFinal(digest, &context); + fclose(file); + /*printf("MD5 (%s) = ", filename); + MDPrint(digest); + printf("\n");*/ + + ret = (char*)malloc(33); + ret[0] = '\0'; + for(i = 0; i < 16; i++) { + sprintf(ret, "%s%02x", ret, digest[i]); + } + + return(ret); + } + return(NULL); +} + +/* Prints a message digest in hexadecimal. + */ +void MDPrint(unsigned char digest[16]) +{ + unsigned int i; + for (i = 0; i < 16; i++) + printf ("%02x", digest[i]); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c new file mode 100644 index 00000000..f20b88eb --- /dev/null +++ b/lib/libalpm/package.c @@ -0,0 +1,342 @@ +/* + * package.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <fcntl.h> +#include <string.h> +#include <libtar.h> +#include <zlib.h> +/* pacman */ +#include "log.h" +#include "util.h" +#include "error.h" +#include "list.h" +#include "package.h" + +pmpkg_t *pkg_new() +{ + pmpkg_t* pkg = NULL; + + MALLOC(pkg, sizeof(pmpkg_t)); + + pkg->name[0] = '\0'; + pkg->version[0] = '\0'; + pkg->desc[0] = '\0'; + pkg->url[0] = '\0'; + pkg->license[0] = '\0'; + pkg->builddate[0] = '\0'; + pkg->installdate[0] = '\0'; + pkg->packager[0] = '\0'; + pkg->md5sum[0] = '\0'; + pkg->arch[0] = '\0'; + pkg->size = 0; + pkg->scriptlet = 0; + pkg->force = 0; + pkg->reason = PM_PKG_REASON_EXPLICIT; + pkg->requiredby = NULL; + pkg->conflicts = NULL; + pkg->files = NULL; + pkg->backup = NULL; + pkg->depends = NULL; + pkg->groups = NULL; + pkg->provides = NULL; + pkg->replaces = NULL; + /* internal */ + pkg->origin = 0; + pkg->data = NULL; + pkg->infolevel = 0; + + return(pkg); +} + +void pkg_free(pmpkg_t *pkg) +{ + if(pkg == NULL) { + return; + } + + FREELIST(pkg->files); + FREELIST(pkg->backup); + FREELIST(pkg->depends); + FREELIST(pkg->conflicts); + FREELIST(pkg->requiredby); + FREELIST(pkg->groups); + FREELIST(pkg->provides); + FREELIST(pkg->replaces); + if(pkg->origin == PKG_FROM_FILE) { + FREE(pkg->data); + } + free(pkg); + + return; +} + +/* Parses the package description file for the current package + * + * Returns: 0 on success, 1 on error + * + */ +static int parse_descfile(char *descfile, pmpkg_t *info, int output) +{ + FILE* fp = NULL; + char line[PATH_MAX+1]; + char* ptr = NULL; + char* key = NULL; + int linenum = 0; + + if((fp = fopen(descfile, "r")) == NULL) { + _alpm_log(PM_LOG_ERROR, "could not open file %s", descfile); + return(-1); + } + + while(!feof(fp)) { + fgets(line, PATH_MAX, fp); + linenum++; + _alpm_strtrim(line); + if(strlen(line) == 0 || line[0] == '#') { + continue; + } + if(output) { + printf("%s\n", line); + } + ptr = line; + key = strsep(&ptr, "="); + if(key == NULL || ptr == NULL) { + fprintf(stderr, "%s: syntax error in description file line %d\n", + info->name[0] != '\0' ? info->name : "error", linenum); + } else { + _alpm_strtrim(key); + key = _alpm_strtoupper(key); + _alpm_strtrim(ptr); + if(!strcmp(key, "PKGNAME")) { + strncpy(info->name, ptr, sizeof(info->name)); + } else if(!strcmp(key, "PKGVER")) { + strncpy(info->version, ptr, sizeof(info->version)); + } else if(!strcmp(key, "PKGDESC")) { + strncpy(info->desc, ptr, sizeof(info->desc)); + } else if(!strcmp(key, "GROUP")) { + info->groups = pm_list_add(info->groups, strdup(ptr)); + } else if(!strcmp(key, "URL")) { + strncpy(info->url, ptr, sizeof(info->url)); + } else if(!strcmp(key, "LICENSE")) { + strncpy(info->license, ptr, sizeof(info->license)); + } else if(!strcmp(key, "BUILDDATE")) { + strncpy(info->builddate, ptr, sizeof(info->builddate)); + } else if(!strcmp(key, "INSTALLDATE")) { + strncpy(info->installdate, ptr, sizeof(info->installdate)); + } else if(!strcmp(key, "PACKAGER")) { + strncpy(info->packager, ptr, sizeof(info->packager)); + } else if(!strcmp(key, "ARCH")) { + strncpy(info->arch, ptr, sizeof(info->arch)); + } else if(!strcmp(key, "SIZE")) { + char tmp[32]; + strncpy(tmp, ptr, sizeof(tmp)); + info->size = atol(tmp); + } else if(!strcmp(key, "DEPEND")) { + info->depends = pm_list_add(info->depends, strdup(ptr)); + } else if(!strcmp(key, "CONFLICT")) { + info->conflicts = pm_list_add(info->conflicts, strdup(ptr)); + } else if(!strcmp(key, "REPLACES")) { + info->replaces = pm_list_add(info->replaces, strdup(ptr)); + } else if(!strcmp(key, "PROVIDES")) { + info->provides = pm_list_add(info->provides, strdup(ptr)); + } else if(!strcmp(key, "BACKUP")) { + info->backup = pm_list_add(info->backup, strdup(ptr)); + } else { + fprintf(stderr, "%s: syntax error in description file line %d\n", + info->name[0] != '\0' ? info->name : "error", linenum); + } + } + line[0] = '\0'; + } + fclose(fp); + unlink(descfile); + + return(0); +} + +pmpkg_t *pkg_load(char *pkgfile) +{ + char *expath; + int i; + int config = 0; + int filelist = 0; + int scriptcheck = 0; + TAR *tar; + pmpkg_t *info = NULL; + tartype_t gztype = { + (openfunc_t)_alpm_gzopen_frontend, + (closefunc_t)gzclose, + (readfunc_t)gzread, + (writefunc_t)gzwrite + }; + + if(pkgfile == NULL) { + PM_RET_ERR(PM_ERR_WRONG_ARGS, NULL); + } + + if(tar_open(&tar, pkgfile, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + PM_RET_ERR(PM_ERR_NOT_A_FILE, NULL); + } + + info = pkg_new(); + if(info == NULL) { + tar_close(tar); + PM_RET_ERR(PM_ERR_MEMORY, NULL); + } + + for(i = 0; !th_read(tar); i++) { + if(config && filelist && scriptcheck) { + /* we have everything we need */ + break; + } + if(!strcmp(th_get_pathname(tar), ".PKGINFO")) { + char *descfile; + + /* extract this file into /tmp. it has info for us */ + descfile = strdup("/tmp/pacman_XXXXXX"); + mkstemp(descfile); + tar_extract_file(tar, descfile); + /* parse the info file */ + if(parse_descfile(descfile, info, 0) == -1) { + goto error; + } + if(!strlen(info->name)) { + _alpm_log(PM_LOG_ERROR, "missing package name in %s", pkgfile); + goto error; + } + if(!strlen(info->version)) { + _alpm_log(PM_LOG_ERROR, "missing package version in %s", pkgfile); + goto error; + } + config = 1; + FREE(descfile); + continue; + } else if(!strcmp(th_get_pathname(tar), "._install") || !strcmp(th_get_pathname(tar), ".INSTALL")) { + info->scriptlet = 1; + scriptcheck = 1; + } else if(!strcmp(th_get_pathname(tar), ".FILELIST")) { + /* Build info->files from the filelist */ + FILE *fp; + char *fn; + char *str; + + MALLOC(str, PATH_MAX); + fn = strdup("/tmp/pacman_XXXXXX"); + mkstemp(fn); + tar_extract_file(tar, fn); + fp = fopen(fn, "r"); + while(!feof(fp)) { + if(fgets(str, PATH_MAX, fp) == NULL) { + continue; + } + _alpm_strtrim(str); + info->files = pm_list_add(info->files, strdup(str)); + } + FREE(str); + fclose(fp); + if(unlink(fn)) { + _alpm_log(PM_LOG_WARNING, "could not remove tempfile %s\n", fn); + } + FREE(fn); + filelist = 1; + continue; + } else { + scriptcheck = 1; + if(!filelist) { + /* no .FILELIST present in this package.. build the filelist the */ + /* old-fashioned way, one at a time */ + expath = strdup(th_get_pathname(tar)); + info->files = pm_list_add(info->files, expath); + } + } + + if(TH_ISREG(tar) && tar_skip_regfile(tar)) { + _alpm_log(PM_LOG_ERROR, "bad package file in %s", pkgfile); + goto error; + } + expath = NULL; + } + tar_close(tar); + + if(!config) { + _alpm_log(PM_LOG_ERROR, "missing package info file in %s", pkgfile); + goto error; + } + + /* internal */ + info->origin = PKG_FROM_FILE; + info->data = strdup(pkgfile); + info->infolevel = 0xFF; + + return(info); + +error: + printf("toto\n"); + + FREEPKG(info); + tar_close(tar); + + return(NULL); +} + +/* Helper function for sorting packages + */ +int pkg_cmp(const void *p1, const void *p2) +{ + pmpkg_t *pkg1 = (pmpkg_t *)p1; + pmpkg_t *pkg2 = (pmpkg_t *)p2; + + return(strcmp(pkg1->name, pkg2->name)); +} + +/* Test for existence of a package in a PMList* + * of pmpkg_t* + * + * returns: 0 for no match + * 1 for identical match + * -1 for name-only match (version mismatch) + */ +int pkg_isin(pmpkg_t *needle, PMList *haystack) +{ + PMList *lp; + + if(needle == NULL || haystack == NULL) { + return(0); + } + + for(lp = haystack; lp; lp = lp->next) { + pmpkg_t *info = lp->data; + + if(info && !strcmp(info->name, needle->name)) { + if(!strcmp(info->version, needle->version)) { + return(1); + } + return(-1); + } + } + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h new file mode 100644 index 00000000..890bbccc --- /dev/null +++ b/lib/libalpm/package.h @@ -0,0 +1,78 @@ +/* + * package.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_PACKAGE_H +#define _ALPM_PACKAGE_H + +#include "list.h" + +#define PKG_FROM_CACHE 1 +#define PKG_FROM_FILE 2 + +typedef struct __pmpkg_t { + char name[256]; + char version[64]; + char desc[512]; + char url[256]; + char license[128]; + char builddate[32]; + char installdate[32]; + char packager[64]; + char md5sum[33]; + char arch[32]; + unsigned long size; + unsigned char scriptlet; + unsigned char force; + unsigned char reason; + PMList *replaces; + PMList *groups; + PMList *files; + PMList *backup; + PMList *depends; + PMList *requiredby; + PMList *conflicts; + PMList *provides; + /* internal */ + unsigned char origin; + void *data; + unsigned char infolevel; +} pmpkg_t; + +#define FREEPKG(p) do { if(p) { pkg_free(p); p = NULL; } } while(0) + +#define FREELISTPKGS(p) do {\ + if(p) { \ + PMList *i;\ + for(i = p; i; i = i->next) {\ + FREEPKG(i->data); \ + }\ + FREELIST(p);\ + } \ +} while(0) + +pmpkg_t* pkg_new(); +void pkg_free(pmpkg_t *pkg); +pmpkg_t *pkg_load(char *pkgfile); +int pkg_cmp(const void *p1, const void *p2); +int pkg_isin(pmpkg_t *needle, PMList *haystack); + +#endif /* _ALPM_PACKAGE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/provide.c b/lib/libalpm/provide.c new file mode 100644 index 00000000..a472c7af --- /dev/null +++ b/lib/libalpm/provide.c @@ -0,0 +1,53 @@ +/* + * provide.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <string.h> +/* pacman */ +#include "cache.h" +#include "list.h" +#include "db.h" +#include "alpm.h" + +/* return a PMList of packages in "db" that provide "package" + */ +PMList *_alpm_db_whatprovides(pmdb_t *db, char *package) +{ + PMList *pkgs = NULL; + PMList *lp; + + if(db == NULL || package == NULL || strlen(package) == 0) { + return(NULL); + } + + for(lp = db_get_pkgcache(db); lp; lp = lp->next) { + pmpkg_t *info = lp->data; + + if(pm_list_is_strin(package, info->provides)) { + pkgs = pm_list_add(pkgs, info); + } + } + + return(pkgs); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/provide.h b/lib/libalpm/provide.h new file mode 100644 index 00000000..c8c84367 --- /dev/null +++ b/lib/libalpm/provide.h @@ -0,0 +1,33 @@ +/* + * provide.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_PROVIDE_H +#define _ALPM_PROVIDE_H + +#include "config.h" + +#include "list.h" +#include "db.h" + +PMList *_alpm_db_whatprovides(pmdb_t *db, char *package); + +#endif /* _ALPM_PROVIDE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c new file mode 100644 index 00000000..c53ffa4d --- /dev/null +++ b/lib/libalpm/remove.c @@ -0,0 +1,259 @@ +/* + * remove.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <string.h> +#include <zlib.h> +#include <libtar.h> +/* pacman */ +#include "util.h" +#include "error.h" +#include "rpmvercmp.h" +#include "md5.h" +#include "log.h" +#include "backup.h" +#include "package.h" +#include "db.h" +#include "cache.h" +#include "deps.h" +#include "provide.h" +#include "remove.h" +#include "handle.h" +#include "alpm.h" + +extern pmhandle_t *handle; + +int remove_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name) +{ + pmpkg_t *info; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(name != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if((info = db_scan(db, name, INFRQ_ALL)) == NULL) { + _alpm_log(PM_LOG_ERROR, "could not find %s in database", name); + PM_RET_ERR(PM_ERR_PKG_NOT_FOUND, -1); + } + trans->packages = pm_list_add(trans->packages, info); + + return(0); +} + +int remove_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data) +{ + pmpkg_t *info; + PMList *lp; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(data != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if(!(trans->flags & (PM_TRANS_FLAG_NODEPS)) && (trans->type != PM_TRANS_TYPE_UPGRADE)) { + TRANS_CB(trans, PM_TRANS_CB_DEPS_START, NULL, NULL); + + if((lp = checkdeps(db, trans->type, trans->packages)) != NULL) { + if(trans->flags & PM_TRANS_FLAG_CASCADE) { + while(lp) { + PMList *j; + for(j = lp; j; j = j->next) { + pmdepmissing_t* miss = (pmdepmissing_t*)j->data; + info = db_scan(db, miss->depend.name, INFRQ_ALL); + if(!pkg_isin(info, trans->packages)) { + trans->packages = pm_list_add(trans->packages, info); + } + } + FREELIST(lp); + lp = checkdeps(db, trans->type, trans->packages); + } + } else { + *data = lp; + PM_RET_ERR(PM_ERR_UNSATISFIED_DEPS, -1); + } + } + + if(trans->flags & PM_TRANS_FLAG_RECURSE) { + _alpm_log(PM_LOG_FLOW1, "finding removable dependencies..."); + trans->packages = removedeps(db, trans->packages); + } + + TRANS_CB(trans, PM_TRANS_CB_DEPS_DONE, NULL, NULL); + } + + return(0); +} + +int remove_commit(pmdb_t *db, pmtrans_t *trans) +{ + pmpkg_t *info; + struct stat buf; + PMList *targ, *lp; + char line[PATH_MAX+1]; + + ASSERT(db != NULL, PM_RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + + for(targ = trans->packages; targ; targ = targ->next) { + char pm_install[PATH_MAX]; + info = (pmpkg_t*)targ->data; + + if(trans->type != PM_TRANS_TYPE_UPGRADE) { + TRANS_CB(trans, PM_TRANS_CB_REMOVE_START, info, NULL); + + /* run the pre-remove scriptlet if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version); + _alpm_runscriptlet(handle->root, pm_install, "pre_remove", info->version, NULL); + } + + if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) { + /* iterate through the list backwards, unlinking files */ + for(lp = pm_list_last(info->files); lp; lp = lp->prev) { + char *newpath = NULL; + int nb = 0; + if(_alpm_needbackup(lp->data, info->backup)) { + nb = 1; + } + if(!nb && trans->type == PM_TRANS_TYPE_UPGRADE) { + /* check noupgrade */ + if(pm_list_is_strin(lp->data, handle->noupgrade)) { + nb = 1; + } + } + snprintf(line, PATH_MAX, "%s%s", handle->root, (char*)lp->data); + if(lstat(line, &buf)) { + _alpm_log(PM_LOG_ERROR, "file %s does not exist", line); + continue; + } + if(S_ISDIR(buf.st_mode)) { + _alpm_log(PM_LOG_DEBUG, "removing directory %s", line); + if(rmdir(line)) { + /* this is okay, other packages are probably using it. */ + } + } else { + /* if the file is flagged, back it up to .pacsave */ + if(nb) { + if(trans->type == PM_TRANS_TYPE_UPGRADE) { + /* we're upgrading so just leave the file as is. pacman_add() will handle it */ + } else { + if(!(trans->flags & PM_TRANS_FLAG_NOSAVE)) { + newpath = (char*)realloc(newpath, strlen(line)+strlen(".pacsave")+1); + sprintf(newpath, "%s.pacsave", line); + rename(line, newpath); + _alpm_log(PM_LOG_WARNING, "%s saved as %s", line, newpath); + alpm_logaction("%s saved as %s", line, newpath); + } else { + _alpm_log(PM_LOG_DEBUG, "unlinking %s", line); + if(unlink(line)) { + _alpm_log(PM_LOG_ERROR, "cannot remove file %s", line); + } + } + } + } else { + _alpm_log(PM_LOG_DEBUG, "unlinking %s", line); + if(unlink(line)) { + _alpm_log(PM_LOG_ERROR, "cannot remove file %s", line); + } + } + } + } + } + + if(trans->type != PM_TRANS_TYPE_UPGRADE) { + char pm_install[PATH_MAX]; + + /* run the post-remove script if it exists */ + snprintf(pm_install, PATH_MAX, "%s%s/%s/%s-%s/install", handle->root, handle->dbpath, db->treename, info->name, info->version); + _alpm_runscriptlet(handle->root, pm_install, "post_remove", info->version, NULL); + } + + /* remove the package from the database */ + if(db_remove(db, info) == -1) { + _alpm_log(PM_LOG_ERROR, "failed to remove database entry %s/%s-%s", db->path, info->name, info->version); + } + + /* update dependency packages' REQUIREDBY fields */ + for(lp = info->depends; lp; lp = lp->next) { + PMList *last, *j; + pmpkg_t *depinfo = NULL; + pmdepend_t depend; + + splitdep((char*)lp->data, &depend); + + depinfo = db_scan(db, depend.name, INFRQ_DESC|INFRQ_DEPENDS); + if(depinfo == NULL) { + /* look for a provides package */ + PMList *provides = _alpm_db_whatprovides(db, depend.name); + if(provides) { + /* TODO: should check _all_ packages listed in provides, not just + * the first one. + */ + /* use the first one */ + depinfo = db_scan(db, provides->data, INFRQ_DEPENDS); + FREELIST(provides); + if(depinfo == NULL) { + /* wtf */ + continue; + } + } else { + continue; + } + } + /* splice out this entry from requiredby */ + last = pm_list_last(depinfo->requiredby); + /* ORE - use list_remove here? */ + for(j = depinfo->requiredby; j; j = j->next) { + if(!strcmp((char*)j->data, info->name)) { + if(j == depinfo->requiredby) { + depinfo->requiredby = j->next; + } + if(j->prev) j->prev->next = j->next; + if(j->next) j->next->prev = j->prev; + /* free the spliced node */ + j->prev = j->next = NULL; + FREELIST(j); + break; + } + } + db_write(db, depinfo, INFRQ_DEPENDS); + FREEPKG(depinfo); + } + + if(trans->type != PM_TRANS_TYPE_UPGRADE) { + TRANS_CB(trans, PM_TRANS_CB_REMOVE_DONE, info, NULL); + alpm_logaction("removed %s (%s)", info->name, info->version); + } + } + + /* run ldconfig if it exists */ + _alpm_log(PM_LOG_FLOW2, "running \"%ssbin/ldconfig -r %s\"", handle->root, handle->root); + _alpm_ldconfig(handle->root); + + /* cache needs to be rebuilt */ + db_free_pkgcache(db); + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/remove.h b/lib/libalpm/remove.h new file mode 100644 index 00000000..2d5cec1b --- /dev/null +++ b/lib/libalpm/remove.h @@ -0,0 +1,34 @@ +/* + * remove.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_REMOVE_H +#define _ALPM_REMOVE_H + +#include "list.h" +#include "db.h" +#include "trans.h" + +int remove_loadtarget(pmdb_t *db, pmtrans_t *trans, char *name); +int remove_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data); +int remove_commit(pmdb_t *db, pmtrans_t *trans); + +#endif /* _ALPM_REMOVE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/rpmvercmp.c b/lib/libalpm/rpmvercmp.c new file mode 100644 index 00000000..bbabc2b4 --- /dev/null +++ b/lib/libalpm/rpmvercmp.c @@ -0,0 +1,237 @@ +/* + * rpmvercmp.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdio.h> +#include <ctype.h> +#include <string.h> +/* pacman */ +#include "rpmvercmp.h" + +/* this function was taken from rpm 4.0.4 and rewritten */ +int rpmvercmp(const char *a, const char *b) +{ + char *str1, *str2; + char *one, *two; + char *rel1 = NULL, *rel2 = NULL; + char oldch1, oldch2; + int is1num, is2num; + int rc; + + if(!strcmp(a,b)) { + return(0); + } + + str1 = strdup(a); + str2 = strdup(b); + + /* lose the release number */ + for(one = str1; *one && *one != '-'; one++); + if(one) { + *one = '\0'; + rel1 = ++one; + } + for(two = str2; *two && *two != '-'; two++); + if(two) { + *two = '\0'; + rel2 = ++two; + } + + one = str1; + two = str2; + + while(*one || *two) { + while(*one && !isalnum(*one)) one++; + while(*two && !isalnum(*two)) two++; + + str1 = one; + str2 = two; + + /* find the next segment for each string */ + if(isdigit(*str1)) { + is1num = 1; + while(*str1 && isdigit(*str1)) str1++; + } else { + is1num = 0; + while(*str1 && isalpha(*str1)) str1++; + } + if(isdigit(*str2)) { + is2num = 1; + while(*str2 && isdigit(*str2)) str2++; + } else { + is2num = 0; + while(*str2 && isalpha(*str2)) str2++; + } + + oldch1 = *str1; + *str1 = '\0'; + oldch2 = *str2; + *str2 = '\0'; + + /* see if we ran out of segments on one string */ + if(one == str1 && two != str2) { + return(is2num ? -1 : 1); + } + if(one != str1 && two == str2) { + return(is1num ? 1 : -1); + } + + /* see if we have a type mismatch (ie, one is alpha and one is digits) */ + if(is1num && !is2num) return(1); + if(!is1num && is2num) return(-1); + + if(is1num) while(*one == '0') one++; + if(is2num) while(*two == '0') two++; + + rc = strverscmp(one, two); + if(rc) return(rc); + + *str1 = oldch1; + *str2 = oldch2; + one = str1; + two = str2; + } + + if((!*one) && (!*two)) { + /* compare release numbers */ + if(rel1 && rel2) return(rpmvercmp(rel1, rel2)); + return(0); + } + + return(*one ? 1 : -1); +} + +#ifndef HAVE_STRVERSCMP + +/* GNU's strverscmp() function, taken from glibc 2.3.2 sources + */ + +/* Compare strings while treating digits characters numerically. + Copyright (C) 1997, 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jean-François Bignolles <bignolle@ecoledoc.ibp.fr>, 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* states: S_N: normal, S_I: comparing integral part, S_F: comparing + fractionnal parts, S_Z: idem but with leading Zeroes only */ +#define S_N 0x0 +#define S_I 0x4 +#define S_F 0x8 +#define S_Z 0xC + +/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ +#define CMP 2 +#define LEN 3 + + +/* Compare S1 and S2 as strings holding indices/version numbers, + returning less than, equal to or greater than zero if S1 is less than, + equal to or greater than S2 (for more info, see the texinfo doc). +*/ + +int strverscmp (s1, s2) + const char *s1; + const char *s2; +{ + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + int state; + int diff; + + /* Symbol(s) 0 [1-9] others (padding) + Transition (10) 0 (01) d (00) x (11) - */ + static const unsigned int next_state[] = + { + /* state x d 0 - */ + /* S_N */ S_N, S_I, S_Z, S_N, + /* S_I */ S_N, S_I, S_I, S_I, + /* S_F */ S_N, S_F, S_F, S_F, + /* S_Z */ S_N, S_F, S_Z, S_Z + }; + + static const int result_type[] = + { + /* state x/x x/d x/0 x/- d/x d/d d/0 d/- + 0/x 0/d 0/0 0/- -/x -/d -/0 -/- */ + + /* S_N */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_I */ CMP, -1, -1, CMP, +1, LEN, LEN, CMP, + +1, LEN, LEN, CMP, CMP, CMP, CMP, CMP, + /* S_F */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_Z */ CMP, +1, +1, CMP, -1, CMP, CMP, CMP, + -1, CMP, CMP, CMP + }; + + if (p1 == p2) + return 0; + + c1 = *p1++; + c2 = *p2++; + /* Hint: '0' is a digit too. */ + state = S_N | ((c1 == '0') + (isdigit (c1) != 0)); + + while ((diff = c1 - c2) == 0 && c1 != '\0') + { + state = next_state[state]; + c1 = *p1++; + c2 = *p2++; + state |= (c1 == '0') + (isdigit (c1) != 0); + } + + state = result_type[state << 2 | (((c2 == '0') + (isdigit (c2) != 0)))]; + + switch (state) + { + case CMP: + return diff; + + case LEN: + while (isdigit (*p1++)) + if (!isdigit (*p2++)) + return 1; + + return isdigit (*p2) ? -1 : diff; + + default: + return state; + } +} + +#endif + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/rpmvercmp.h b/lib/libalpm/rpmvercmp.h new file mode 100644 index 00000000..c42c0fc7 --- /dev/null +++ b/lib/libalpm/rpmvercmp.h @@ -0,0 +1,32 @@ +/* + * rpmvercmp.h + * + * Copyright (c) 2002 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_RPMVERCMP_H +#define _PM_RPMVERCMP_H + +int rpmvercmp(const char *a, const char *b); + +#ifndef HAVE_STRVERSCMP +int strverscmp(const char *s1, const char *s2); +#endif + +#endif + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c new file mode 100644 index 00000000..8a049c74 --- /dev/null +++ b/lib/libalpm/sync.c @@ -0,0 +1,248 @@ +/* + * sync.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +/* pacman */ +#include "log.h" +#include "util.h" +#include "list.h" +#include "package.h" +#include "db.h" +#include "cache.h" +#include "deps.h" +#include "trans.h" +#include "sync.h" +#include "rpmvercmp.h" +#include "handle.h" + +extern pmhandle_t *handle; + +pmsync_t *sync_new(int type, pmpkg_t *lpkg, pmpkg_t *spkg) +{ + pmsync_t *sync; + + if((sync = (pmsync_t *)malloc(sizeof(pmsync_t))) == NULL) { + return(NULL); + } + + sync->type = type; + sync->lpkg = lpkg; + sync->spkg = spkg; + + return(sync); +} + +int sync_sysupgrade(PMList **data) +{ + PMList *i, *j, *k; + PMList *targets = NULL; + + *data = NULL; + + /* check for "recommended" package replacements */ + for(i = handle->dbs_sync; i; i = i->next) { + PMList *j; + + for(j = db_get_pkgcache(i->data); j; j = j->next) { + pmpkg_t *spkg = j->data; + + for(k = spkg->replaces; k; k = k->next) { + PMList *m; + + for(m = db_get_pkgcache(handle->db_local); m; m = m->next) { + pmpkg_t *lpkg = m->data; + + if(!strcmp(k->data, lpkg->name)) { + if(pm_list_is_strin(lpkg->name, handle->ignorepkg)) { + _alpm_log(PM_LOG_WARNING, "%s-%s: ignoring package upgrade (to be replaced by %s-%s)", + lpkg->name, lpkg->version, spkg->name, spkg->version); + } else { + pmsync_t *sync = sync_new(PM_SYSUPG_REPLACE, lpkg, spkg); + + if(sync == NULL) { + pm_errno = PM_ERR_MEMORY; + goto error; + } + + targets = pm_list_add(targets, sync); + } + } + } + } + } + } + + /* match installed packages with the sync dbs and compare versions */ + for(i = db_get_pkgcache(handle->db_local); i; i = i->next) { + int cmp; + pmpkg_t *local = i->data; + pmpkg_t *spkg = NULL; + pmsync_t *sync; + + for(j = handle->dbs_sync; !spkg && j; j = j->next) { + + for(k = db_get_pkgcache(j->data); !spkg && k; k = k->next) { + pmpkg_t *sp = k->data; + + if(!strcmp(local->name, sp->name)) { + spkg = sp; + } + } + } + if(spkg == NULL) { + /*fprintf(stderr, "%s: not found in sync db. skipping.", local->name);*/ + continue; + } + + /* compare versions and see if we need to upgrade */ + cmp = rpmvercmp(local->version, spkg->version); + if(cmp > 0 && !spkg->force) { + /* local version is newer */ + _alpm_log(PM_LOG_FLOW1, "%s-%s: local version is newer", + local->name, local->version); + continue; + } else if(cmp == 0) { + /* versions are identical */ + continue; + } else if(pm_list_is_strin(i->data, handle->ignorepkg)) { + /* package should be ignored (IgnorePkg) */ + _alpm_log(PM_LOG_FLOW1, "%s-%s: ignoring package upgrade (%s)", + local->name, local->version, spkg->version); + continue; + } + + sync = sync_new(PM_SYSUPG_UPGRADE, local, spkg); + if(sync == NULL) { + pm_errno = PM_ERR_MEMORY; + goto error; + } + + targets = pm_list_add(targets, sync); + } + + *data = targets; + + return(0); + +error: + FREELIST(targets); + return(-1); +} + +int sync_resolvedeps(PMList **syncs) +{ + return(0); +} + +int sync_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data) +{ + PMList *i; + PMList *trail = NULL; + + /* Resolve targets dependencies */ + for(i = trans->targets; i; i = i->next) { + if(resolvedeps(handle->db_local, handle->dbs_sync, i->data, trans->targets, trail, data) == -1) { + /* pm_errno is set by resolvedeps */ + goto error; + } + } + + /* ORE + check for inter-conflicts and whatnot */ + + /* ORE + any packages in rmtargs need to be removed from final. + rather than ripping out nodes from final, we just copy over + our "good" nodes to a new list and reassign. */ + + /* ORE + Check dependencies of packages in rmtargs and make sure + we won't be breaking anything by removing them. + If a broken dep is detected, make sure it's not from a + package that's in our final (upgrade) list. */ + + return(0); + +error: + return(-1); +} + +int sync_commit(pmdb_t *db, pmtrans_t *trans) +{ + PMList *i, *files = NULL; + PMList *final = NULL; + PMList *data; + pmtrans_t *tr; + + /* remove any conflicting packages (WITHOUT dep checks) */ + + /* remove to-be-replaced packages */ + + /* install targets */ + tr = trans_new(PM_TRANS_TYPE_UPGRADE, 0); + for(i = files; i; i = i->next) { + trans_addtarget(tr, i->data); + } + + trans_prepare(tr, &data); + + trans_commit(tr); + + trans_free(tr); + + /* propagate replaced packages' requiredby fields to their new owners */ + for(i = final; i; i = i->next) { + /*syncpkg_t *sync = (syncpkg_t*)i->data; + if(sync->replaces) { + pkginfo_t *new = db_scan(db, sync->pkg->name, INFRQ_DEPENDS); + for(j = sync->replaces; j; j = j->next) { + pkginfo_t *old = (pkginfo_t*)j->data; + // merge lists + for(k = old->requiredby; k; k = k->next) { + if(!is_in(k->data, new->requiredby)) { + // replace old's name with new's name in the requiredby's dependency list + PMList *m; + pkginfo_t *depender = db_scan(db, k->data, INFRQ_DEPENDS); + for(m = depender->depends; m; m = m->next) { + if(!strcmp(m->data, old->name)) { + FREE(m->data); + m->data = strdup(new->name); + } + } + db_write(db, depender, INFRQ_DEPENDS); + + // add the new requiredby + new->requiredby = list_add(new->requiredby, strdup(k->data)); + } + } + } + db_write(db, new, INFRQ_DEPENDS); + FREEPKG(new); + }*/ + } + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/sync.h b/lib/libalpm/sync.h new file mode 100644 index 00000000..00bd7c57 --- /dev/null +++ b/lib/libalpm/sync.h @@ -0,0 +1,47 @@ +/* + * sync.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_SYNC_H +#define _ALPM_SYNC_H + +#include "db.h" +#include "package.h" +#include "trans.h" +#include "alpm.h" + +typedef struct __pmsync_t { + unsigned char type; + pmpkg_t *lpkg; + pmpkg_t *spkg; +} pmsync_t; + +pmsync_t *sync_new(int type, pmpkg_t *lpkg, pmpkg_t *spkg); + +/*int sync_findpkg(char *name, PMList *dbs, pmsyncpkg_t **sync); +pmsyncpkg_t *find_pkginsync(char *needle, PMList *haystack); +PMList *rm_pkginsync(char *needle, PMList *haystack);*/ + +int sync_sysupgrade(PMList **data); +int sync_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data); +int sync_commit(pmdb_t *db, pmtrans_t *trans); + +#endif /* _ALPM_SYNC_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c new file mode 100644 index 00000000..1aaca0e4 --- /dev/null +++ b/lib/libalpm/trans.c @@ -0,0 +1,187 @@ +/* + * trans.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +/* pacman */ +#include "error.h" +#include "package.h" +#include "util.h" +#include "list.h" +#include "handle.h" +#include "add.h" +#include "remove.h" +#include "sync.h" +#include "alpm.h" + +extern pmhandle_t *handle; + +pmtrans_t *trans_new() +{ + pmtrans_t *trans; + + if((trans = (pmtrans_t *)malloc(sizeof(pmtrans_t))) == NULL) { + return(NULL); + } + + trans->targets = NULL; + trans->packages = NULL; + trans->type = 0; + trans->flags = 0; + trans->cb = NULL; + trans->state = STATE_IDLE; + + return(trans); +} + +void trans_free(pmtrans_t *trans) +{ + if(trans == NULL) { + return; + } + + FREELIST(trans->targets); + FREELISTPKGS(trans->packages); + + free(trans); +} + +int trans_init(pmtrans_t *trans, unsigned char type, unsigned char flags, alpm_trans_cb cb) +{ + /* Sanity checks */ + if(trans == NULL) { + PM_RET_ERR(PM_ERR_TRANS_NULL, -1); + } + + /* ORE + perform sanity checks on type and flags: + for instance, we can't set UPGRADE and FRESHEN at the same time */ + + trans->type = type; + trans->flags = flags; + trans->cb = cb; + trans->state = STATE_INITIALIZED; + + return(0); +} + +int trans_addtarget(pmtrans_t *trans, char *target) +{ + /* Sanity checks */ + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_TRANS_NULL, -1)); + ASSERT(target != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if(pm_list_is_strin(target, trans->targets)) { + PM_RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1); + } + + switch(trans->type) { + case PM_TRANS_TYPE_ADD: + case PM_TRANS_TYPE_UPGRADE: + if(add_loadtarget(handle->db_local, trans, target) == -1) { + /* pm_errno is set by add_loadtarget() */ + return(-1); + } + break; + case PM_TRANS_TYPE_REMOVE: + if(remove_loadtarget(handle->db_local, trans, target) == -1) { + /* pm_errno is set by remove_loadtarget() */ + return(-1); + } + break; + } + trans->targets = pm_list_add(trans->targets, strdup(target)); + trans->state = STATE_INITIALIZED; + + return(0); +} + +int trans_prepare(pmtrans_t *trans, PMList **data) +{ + *data = NULL; + + /* Sanity checks */ + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + ASSERT(trans->packages != NULL, return(0)); + + switch(trans->type) { + case PM_TRANS_TYPE_ADD: + case PM_TRANS_TYPE_UPGRADE: + if(add_prepare(handle->db_local, trans, data) == -1) { + /* pm_errno is set by add_prepare() */ + return(-1); + } + break; + case PM_TRANS_TYPE_REMOVE: + if(remove_prepare(handle->db_local, trans, data) == -1) { + /* pm_errno is set by remove_prepare() */ + return(-1); + } + break; + case PM_TRANS_TYPE_SYNC: + if(sync_prepare(handle->db_local, trans, data) == -1) { + /* pm_errno is set by sync_prepare() */ + return(-1); + } + break; + } + + trans->state = STATE_PREPARED; + + return(0); +} + +int trans_commit(pmtrans_t *trans) +{ + /* Sanity checks */ + ASSERT(trans != NULL, PM_RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + /* If there's nothing to do, return without complaining */ + ASSERT(trans->packages != NULL, return(0)); + + switch(trans->type) { + case PM_TRANS_TYPE_ADD: + case PM_TRANS_TYPE_UPGRADE: + if(add_commit(handle->db_local, trans) == -1) { + return(-1); + } + break; + case PM_TRANS_TYPE_REMOVE: + if(remove_commit(handle->db_local, trans) == -1) { + return(-1); + } + break; + case PM_TRANS_TYPE_SYNC: + if(sync_commit(handle->db_local, trans) == -1) { + return(-1); + } + break; + } + + trans->state = STATE_COMMITED; + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/trans.h b/lib/libalpm/trans.h new file mode 100644 index 00000000..98732fbf --- /dev/null +++ b/lib/libalpm/trans.h @@ -0,0 +1,54 @@ +/* + * trans.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_TRANS_H +#define _ALPM_TRANS_H + +enum { + STATE_IDLE = 0, + STATE_INITIALIZED, + STATE_PREPARED, + STATE_COMMITED +}; + +#include "alpm.h" + +typedef struct __pmtrans_t { + unsigned char type; + unsigned char flags; + unsigned char state; + PMList *targets; /* PMList of (char *) */ + PMList *packages; /* PMList of (pmpkginfo_t *) */ + alpm_trans_cb cb; +} pmtrans_t; + +#define FREETRANS(p) do { if (p) { trans_free(p); p = NULL; } } while (0) +#define TRANS_CB(t, e, d1, d2) do { if((t) && (t)->cb) { (t)->cb(e, d1, d2); } } while(0) + +pmtrans_t *trans_new(); +void trans_free(pmtrans_t *trans); +int trans_init(pmtrans_t *trans, unsigned char type, unsigned char flags, alpm_trans_cb cb); +int trans_addtarget(pmtrans_t *trans, char *target); +int trans_prepare(pmtrans_t *trans, PMList **data); +int trans_commit(pmtrans_t *trans); + +#endif /* _ALPM_TRANS_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c new file mode 100644 index 00000000..9ba3e1ee --- /dev/null +++ b/lib/libalpm/util.c @@ -0,0 +1,401 @@ +/* + * util.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <ctype.h> +#include <dirent.h> +#include <time.h> +#include <syslog.h> +#include <zlib.h> +#include <libtar.h> +/* pacman */ +#include "log.h" +#include "util.h" +#include "alpm.h" + +/* borrowed and modified from Per Liden's pkgutils (http://crux.nu) */ +long _alpm_gzopen_frontend(char *pathname, int oflags, int mode) +{ + char* gzoflags; + int fd; + gzFile gzf; + + switch (oflags & O_ACCMODE) { + case O_WRONLY: + gzoflags = "w"; + break; + case O_RDONLY: + gzoflags = "r"; + break; + case O_RDWR: + default: + errno = EINVAL; + return -1; + } + + if((fd = open(pathname, oflags, mode)) == -1) { + return -1; + } + if((oflags & O_CREAT) && fchmod(fd, mode)) { + return -1; + } + if(!(gzf = gzdopen(fd, gzoflags))) { + errno = ENOMEM; + return -1; + } + + return (long)gzf; +} + +/* does the same thing as 'mkdir -p' */ +int _alpm_makepath(char *path) +{ + char *orig, *str, *ptr; + char full[PATH_MAX] = ""; + mode_t oldmask; + + oldmask = umask(0000); + + orig = strdup(path); + str = orig; + while((ptr = strsep(&str, "/"))) { + if(strlen(ptr)) { + struct stat buf; + + strcat(full, "/"); + strcat(full, ptr); + if(stat(full, &buf)) { + if(mkdir(full, 0755)) { + free(orig); + umask(oldmask); + return(1); + } + } + } + } + free(orig); + umask(oldmask); + return(0); +} + +int _alpm_copyfile(char *src, char *dest) +{ + FILE *in, *out; + size_t len; + char buf[4097]; + + in = fopen(src, "r"); + if(in == NULL) { + return(1); + } + out = fopen(dest, "w"); + if(out == NULL) { + return(1); + } + + while((len = fread(buf, 1, 4096, in))) { + fwrite(buf, 1, len, out); + } + + fclose(in); + fclose(out); + return(0); +} + +/* Convert a string to uppercase + */ +char *_alpm_strtoupper(char *str) +{ + char *ptr = str; + + while(*ptr) { + (*ptr) = toupper(*ptr); + ptr++; + } + return str; +} + +/* Trim whitespace and newlines from a string + */ +char *_alpm_strtrim(char *str) +{ + char *pch = str; + while(isspace(*pch)) { + pch++; + } + if(pch != str) { + memmove(str, pch, (strlen(pch) + 1)); + } + + pch = (char*)(str + (strlen(str) - 1)); + while(isspace(*pch)) { + pch--; + } + *++pch = '\0'; + + return str; +} + +/* A cheap grep for text files, returns 1 if a substring + * was found in the text file fn, 0 if it wasn't + */ +int _alpm_grep(const char *fn, const char *needle) +{ + FILE *fp; + + if((fp = fopen(fn, "r")) == NULL) { + return(0); + } + while(!feof(fp)) { + char line[1024]; + fgets(line, 1024, fp); + if(feof(fp)) { + continue; + } + if(strstr(line, needle)) { + fclose(fp); + return(1); + } + } + fclose(fp); + return(0); +} + +/* Create a lock file + */ +int _alpm_lckmk(char *file) +{ + int fd, count = 0; + + while((fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000)) == -1 && errno == EACCES) { + if(++count < 1) { + sleep(1); + } else { + return(-1); + } + } + return(fd > 0 ? 0 : -1); + + return(0); +} + +/* Remove a lock file + */ +int _alpm_lckrm(char *file) +{ + return(unlink(file) == -1); +} + +int _alpm_unpack(char *archive, const char *prefix, const char *fn) +{ + TAR *tar = NULL; + char expath[PATH_MAX]; + tartype_t gztype = { + (openfunc_t) _alpm_gzopen_frontend, + (closefunc_t)gzclose, + (readfunc_t) gzread, + (writefunc_t)gzwrite + }; + + /* open the .tar.gz package */ + if(tar_open(&tar, archive, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + perror(archive); + return(1); + } + while(!th_read(tar)) { + if(fn && strcmp(fn, th_get_pathname(tar))) { + if(TH_ISREG(tar) && tar_skip_regfile(tar)) { + char errorstr[255]; + snprintf(errorstr, 255, "bad tar archive: %s", archive); + perror(errorstr); + tar_close(tar); + return(1); + } + continue; + } + snprintf(expath, PATH_MAX, "%s/%s", prefix, th_get_pathname(tar)); + if(tar_extract_file(tar, expath)) { + fprintf(stderr, "could not extract %s: %s\n", th_get_pathname(tar), strerror(errno)); + } + if(fn) break; + } + tar_close(tar); + + return(0); +} + +/* does the same thing as 'rm -rf' */ +int _alpm_rmrf(char *path) +{ + int errflag = 0; + struct dirent *dp; + DIR *dirp; + char name[PATH_MAX]; + extern int errno; + + if(!unlink(path)) { + return(0); + } else { + if(errno == ENOENT) { + return(0); + } else if(errno == EPERM) { + /* fallthrough */ + } else if(errno == EISDIR) { + /* fallthrough */ + } else if(errno == ENOTDIR) { + return(1); + } else { + /* not a directory */ + return(1); + } + + if((dirp = opendir(path)) == (DIR *)-1) { + return(1); + } + for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + if(dp->d_ino) { + sprintf(name, "%s/%s", path, dp->d_name); + if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) { + errflag += _alpm_rmrf(name); + } + } + } + closedir(dirp); + if(rmdir(path)) { + errflag++; + } + return(errflag); + } + return(0); +} + +int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...) +{ + char msg[1024]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, 1024, fmt, args); + va_end(args); + + if(usesyslog) { + syslog(LOG_WARNING, "%s", msg); + } + + if(f) { + time_t t; + struct tm *tm; + + t = time(NULL); + tm = localtime(&t); + + fprintf(f, "[%02d/%02d/%02d %02d:%02d] %s\n", tm->tm_mon+1, tm->tm_mday, + tm->tm_year-100, tm->tm_hour, tm->tm_min, msg); + } + + return(0); +} + +int _alpm_ldconfig(char *root) +{ + char line[PATH_MAX]; + struct stat buf; + + snprintf(line, PATH_MAX, "%setc/ld.so.conf", root); + if(!stat(line, &buf)) { + snprintf(line, PATH_MAX, "%ssbin/ldconfig", root); + if(!stat(line, &buf)) { + char cmd[PATH_MAX]; + snprintf(cmd, PATH_MAX, "%s -r %s", line, root); + system(cmd); + } + } + + return(0); +} + +int _alpm_runscriptlet(char *root, char *installfn, char *script, char *ver, char *oldver) +{ + char scriptfn[PATH_MAX]; + char cmdline[PATH_MAX]; + char tmpdir[PATH_MAX] = ""; + char *scriptpath; + struct stat buf; + + return(0); + + if(stat(installfn, &buf)) { + /* not found */ + return(0); + } + + if(!strcmp(script, "pre_upgrade") || !strcmp(script, "pre_install")) { + snprintf(tmpdir, PATH_MAX, "%stmp/", root); + if(stat(tmpdir, &buf)) { + _alpm_makepath(tmpdir); + } + snprintf(tmpdir, PATH_MAX, "%stmp/pacman-XXXXXX", root); + if(mkdtemp(tmpdir) == NULL) { + perror("error creating temp directory"); + return(1); + } + _alpm_unpack(installfn, tmpdir, ".INSTALL"); + snprintf(scriptfn, PATH_MAX, "%s/.INSTALL", tmpdir); + /* chop off the root so we can find the tmpdir in the chroot */ + scriptpath = scriptfn + strlen(root) - 1; + return(0); + } else { + strncpy(scriptfn, installfn, PATH_MAX-1); + /* chop off the root so we can find the tmpdir in the chroot */ + scriptpath = scriptfn + strlen(root) - 1; + } + + if(!_alpm_grep(scriptfn, script)) { + /* script not found in scriptlet file */ + return(0); + } + + /* ORE + pm_cblog(PM_LOG_FLOW2, "Executing %s script...\n", script);*/ + if(oldver) { + snprintf(cmdline, PATH_MAX, "echo \"umask 0022; source %s %s %s %s\" | chroot %s /bin/sh", + scriptpath, script, ver, oldver, root); + } else { + snprintf(cmdline, PATH_MAX, "echo \"umask 0022; source %s %s %s\" | chroot %s /bin/sh", + scriptpath, script, ver, root); + } + /* ORE + pm_cblog(PM_LOG_FLOW2, "%s\n", cmdline);*/ + system(cmdline); + + if(strlen(tmpdir) && _alpm_rmrf(tmpdir)) { + fprintf(stderr, "warning: could not remove tmpdir %s\n", tmpdir); + } + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h new file mode 100644 index 00000000..3776ebbc --- /dev/null +++ b/lib/libalpm/util.h @@ -0,0 +1,57 @@ +/* + * util.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _ALPM_UTIL_H +#define _ALPM_UTIL_H + +#include <stdio.h> + +#define MALLOC(p, b) { \ + if((b) > 0) { \ + p = malloc(b); \ + if (!(p)) { \ + fprintf(stderr, "malloc failure: could not allocate %d bytes\n", (b)); \ + exit(1); \ + } \ + } else { \ + p = NULL; \ + } \ +} +#define FREE(p) do { if (p) { free(p); p = NULL; } } while(0) + +#define ASSERT(cond, action) do { if(!(cond)) { action; } } while(0) + +long _alpm_gzopen_frontend(char *pathname, int oflags, int mode); +int _alpm_makepath(char *path); +int _alpm_copyfile(char *src, char *dest); +char *_alpm_strtoupper(char *str); +char *_alpm_strtrim(char *str); +int _alpm_grep(const char *fn, const char *needle); +int _alpm_lckmk(char *file); +int _alpm_lckrm(char *file); +int _alpm_unpack(char *archive, const char *prefix, const char *fn); +int _alpm_rmrf(char *path); +int _alpm_log_action(unsigned char usesyslog, FILE *f, char *fmt, ...); +int _alpm_ldconfig(char *root); +int _alpm_runscriptlet(char *util, char *installfn, char *script, char *ver, char *oldver); + +#endif /* _ALPM_UTIL_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libftp/Makefile b/lib/libftp/Makefile new file mode 100644 index 00000000..c68bf2b5 --- /dev/null +++ b/lib/libftp/Makefile @@ -0,0 +1,68 @@ +# +# This makefile contains modifications submitted by Richard Braakman +# (dark@xs4all.nl) for the shared library generation. +# + +# By default, ftplib uses PASV. If you need it to use PORT +# instead, uncomment the next line +DEFINES = -DFTPLIB_DEFMODE=FTPLIB_PORT + +SONAME = 3 +SOVERSION = $(SONAME).1 + +TARGETS = qftp libftp.a libftp.so +OBJECTS = qftp.o ftplib.o +SOURCES = qftp.c ftplib.c + +CFLAGS = -Wall $(DEBUG) -I. $(INCLUDES) $(DEFINES) +LDFLAGS = -L. +DEPFLAGS = + +all : $(TARGETS) + +clean : + rm -f $(OBJECTS) core *.bak + rm -rf unshared + +clobber : clean + rm -f $(TARGETS) .depend + rm -f libftp.so.* + +install : all + install qftp /usr/local/bin + install -m 644 libftp.so.$(SOVERSION) /usr/local/lib + install -m 644 ftplib.h /usr/local/include + (cd /usr/local/lib && \ + ln -sf libftp.so.$(SOVERSION) libftp.so.$(SONAME) && \ + ln -sf libftp.so.$(SONAME) libftp.so) + -(cd /usr/local/bin && \ + for f in ftpdir ftpget ftplist ftprm ftpsend; \ + do ln -s qftp $$f; done) + +depend : + $(CC) $(CFLAGS) -M $(SOURCES) > .depend + +# build without -fPIC +unshared/ftplib.o: ftplib.c ftplib.h + -mkdir unshared + $(CC) -c $(CFLAGS) -D_REENTRANT $< -o $@ + +ftplib.o: ftplib.c ftplib.h + $(CC) -c $(CFLAGS) -fPIC -D_REENTRANT $< -o $@ + +libftp.a: unshared/ftplib.o + ar -rcs $@ $< + +libftp.so.$(SOVERSION): ftplib.o + $(CC) -shared -Wl,-soname,libftp.so.$(SONAME) -lc -o $@ $< + +libftp.so: libftp.so.$(SOVERSION) + ln -sf $< libftp.so.$(SONAME) + ln -sf $< $@ + +qftp : qftp.o libftp.so ftplib.h + $(CC) $(LDFLAGS) -o $@ $< -lftp + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/lib/libftp/ftplib.c b/lib/libftp/ftplib.c new file mode 100644 index 00000000..b69744eb --- /dev/null +++ b/lib/libftp/ftplib.c @@ -0,0 +1,1569 @@ +/***************************************************************************/ +/* */ +/* ftplib.c - callable ftp access routines */ +/* Copyright (C) 1996-2000 Thomas Pfau, pfau@cnj.digex.net */ +/* 73 Catherine Street, South Bound Brook, NJ, 08880 */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this progam; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/***************************************************************************/ + +#if defined(__unix__) || defined(__VMS) +#include <unistd.h> +#endif +#if defined(_WIN32) +#include <windows.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#if defined(__unix__) +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#elif defined(VMS) +#include <types.h> +#include <socket.h> +#include <in.h> +#include <netdb.h> +#include <inet.h> +#elif defined(_WIN32) +#include <winsock.h> +#endif + +#define BUILDING_LIBRARY +#include "ftplib.h" + +#if defined(_WIN32) +#define SETSOCKOPT_OPTVAL_TYPE (const char *) +#else +#define SETSOCKOPT_OPTVAL_TYPE (void *) +#endif + +#define FTPLIB_BUFSIZ 8192 +#define ACCEPT_TIMEOUT 30 + +#define FTPLIB_CONTROL 0 +#define FTPLIB_READ 1 +#define FTPLIB_WRITE 2 + +#if !defined FTPLIB_DEFMODE +#define FTPLIB_DEFMODE FTPLIB_PASSIVE +#endif + +struct NetBuf { + char *cput,*cget; + int handle; + int cavail,cleft; + char *buf; + int dir; + netbuf *ctrl; + netbuf *data; + int cmode; + struct timeval idletime; + FtpCallback idlecb; + void *idlearg; + int xfered; + int cbbytes; + int xfered1; + char response[256]; +}; + +static char *version = +"ftplib Release 3.1-1 9/16/00, copyright 1996-2000 Thomas Pfau"; + +GLOBALDEF int ftplib_debug = 0; + +#if defined(__unix__) || defined(VMS) +#define net_read read +#define net_write write +#define net_close close +#elif defined(_WIN32) +#define net_read(x,y,z) recv(x,y,z,0) +#define net_write(x,y,z) send(x,y,z,0) +#define net_close closesocket +#endif + +#if defined(NEED_MEMCCPY) + /* + * VAX C does not supply a memccpy routine so I provide my own + */ +void *memccpy(void *dest, const void *src, int c, size_t n) +{ + int i=0; + const unsigned char *ip=src; + unsigned char *op=dest; + + while (i < n) + { + if ((*op++ = *ip++) == c) + break; + i++; + } + if (i == n) + return NULL; + return op; +} +#endif +#if defined(NEED_STRDUP) +/* + * strdup - return a malloc'ed copy of a string + */ +char *strdup(const char *src) +{ + int l = strlen(src) + 1; + char *dst = malloc(l); + if (dst) + strcpy(dst,src); + return dst; +} +#endif + +/* + * socket_wait - wait for socket to receive or flush data + * + * return 1 if no user callback, otherwise, return value returned by + * user callback + */ +static int socket_wait(netbuf *ctl) +{ + fd_set fd,*rfd = NULL,*wfd = NULL; + struct timeval tv; + int rv = 0; + if ((ctl->dir == FTPLIB_CONTROL) || (ctl->idlecb == NULL)) + return 1; + if (ctl->dir == FTPLIB_WRITE) + wfd = &fd; + else + rfd = &fd; + FD_ZERO(&fd); + do + { + FD_SET(ctl->handle,&fd); + tv = ctl->idletime; + rv = select(ctl->handle+1, rfd, wfd, NULL, &tv); + if (rv == -1) + { + rv = 0; + strncpy(ctl->ctrl->response, strerror(errno), + sizeof(ctl->ctrl->response)); + break; + } + else if (rv > 0) + { + rv = 1; + break; + } + } + while ((rv = ctl->idlecb(ctl, ctl->xfered, ctl->idlearg))); + return rv; +} + +/* + * read a line of text + * + * return -1 on error or bytecount + */ +static int readline(char *buf,int max,netbuf *ctl) +{ + int x,retval = 0; + char *end,*bp=buf; + int eof = 0; + + if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ)) + return -1; + if (max == 0) + return 0; + do + { + if (ctl->cavail > 0) + { + x = (max >= ctl->cavail) ? ctl->cavail : max-1; + end = memccpy(bp,ctl->cget,'\n',x); + if (end != NULL) + x = end - bp; + retval += x; + bp += x; + *bp = '\0'; + max -= x; + ctl->cget += x; + ctl->cavail -= x; + if (end != NULL) + { + bp -= 2; + if (strcmp(bp,"\r\n") == 0) + { + *bp++ = '\n'; + *bp++ = '\0'; + --retval; + } + break; + } + } + if (max == 1) + { + *buf = '\0'; + break; + } + if (ctl->cput == ctl->cget) + { + ctl->cput = ctl->cget = ctl->buf; + ctl->cavail = 0; + ctl->cleft = FTPLIB_BUFSIZ; + } + if (eof) + { + if (retval == 0) + retval = -1; + break; + } + if (!socket_wait(ctl)) + return retval; + if ((x = net_read(ctl->handle,ctl->cput,ctl->cleft)) == -1) + { + perror("read"); + retval = -1; + break; + } + if (x == 0) + eof = 1; + ctl->cleft -= x; + ctl->cavail += x; + ctl->cput += x; + } + while (1); + return retval; +} + +/* + * write lines of text + * + * return -1 on error or bytecount + */ +static int writeline(char *buf, int len, netbuf *nData) +{ + int x, nb=0, w; + char *ubp = buf, *nbp; + char lc=0; + + if (nData->dir != FTPLIB_WRITE) + return -1; + nbp = nData->buf; + for (x=0; x < len; x++) + { + if ((*ubp == '\n') && (lc != '\r')) + { + if (nb == FTPLIB_BUFSIZ) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + if (w != FTPLIB_BUFSIZ) + { + printf("net_write(1) returned %d, errno = %d\n", w, errno); + return(-1); + } + nb = 0; + } + nbp[nb++] = '\r'; + } + if (nb == FTPLIB_BUFSIZ) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + if (w != FTPLIB_BUFSIZ) + { + printf("net_write(2) returned %d, errno = %d\n", w, errno); + return(-1); + } + nb = 0; + } + nbp[nb++] = lc = *ubp++; + } + if (nb) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, nb); + if (w != nb) + { + printf("net_write(3) returned %d, errno = %d\n", w, errno); + return(-1); + } + } + return len; +} + +/* + * read a response from the server + * + * return 0 if first char doesn't match + * return 1 if first char matches + */ +static int readresp(char c, netbuf *nControl) +{ + char match[5]; + if (readline(nControl->response,256,nControl) == -1) + { + perror("Control socket read failed"); + return 0; + } + if (ftplib_debug > 1) + fprintf(stderr,"%s",nControl->response); + if (nControl->response[3] == '-') + { + strncpy(match,nControl->response,3); + match[3] = ' '; + match[4] = '\0'; + do + { + if (readline(nControl->response,256,nControl) == -1) + { + perror("Control socket read failed"); + return 0; + } + if (ftplib_debug > 1) + fprintf(stderr,"%s",nControl->response); + } + while (strncmp(nControl->response,match,4)); + } + if (nControl->response[0] == c) + return 1; + return 0; +} + +/* + * FtpInit for stupid operating systems that require it (Windows NT) + */ +GLOBALDEF void FtpInit(void) +{ +#if defined(_WIN32) + WORD wVersionRequested; + WSADATA wsadata; + int err; + wVersionRequested = MAKEWORD(1,1); + if ((err = WSAStartup(wVersionRequested,&wsadata)) != 0) + fprintf(stderr,"Network failed to start: %d\n",err); +#endif +} + +/* + * FtpLastResponse - return a pointer to the last response received + */ +GLOBALDEF char *FtpLastResponse(netbuf *nControl) +{ + if ((nControl) && (nControl->dir == FTPLIB_CONTROL)) + return nControl->response; + return NULL; +} + +/* + * FtpConnect - connect to remote server + * + * return 1 if connected, 0 if not + */ +GLOBALDEF int FtpConnect(const char *host, netbuf **nControl) +{ + int sControl; + struct sockaddr_in sin; + struct hostent *phe; + struct servent *pse; + int on=1; + netbuf *ctrl; + char *lhost; + char *pnum; + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + lhost = strdup(host); + pnum = strchr(lhost,':'); + if (pnum == NULL) + { +#if defined(VMS) + sin.sin_port = htons(21); +#else + if ((pse = getservbyname("ftp","tcp")) == NULL) + { + perror("getservbyname"); + return 0; + } + sin.sin_port = pse->s_port; +#endif + } + else + { + *pnum++ = '\0'; + if (isdigit(*pnum)) + sin.sin_port = htons(atoi(pnum)); + else + { + pse = getservbyname(pnum,"tcp"); + if(pse == NULL) { + perror("getservbyname"); + return 0; + } + sin.sin_port = pse->s_port; + } + } + if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1) + { + if ((phe = gethostbyname(lhost)) == NULL) + { + perror("gethostbyname"); + return 0; + } + memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); + } + free(lhost); + sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sControl == -1) + { + perror("socket"); + return 0; + } + if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, + SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) + { + perror("setsockopt"); + net_close(sControl); + return 0; + } + if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) + { + perror("connect"); + net_close(sControl); + return 0; + } + ctrl = calloc(1,sizeof(netbuf)); + if (ctrl == NULL) + { + perror("calloc"); + net_close(sControl); + return 0; + } + ctrl->buf = malloc(FTPLIB_BUFSIZ); + if (ctrl->buf == NULL) + { + perror("calloc"); + net_close(sControl); + free(ctrl); + return 0; + } + ctrl->handle = sControl; + ctrl->dir = FTPLIB_CONTROL; + ctrl->ctrl = NULL; + ctrl->cmode = FTPLIB_DEFMODE; + ctrl->idlecb = NULL; + ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0; + ctrl->idlearg = NULL; + ctrl->xfered = 0; + ctrl->xfered1 = 0; + ctrl->cbbytes = 0; + if (readresp('2', ctrl) == 0) + { + net_close(sControl); + free(ctrl->buf); + free(ctrl); + return 0; + } + *nControl = ctrl; + return 1; +} + +/* + * FtpOptions - change connection options + * + * returns 1 if successful, 0 on error + */ +GLOBALDEF int FtpOptions(int opt, long val, netbuf *nControl) +{ + int v,rv=0; + switch (opt) + { + case FTPLIB_CONNMODE: + v = (int) val; + if ((v == FTPLIB_PASSIVE) || (v == FTPLIB_PORT)) + { + nControl->cmode = v; + rv = 1; + } + break; + case FTPLIB_CALLBACK: + nControl->idlecb = (FtpCallback) val; + rv = 1; + break; + case FTPLIB_IDLETIME: + v = (int) val; + rv = 1; + nControl->idletime.tv_sec = v / 1000; + nControl->idletime.tv_usec = (v % 1000) * 1000; + break; + case FTPLIB_CALLBACKARG: + rv = 1; + nControl->idlearg = (void *) val; + break; + case FTPLIB_CALLBACKBYTES: + rv = 1; + nControl->cbbytes = (int) val; + break; + } + return rv; +} + +/* + * FtpSendCmd - send a command and wait for expected response + * + * return 1 if proper response received, 0 otherwise + */ +static int FtpSendCmd(const char *cmd, char expresp, netbuf *nControl) +{ + char buf[256]; + if (nControl->dir != FTPLIB_CONTROL) + return 0; + if (ftplib_debug > 2) + fprintf(stderr,"%s\n",cmd); + if ((strlen(cmd) + 3) > sizeof(buf)) + return 0; + sprintf(buf,"%s\r\n",cmd); + if (net_write(nControl->handle,buf,strlen(buf)) <= 0) + { + perror("write"); + return 0; + } + return readresp(expresp, nControl); +} + +/* + * FtpLogin - log in to remote server + * + * return 1 if logged in, 0 otherwise + */ +GLOBALDEF int FtpLogin(const char *user, const char *pass, netbuf *nControl) +{ + char tempbuf[64]; + + if (((strlen(user) + 7) > sizeof(tempbuf)) || + ((strlen(pass) + 7) > sizeof(tempbuf))) + return 0; + sprintf(tempbuf,"USER %s",user); + if (!FtpSendCmd(tempbuf,'3',nControl)) + { + if (nControl->response[0] == '2') + return 1; + return 0; + } + sprintf(tempbuf,"PASS %s",pass); + return FtpSendCmd(tempbuf,'2',nControl); +} + +/* + * FtpOpenPort - set up data connection + * + * return 1 if successful, 0 otherwise + */ +static int FtpOpenPort(netbuf *nControl, netbuf **nData, int mode, int dir) +{ + int sData; + union { + struct sockaddr sa; + struct sockaddr_in in; + } sin; + struct linger lng = { 0, 0 }; + unsigned int l; + int on=1; + netbuf *ctrl; + char *cp; + unsigned int v[6]; + char buf[256]; + + if (nControl->dir != FTPLIB_CONTROL) + return -1; + if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE)) + { + sprintf(nControl->response, "Invalid direction %d\n", dir); + return -1; + } + if ((mode != FTPLIB_ASCII) && (mode != FTPLIB_IMAGE)) + { + sprintf(nControl->response, "Invalid mode %c\n", mode); + return -1; + } + l = sizeof(sin); + if (nControl->cmode == FTPLIB_PASSIVE) + { + memset(&sin, 0, l); + sin.in.sin_family = AF_INET; + if (!FtpSendCmd("PASV",'2',nControl)) + return -1; + cp = strchr(nControl->response,'('); + if (cp == NULL) + return -1; + cp++; + sscanf(cp,"%u,%u,%u,%u,%u,%u",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + sin.sa.sa_data[2] = v[2]; + sin.sa.sa_data[3] = v[3]; + sin.sa.sa_data[4] = v[4]; + sin.sa.sa_data[5] = v[5]; + sin.sa.sa_data[0] = v[0]; + sin.sa.sa_data[1] = v[1]; + } + else + { + if (getsockname(nControl->handle, &sin.sa, &l) < 0) + { + perror("getsockname"); + return 0; + } + } + sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (sData == -1) + { + perror("socket"); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR, + SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1) + { + perror("setsockopt"); + net_close(sData); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_LINGER, + SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1) + { + perror("setsockopt"); + net_close(sData); + return -1; + } + if (nControl->cmode == FTPLIB_PASSIVE) + { + if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1) + { + perror("connect"); + net_close(sData); + return -1; + } + } + else + { + sin.in.sin_port = 0; + if (bind(sData, &sin.sa, sizeof(sin)) == -1) + { + perror("bind"); + net_close(sData); + return 0; + } + if (listen(sData, 1) < 0) + { + perror("listen"); + net_close(sData); + return 0; + } + if (getsockname(sData, &sin.sa, &l) < 0) + return 0; + sprintf(buf, "PORT %d,%d,%d,%d,%d,%d", + (unsigned char) sin.sa.sa_data[2], + (unsigned char) sin.sa.sa_data[3], + (unsigned char) sin.sa.sa_data[4], + (unsigned char) sin.sa.sa_data[5], + (unsigned char) sin.sa.sa_data[0], + (unsigned char) sin.sa.sa_data[1]); + if (!FtpSendCmd(buf,'2',nControl)) + { + net_close(sData); + return 0; + } + } + ctrl = calloc(1,sizeof(netbuf)); + if (ctrl == NULL) + { + perror("calloc"); + net_close(sData); + return -1; + } + if ((mode == 'A') && ((ctrl->buf = malloc(FTPLIB_BUFSIZ)) == NULL)) + { + perror("calloc"); + net_close(sData); + free(ctrl); + return -1; + } + ctrl->handle = sData; + ctrl->dir = dir; + ctrl->idletime = nControl->idletime; + ctrl->idlearg = nControl->idlearg; + ctrl->xfered = 0; + ctrl->xfered1 = 0; + ctrl->cbbytes = nControl->cbbytes; + if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec || ctrl->cbbytes) + ctrl->idlecb = nControl->idlecb; + else + ctrl->idlecb = NULL; + *nData = ctrl; + return 1; +} + +/* + * FtpAcceptConnection - accept connection from server + * + * return 1 if successful, 0 otherwise + */ +static int FtpAcceptConnection(netbuf *nData, netbuf *nControl) +{ + int sData; + struct sockaddr addr; + unsigned int l; + int i; + struct timeval tv; + fd_set mask; + int rv; + + FD_ZERO(&mask); + FD_SET(nControl->handle, &mask); + FD_SET(nData->handle, &mask); + tv.tv_usec = 0; + tv.tv_sec = ACCEPT_TIMEOUT; + i = nControl->handle; + if (i < nData->handle) + i = nData->handle; + i = select(i+1, &mask, NULL, NULL, &tv); + if (i == -1) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + net_close(nData->handle); + nData->handle = 0; + rv = 0; + } + else if (i == 0) + { + strcpy(nControl->response, "timed out waiting for connection"); + net_close(nData->handle); + nData->handle = 0; + rv = 0; + } + else + { + if (FD_ISSET(nData->handle, &mask)) + { + l = sizeof(addr); + sData = accept(nData->handle, &addr, &l); + i = errno; + net_close(nData->handle); + if (sData > 0) + { + rv = 1; + nData->handle = sData; + } + else + { + strncpy(nControl->response, strerror(i), + sizeof(nControl->response)); + nData->handle = 0; + rv = 0; + } + } + else if (FD_ISSET(nControl->handle, &mask)) + { + net_close(nData->handle); + nData->handle = 0; + readresp('2', nControl); + rv = 0; + } + } + return rv; +} + +/* + * FtpAccess - return a handle for a data stream + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl, + netbuf **nData) +{ + char buf[256]; + int dir; + if ((path == NULL) && + ((typ == FTPLIB_FILE_WRITE) || (typ == FTPLIB_FILE_READ))) + { + sprintf(nControl->response, + "Missing path argument for file transfer\n"); + return 0; + } + sprintf(buf, "TYPE %c", mode); + if (!FtpSendCmd(buf, '2', nControl)) + return 0; + switch (typ) + { + case FTPLIB_DIR: + strcpy(buf,"NLST"); + dir = FTPLIB_READ; + break; + case FTPLIB_DIR_VERBOSE: + strcpy(buf,"LIST"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_READ: + strcpy(buf,"RETR"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_WRITE: + strcpy(buf,"STOR"); + dir = FTPLIB_WRITE; + break; + default: + sprintf(nControl->response, "Invalid open type %d\n", typ); + return 0; + } + if (path != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + if ((strlen(path) + i) >= sizeof(buf)) + return 0; + strcpy(&buf[i],path); + } + if (FtpOpenPort(nControl, nData, mode, dir) == -1) + return 0; + if (!FtpSendCmd(buf, '1', nControl)) + { + FtpClose(*nData); + *nData = NULL; + return 0; + } + (*nData)->ctrl = nControl; + nControl->data = *nData; + if (nControl->cmode == FTPLIB_PORT) + { + if (!FtpAcceptConnection(*nData,nControl)) + { + FtpClose(*nData); + *nData = NULL; + nControl->data = NULL; + return 0; + } + } + return 1; +} + +/* + * FtpRead - read from a data connection + */ +GLOBALDEF int FtpRead(void *buf, int max, netbuf *nData) +{ + int i; + if (nData->dir != FTPLIB_READ) + return 0; + if (nData->buf) + i = readline(buf, max, nData); + else + { + i = socket_wait(nData); + if (i != 1) + return 0; + i = net_read(nData->handle, buf, max); + } + if (i == -1) + return 0; + nData->xfered += i; + if (nData->idlecb && nData->cbbytes) + { + nData->xfered1 += i; + if (nData->xfered1 > nData->cbbytes) + { + if (nData->idlecb(nData, nData->xfered, nData->idlearg) == 0) + return 0; + nData->xfered1 = 0; + } + } + return i; +} + +/* + * FtpWrite - write to a data connection + */ +GLOBALDEF int FtpWrite(void *buf, int len, netbuf *nData) +{ + int i; + if (nData->dir != FTPLIB_WRITE) + return 0; + if (nData->buf) + i = writeline(buf, len, nData); + else + { + socket_wait(nData); + i = net_write(nData->handle, buf, len); + } + if (i == -1) + return 0; + nData->xfered += i; + if (nData->idlecb && nData->cbbytes) + { + nData->xfered1 += i; + if (nData->xfered1 > nData->cbbytes) + { + nData->idlecb(nData, nData->xfered, nData->idlearg); + nData->xfered1 = 0; + } + } + return i; +} + +/* + * FtpClose - close a data connection + */ +GLOBALDEF int FtpClose(netbuf *nData) +{ + netbuf *ctrl; + switch (nData->dir) + { + case FTPLIB_WRITE: + /* potential problem - if buffer flush fails, how to notify user? */ + if (nData->buf != NULL) + writeline(NULL, 0, nData); + case FTPLIB_READ: + if (nData->buf) + free(nData->buf); + shutdown(nData->handle,2); + net_close(nData->handle); + ctrl = nData->ctrl; + free(nData); + if (ctrl) + { + ctrl->data = NULL; + return(readresp('2', ctrl)); + } + return 1; + case FTPLIB_CONTROL: + if (nData->data) + { + nData->ctrl = NULL; + FtpClose(nData); + } + net_close(nData->handle); + free(nData); + return 0; + } + return 1; +} + +/* + * FtpSite - send a SITE command + * + * return 1 if command successful, 0 otherwise + */ +GLOBALDEF int FtpSite(const char *cmd, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(cmd) + 7) > sizeof(buf)) + return 0; + sprintf(buf,"SITE %s",cmd); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpSysType - send a SYST command + * + * Fills in the user buffer with the remote system type. If more + * information from the response is required, the user can parse + * it out of the response buffer returned by FtpLastResponse(). + * + * return 1 if command successful, 0 otherwise + */ +GLOBALDEF int FtpSysType(char *buf, int max, netbuf *nControl) +{ + int l = max; + char *b = buf; + char *s; + if (!FtpSendCmd("SYST",'2',nControl)) + return 0; + s = &nControl->response[4]; + while ((--l) && (*s != ' ')) + *b++ = *s++; + *b++ = '\0'; + return 1; +} + +/* + * FtpMkdir - create a directory at server + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpMkdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"MKD %s",path); + if (!FtpSendCmd(buf,'2', nControl)) + return 0; + return 1; +} + +/* + * FtpChdir - change path at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpChdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"CWD %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpCDUp - move to parent directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpCDUp(netbuf *nControl) +{ + if (!FtpSendCmd("CDUP",'2',nControl)) + return 0; + return 1; +} + +/* + * FtpRmdir - remove directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpRmdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"RMD %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpPwd - get working directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpPwd(char *path, int max, netbuf *nControl) +{ + int l = max; + char *b = path; + char *s; + if (!FtpSendCmd("PWD",'2',nControl)) + return 0; + s = strchr(nControl->response, '"'); + if (s == NULL) + return 0; + s++; + while ((--l) && (*s) && (*s != '"')) + *b++ = *s++; + *b++ = '\0'; + return 1; +} + +/* + * FtpXfer - issue a command and transfer data + * + * return 1 if successful, 0 otherwise + */ +static int FtpXfer(const char *localfile, const char *path, + netbuf *nControl, int typ, int mode) +{ + int l,c; + char *dbuf; + FILE *local = NULL; + netbuf *nData; + int rv=1; + + if (localfile != NULL) + { + char ac[4] = "a"; + if (typ == FTPLIB_FILE_WRITE) + ac[0] = 'r'; + if (mode == FTPLIB_IMAGE) + ac[1] = 'b'; + local = fopen(localfile, ac); + if (local == NULL) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + return 0; + } + } + if (local == NULL) + local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout; + if (!FtpAccess(path, typ, mode, nControl, &nData)) + return 0; + dbuf = malloc(FTPLIB_BUFSIZ); + if (typ == FTPLIB_FILE_WRITE) + { + while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0) + if ((c = FtpWrite(dbuf, l, nData)) < l) + { + printf("short write: passed %d, wrote %d\n", l, c); + rv = 0; + break; + } + } + else + { + while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0) + if (fwrite(dbuf, 1, l, local) < l) + { + perror("\nlocalfile write"); + rv = 0; + break; + } + } + free(dbuf); + fflush(local); + if (localfile != NULL) + fclose(local); + FtpClose(nData); + return rv; +} + +/* + * FtpNlst - issue an NLST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpNlst(const char *outputfile, const char *path, + netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_DIR, FTPLIB_ASCII); +} + +/* + * FtpDir - issue a LIST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpDir(const char *outputfile, const char *path, netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_DIR_VERBOSE, FTPLIB_ASCII); +} + +/* + * FtpSize - determine the size of a remote file + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpSize(const char *path, int *size, char mode, netbuf *nControl) +{ + char cmd[256]; + int resp,sz,rv=1; + + if ((strlen(path) + 7) > sizeof(cmd)) + return 0; + sprintf(cmd, "TYPE %c", mode); + if (!FtpSendCmd(cmd, '2', nControl)) + return 0; + sprintf(cmd,"SIZE %s",path); + if (!FtpSendCmd(cmd,'2',nControl)) + rv = 0; + else + { + if (sscanf(nControl->response, "%d %d", &resp, &sz) == 2) + *size = sz; + else + rv = 0; + } + return rv; +} + +/* + * FtpRestart - issue a REST command + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpRestart(int offset, netbuf *nControl) +{ + char cmd[256]; + int rv=1; + + sprintf(cmd,"REST %d",offset); + if (!FtpSendCmd(cmd,'3',nControl)) + rv = 0; + return rv; +} + +/* + * FtpModDate - determine the modification date of a remote file + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl) +{ + char buf[256]; + int rv = 1; + + if ((strlen(path) + 7) > sizeof(buf)) + return 0; + sprintf(buf,"MDTM %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + rv = 0; + else + strncpy(dt, &nControl->response[4], max); + return rv; +} + +/* + * FtpGet - issue a GET command and write received data to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpGet(const char *outputfile, const char *path, + char mode, netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_FILE_READ, mode); +} + +/* + * FtpPut - issue a PUT command and send data from input + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpPut(const char *inputfile, const char *path, char mode, + netbuf *nControl) +{ + return FtpXfer(inputfile, path, nControl, FTPLIB_FILE_WRITE, mode); +} + +/* + * FtpRename - rename a file at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpRename(const char *src, const char *dst, netbuf *nControl) +{ + char cmd[256]; + + if (((strlen(src) + 7) > sizeof(cmd)) || + ((strlen(dst) + 7) > sizeof(cmd))) + return 0; + sprintf(cmd,"RNFR %s",src); + if (!FtpSendCmd(cmd,'3',nControl)) + return 0; + sprintf(cmd,"RNTO %s",dst); + if (!FtpSendCmd(cmd,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpDelete - delete a file at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpDelete(const char *fnm, netbuf *nControl) +{ + char cmd[256]; + + if ((strlen(fnm) + 7) > sizeof(cmd)) + return 0; + sprintf(cmd,"DELE %s",fnm); + if (!FtpSendCmd(cmd,'2', nControl)) + return 0; + return 1; +} + +/* + * FtpQuit - disconnect from remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF void FtpQuit(netbuf *nControl) +{ + if (nControl->dir != FTPLIB_CONTROL) + return; + FtpSendCmd("QUIT",'2',nControl); + net_close(nControl->handle); + free(nControl->buf); + free(nControl); +} + +/* + * HttpConnect - connect to remote server + * + * return 1 if connected, 0 if not + */ +GLOBALREF int HttpConnect(const char *host, unsigned short port, netbuf **nControl) +{ + int sControl; + struct sockaddr_in sin; + struct hostent *phe; + struct servent *pse; + netbuf *ctrl; + char *lhost; + char *pnum; + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + lhost = strdup(host); + pnum = strchr(lhost,':'); + if (pnum == NULL) + { +#if defined(VMS) + sin.sin_port = htons(80); +#else + /* we pass a port variable instead (for use with proxies) + * + if ((pse = getservbyname("http","tcp")) == NULL) + { + perror("getservbyname"); + return 0; + } + sin.sin_port = pse->s_port; + */ + sin.sin_port = htons(port); +#endif + } + else + { + *pnum++ = '\0'; + if (isdigit(*pnum)) + sin.sin_port = htons(atoi(pnum)); + else + { + pse = getservbyname(pnum,"tcp"); + sin.sin_port = pse->s_port; + } + } + if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1) + { + if ((phe = gethostbyname(lhost)) == NULL) + { + perror("gethostbyname"); + return 0; + } + memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); + } + free(lhost); + sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sControl == -1) + { + perror("socket"); + return 0; + } + if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) + { + perror("connect"); + net_close(sControl); + return 0; + } + ctrl = calloc(1,sizeof(netbuf)); + if (ctrl == NULL) + { + perror("calloc"); + net_close(sControl); + return 0; + } + ctrl->buf = NULL; + ctrl->handle = sControl; + ctrl->dir = FTPLIB_CONTROL; + ctrl->ctrl = NULL; + ctrl->cmode = FTPLIB_DEFMODE; + ctrl->idlecb = NULL; + ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0; + ctrl->idlearg = NULL; + ctrl->xfered = 0; + ctrl->xfered1 = 0; + ctrl->cbbytes = 0; + *nControl = ctrl; + return 1; +} + +/* + * HttpSendCmd - send a command + * + * return 1 if proper response received, 0 otherwise + */ +static int HttpSendCmd(const char *cmd, char expresp, netbuf *nControl) +{ + int ret = 0; + char *buf = nControl->response; + //if (nControl->dir != FTPLIB_CONTROL) + //return 0; + if (ftplib_debug > 2) + fprintf(stderr,"%s\n",cmd); + if (net_write(nControl->handle,cmd,strlen(cmd)) <= 0) + { + perror("write"); + return 0; + } + while (ret < 256) { + if (socket_wait(nControl) != 1) + return 0; + if (net_read(nControl->handle,buf,1) != 1) { + break; + } + ret++; + if (*buf == '\r') continue; + if (*buf == '\n') break; + buf++; + } + *buf = 0; + if (nControl->response[9] == expresp) + return 1; + return 0; +} + +/* + * HttpXfer - issue a command and transfer data + * + * return 1 if successful, 0 otherwise + */ +static int HttpXfer(const char *localfile, const char *path, int *size, + netbuf *nControl, int typ, int mode) +{ + int l,c; + char *dbuf; + FILE *local = NULL; + int rv=1; + int bytes = 0; + + if (localfile != NULL) + { + char ac[4] = "a"; + if (typ == FTPLIB_FILE_WRITE) + ac[0] = 'r'; + if (mode == FTPLIB_IMAGE) + ac[1] = 'b'; + local = fopen(localfile, ac); + if (local == NULL) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + return 0; + } + } + if (local == NULL) + local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout; + dbuf = malloc(FTPLIB_BUFSIZ); + if (typ == FTPLIB_FILE_WRITE) + { + while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0) + if ((c = FtpWrite(dbuf, l, nControl)) < l) + { + printf("short write: passed %d, wrote %d\n", l, c); + rv = 0; + break; + } + } + else + { + nControl->dir = FTPLIB_READ; + while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nControl)) > 0) { + if (fwrite(dbuf, 1, l, local) < l) + { + perror("\nlocalfile write"); + rv = 0; + break; + } + bytes += l; + if(size && bytes >= *size) { + break; + } + } + } + free(dbuf); + fflush(local); + if (localfile != NULL) + fclose(local); + free(nControl->data); + return rv; +} + +/* + * HttpGet - issue a GET command and write received data to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALREF int HttpGet(const char *host, const char *outputfile, const char *path, + int *size, netbuf *nControl, unsigned int offset) +{ + char buf[256]; + + if(offset > 0) + sprintf(buf, "GET %s HTTP/1.1\r\nHost: %s\r\nRange: bytes=%d-\r\n\r\n", path, host, offset); + else + sprintf(buf, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", path, host); + if(!HttpSendCmd(buf,'2',nControl)) + { + if (nControl->response[9] == '3') + printf("redirection not supported\n"); + return 0; + } + + while (1) + { + int ret = 0; + char *buf = nControl->response; + while (ret < 256) { + if (socket_wait(nControl) != 1) + return 0; + if (net_read(nControl->handle,buf,1) != 1) + break; + ret++; + if (*buf == '\r') continue; + if (*buf == '\n') break; + buf++; + } + *buf = 0; + if (strstr(nControl->response,"Content-Length")) + { + sscanf(nControl->response,"Content-Length: %d",size); + if(offset > 0) + *size += offset; + } + if (strlen(nControl->response) == 0) + break; + } + + return HttpXfer(outputfile, path, size, nControl, FTPLIB_FILE_READ, FTPLIB_IMAGE); +} + +/* + * HttpQuit - disconnect from remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALREF void HttpQuit(netbuf *nControl) +{ + if(nControl) { + net_close(nControl->handle); + free(nControl); + } +} diff --git a/lib/libftp/ftplib.h b/lib/libftp/ftplib.h new file mode 100644 index 00000000..49e0ccbf --- /dev/null +++ b/lib/libftp/ftplib.h @@ -0,0 +1,131 @@ +/***************************************************************************/ +/* */ +/* ftplib.h - header file for callable ftp access routines */ +/* Copyright (C) 1996, 1997 Thomas Pfau, pfau@cnj.digex.net */ +/* 73 Catherine Street, South Bound Brook, NJ, 08880 */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this progam; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/***************************************************************************/ + +#if !defined(__FTPLIB_H) +#define __FTPLIB_H + +#if defined(__unix__) || defined(VMS) +#define GLOBALDEF +#define GLOBALREF extern +#elif defined(_WIN32) +#if defined BUILDING_LIBRARY +#define GLOBALDEF __declspec(dllexport) +#define GLOBALREF __declspec(dllexport) +#else +#define GLOBALREF __declspec(dllimport) +#endif +#endif + +/* FtpAccess() type codes */ +#define FTPLIB_DIR 1 +#define FTPLIB_DIR_VERBOSE 2 +#define FTPLIB_FILE_READ 3 +#define FTPLIB_FILE_WRITE 4 + +/* FtpAccess() mode codes */ +#define FTPLIB_ASCII 'A' +#define FTPLIB_IMAGE 'I' +#define FTPLIB_TEXT FTPLIB_ASCII +#define FTPLIB_BINARY FTPLIB_IMAGE + +/* connection modes */ +#define FTPLIB_PASSIVE 1 +#define FTPLIB_PORT 2 + +/* connection option names */ +#define FTPLIB_CONNMODE 1 +#define FTPLIB_CALLBACK 2 +#define FTPLIB_IDLETIME 3 +#define FTPLIB_CALLBACKARG 4 +#define FTPLIB_CALLBACKBYTES 5 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct NetBuf netbuf; +typedef int (*FtpCallback)(netbuf *nControl, int xfered, void *arg); + +/* v1 compatibility stuff */ +#if !defined(_FTPLIB_NO_COMPAT) +netbuf *DefaultNetbuf; + +#define ftplib_lastresp FtpLastResponse(DefaultNetbuf) +#define ftpInit FtpInit +#define ftpOpen(x) FtpConnect(x, &DefaultNetbuf) +#define ftpLogin(x,y) FtpLogin(x, y, DefaultNetbuf) +#define ftpSite(x) FtpSite(x, DefaultNetbuf) +#define ftpMkdir(x) FtpMkdir(x, DefaultNetbuf) +#define ftpChdir(x) FtpChdir(x, DefaultNetbuf) +#define ftpRmdir(x) FtpRmdir(x, DefaultNetbuf) +#define ftpNlst(x, y) FtpNlst(x, y, DefaultNetbuf) +#define ftpDir(x, y) FtpDir(x, y, DefaultNetbuf) +#define ftpGet(x, y, z) FtpGet(x, y, z, DefaultNetbuf) +#define ftpPut(x, y, z) FtpPut(x, y, z, DefaultNetbuf) +#define ftpRename(x, y) FtpRename(x, y, DefaultNetbuf) +#define ftpDelete(x) FtpDelete(x, DefaultNetbuf) +#define ftpQuit() FtpQuit(DefaultNetbuf) +#endif /* (_FTPLIB_NO_COMPAT) */ +/* end v1 compatibility stuff */ + +GLOBALREF int ftplib_debug; +GLOBALREF void FtpInit(void); +GLOBALREF char *FtpLastResponse(netbuf *nControl); +GLOBALREF int FtpConnect(const char *host, netbuf **nControl); +GLOBALREF int FtpOptions(int opt, long val, netbuf *nControl); +GLOBALREF int FtpLogin(const char *user, const char *pass, netbuf *nControl); +GLOBALREF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl, + netbuf **nData); +GLOBALREF int FtpRead(void *buf, int max, netbuf *nData); +GLOBALREF int FtpWrite(void *buf, int len, netbuf *nData); +GLOBALREF int FtpClose(netbuf *nData); +GLOBALREF int FtpSite(const char *cmd, netbuf *nControl); +GLOBALREF int FtpSysType(char *buf, int max, netbuf *nControl); +GLOBALREF int FtpMkdir(const char *path, netbuf *nControl); +GLOBALREF int FtpChdir(const char *path, netbuf *nControl); +GLOBALREF int FtpCDUp(netbuf *nControl); +GLOBALREF int FtpRmdir(const char *path, netbuf *nControl); +GLOBALREF int FtpPwd(char *path, int max, netbuf *nControl); +GLOBALREF int FtpNlst(const char *output, const char *path, netbuf *nControl); +GLOBALREF int FtpDir(const char *output, const char *path, netbuf *nControl); +GLOBALREF int FtpSize(const char *path, int *size, char mode, netbuf *nControl); +GLOBALREF int FtpRestart(int offset, netbuf *nControl); +GLOBALREF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl); +GLOBALREF int FtpGet(const char *output, const char *path, char mode, + netbuf *nControl); +GLOBALREF int FtpPut(const char *input, const char *path, char mode, + netbuf *nControl); +GLOBALREF int FtpRename(const char *src, const char *dst, netbuf *nControl); +GLOBALREF int FtpDelete(const char *fnm, netbuf *nControl); +GLOBALREF void FtpQuit(netbuf *nControl); + +GLOBALREF int HttpConnect(const char *host, unsigned short port, netbuf **nControl); +GLOBALREF int HttpGet(const char *host, const char *output, const char *path, + int *size, netbuf *nControl, unsigned int offset); +GLOBALREF void HttpQuit(netbuf *nControl); + +#ifdef __cplusplus +}; +#endif + +#endif /* __FTPLIB_H */ diff --git a/scripts/gensync b/scripts/gensync new file mode 100755 index 00000000..49a2939a --- /dev/null +++ b/scripts/gensync @@ -0,0 +1,186 @@ +#!/bin/bash +# +# gensync +# +# Copyright (c) 2002-2004 by Judd Vinet <jvinet@zeroflux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# + +myver='2.9.2' + +usage() { + echo "gensync $myver" + echo "usage: $0 <root> <destfile> [package_directory]" + echo + echo "gensync will generate a sync database by reading all PKGBUILD files" + echo "from <root>. gensync builds the database in a temporary directory" + echo "and then compresses it to <destfile>." + echo + echo "gensync will calculate md5sums of packages in the same directory as" + echo "<destfile>, unless an alternate [package_directory] is specified." + echo + echo "note: The <destfile> name is important. It must be of the form" + echo " {treename}.db.tar.gz where {treename} is the name of the custom" + echo " package repository you configured in /etc/pacman.conf. The" + echo " generated database must reside in the same directory as your" + echo " custom packages (also configured in /etc/pacman.conf)" + echo + echo "example: gensync /var/abs/local /home/mypkgs/custom.db.tar.gz" + echo + echo + exit 0 +} + +die() { + echo "gensync: $*" >&2 + rm -rf $gstmpdir + exit 1 +} + +get_md5checksum() +{ + if [ "$pkgdir" != "" ]; then + pkgfile="$pkgdir/$pkgname-$pkgver-$pkgrel.pkg.tar.gz" + else + pkgfile="$destdir/$pkgname-$pkgver-$pkgrel.pkg.tar.gz" + fi + if [ -f $pkgfile ]; then + md5line=`md5sum $pkgfile` + [ ! -z "$md5line" ] && pkgmd5sum=${md5line% *} + echo $pkgmd5sum + fi + return 0 +} + +db_write_entry() +{ + unset pkgname pkgver pkgrel pkgdesc force + unset groups replaces provides depends conflicts + source $1 || return 1 + cd $gstmpdir + mkdir $pkgname-$pkgver-$pkgrel || return 1 + cd $pkgname-$pkgver-$pkgrel + # desc + : >desc + echo "%NAME%" >>desc + echo "$pkgname" >>desc + echo "" >>desc + echo "%VERSION%" >>desc + echo "$pkgver-$pkgrel" >>desc + echo "" >>desc + echo "%DESC%" >>desc + echo "$pkgdesc" >>desc + echo "" >>desc + echo "%CSIZE%" >>desc + echo "$csize" >>desc + echo "" >>desc + if [ ! -z $pkgmd5sum ]; then + echo "%MD5SUM%" >>desc + echo "$pkgmd5sum" >>desc + echo "" >>desc + fi + if [ ${#groups[*]} -gt 0 ]; then + echo "%GROUPS%" >>desc + for it in "${groups[@]}"; do + echo "$it" >>desc + done + echo "" >>desc + fi + if [ ${#replaces[*]} -gt 0 ]; then + echo "%REPLACES%" >>desc + for it in "${replaces[@]}"; do + echo "$it" >>desc + done + echo "" >>desc + fi + if [ "$force" = "y" -o "$force" = "Y" ]; then + echo "%FORCE%" >>desc + echo "" >>desc + fi + # depends + : >depends + if [ ${#depends[*]} -gt 0 ]; then + echo "%DEPENDS%" >>depends + for it in "${depends[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi + if [ ${#conflicts[*]} -gt 0 ]; then + echo "%CONFLICTS%" >>depends + for it in "${conflicts[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi + if [ ${#provides[*]} -gt 0 ]; then + echo "%PROVIDES%" >>depends + for it in "${provides[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi + # preserve the modification time + touch -r $1 desc depends +} + +if [ $# -lt 2 ]; then + usage + exit 0 +fi + +if [ "$1" = "-h" -o "$1" = "--help" ]; then + usage + exit 0 +fi + +d=`dirname $1` +rootdir=`cd $d && pwd`/`basename $1` +d=`dirname $2` +destdir=`cd $d && pwd` +destfile="$destdir/`basename $2`" +pkgdir= +if [ "$3" != "" ]; then + pkgdir=$3 +fi +gstmpdir=$(mktemp -d /tmp/gensync.XXXXXXXXXX) || exit 1 + +[ ! -d $rootdir ] && die "invalid root dir: $rootdir" +echo "gensync: building database entries, generating md5sums..." >&2 +cd `dirname $2` +for file in `find $rootdir/* -name PKGBUILD`; do + source $file || die "errors parsing $file" + if [ "$pkgdir" != "" ]; then + pkgfile="$pkgdir/$pkgname-$pkgver-$pkgrel.pkg.tar.gz" + else + pkgfile="$destdir/$pkgname-$pkgver-$pkgrel.pkg.tar.gz" + fi + [ -f $pkgfile ] || die "missing package file: $pkgfile" + csize=`du -b $pkgfile | cut -f1` + pkgmd5sum=`get_md5checksum $pkgfile` + [ -z $pkgmd5sum ] && die "error generating checksum for $pkgfile" + db_write_entry $file + [ $? -gt 0 ] && die "error writing entry for $file" +done + +echo "gensync: compressing to $destfile..." >&2 +cd $gstmpdir +tar c * | gzip -9 >$destfile +[ $? -gt 0 ] && die "error writing to $destfile" + +rm -rf $gstmpdir +exit 0 diff --git a/scripts/makepkg b/scripts/makepkg new file mode 100755 index 00000000..021a33a2 --- /dev/null +++ b/scripts/makepkg @@ -0,0 +1,701 @@ +#!/bin/bash +# +# makepkg +# +# Copyright (c) 2002-2004 by Judd Vinet <jvinet@zeroflux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# + +myver='2.9.2' +startdir=`pwd` +PKGDEST=$startdir +USE_COLOR="n" + +# source Arch's abs.conf if it's present +[ -f /etc/abs/abs.conf ] && source /etc/abs/abs.conf + +# makepkg configuration +[ -f /etc/makepkg.conf ] && source /etc/makepkg.conf + +INFAKEROOT= +if [ "$1" = "-F" ]; then + INFAKEROOT=1 + shift +fi + +### SUBROUTINES ### + +plain() { + if [ "$USE_COLOR" = "Y" -o "$USE_COLOR" = "y" ]; then + echo -e " \033[1;1m$1\033[1;0m" >&2 + else + echo " $1" >&2 + fi +} +msg() { + if [ "$USE_COLOR" = "Y" -o "$USE_COLOR" = "y" ]; then + echo -e "\033[1;32m==>\033[1;0m \033[1;1m$1\033[1;0m" >&2 + else + echo "==> $1" >&2 + fi +} +warning() { + if [ "$USE_COLOR" = "Y" -o "$USE_COLOR" = "y" ]; then + echo -e "\033[1;33m==> WARNING:\033[1;0m \033[1;1m$1\033[1;0m" >&2 + else + echo "==> WARNING: $1" >&2 + fi +} +error() { + if [ "$USE_COLOR" = "Y" -o "$USE_COLOR" = "y" ]; then + echo -e "\033[1;31m==> ERROR:\033[1;0m \033[1;1m$1\033[1;0m" >&2 + else + echo "==> ERROR: $1" >&2 + fi +} + +strip_url() { + echo $1 | sed 's|^.*://.*/||g' +} + +checkdeps() { + local missdep="" + local deplist="" + + missdep=`pacman -T $*` + ret=$? + if [ "$ret" != "0" ]; then + if [ "$ret" = "127" ]; then + msg "Missing Dependencies:" + msg "" + nl=0 + for dep in $missdep; do + echo -ne "$dep " >&2 + if [ "$nl" = "1" ]; then + nl=0 + echo -ne "\n" >&2 + # add this dep to the list + depname=`echo $dep | sed 's|=.*$||' | sed 's|>.*$||' | sed 's|<.*$||'` + deplist="$deplist $depname" + continue + fi + nl=1 + done + msg "" + else + error "pacman returned a fatal error." + exit 1 + fi + fi + echo $deplist +} + +handledeps() { + local missingdeps=0 + local deplist="$*" + local haveperm=0 + if [ "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then + haveperm=1 + fi + + if [ "$deplist" != "" -a $haveperm -eq 1 ]; then + if [ "$DEP_BIN" = "1" ]; then + # install missing deps from binary packages (using pacman -S) + msg "Installing missing dependencies..." + pacman -D $deplist + if [ "$?" = "127" ]; then + error "Failed to install missing dependencies." + exit 1 + fi + # TODO: check deps again to make sure they were resolved + elif [ "$DEP_SRC" = "1" ]; then + # install missing deps by building them from source. + # we look for each package name in $ABSROOT and build it. + if [ "$ABSROOT" = "" ]; then + error "The ABSROOT environment variable is not defined." + exit 1 + fi + # TODO: handle version comparators (eg, glibc>=2.2.5) + msg "Building missing dependencies..." + for dep in $deplist; do + candidates=`find $ABSROOT -type d -name "$dep"` + if [ "$candidates" = "" ]; then + error "Could not find \"$dep\" under $ABSROOT" + exit 1 + fi + success=0 + for pkgdir in $candidates; do + if [ -f $pkgdir/PKGBUILD ]; then + cd $pkgdir + if [ "$RMDEPS" = "1" ]; then + makepkg -i -c -b -r -w $PKGDEST + else + makepkg -i -c -b -w $PKGDEST + fi + if [ $? -eq 0 ]; then + success=1 + break + fi + fi + done + if [ "$success" = "0" ]; then + error "Failed to build \"$dep\"" + exit 1 + fi + done + # TODO: check deps again to make sure they were resolved + else + missingdeps=1 + fi + elif [ "$deplist" != "" -a $haveperm -eq 0 ]; then + if [ "$DEP_SRC" = "1" -o "$DEP_BIN" = "1" ]; then + warning "Cannot auto-install missing dependencies as a normal user!" + plain "Run makepkg as root to resolve dependencies automatically." + fi + missingdeps=1 + fi + return $missingdeps +} + +usage() { + echo "makepkg version $myver" + echo "usage: $0 [options]" + echo "options:" + echo " -b, --builddeps Build missing dependencies from source" + echo " -c, --clean Clean up work files after build" + echo " -C, --cleancache Clean up source files from the cache" + echo " -d, --nodeps Skip all dependency checks" + echo " -e, --noextract Do not extract source files (use existing src/ dir)" + echo " -f, --force Overwrite existing package" + echo " -g, --genmd5 Generate MD5sums for source files" + echo " -h, --help This help" + echo " -i, --install Install package after successful build" + echo " -j <jobs> Set MAKEFLAGS to \"-j<jobs>\" before building" + echo " -m, --nocolor Disable colorized output messages" + echo " -n, --nostrip Do not strip binaries/libraries" + echo " -o, --nobuild Download and extract files only" + echo " -p <buildscript> Use an alternate build script (instead of PKGBUILD)" + echo " -r, --rmdeps Remove installed dependencies after a successful build" + echo " -s, --syncdeps Install missing dependencies with pacman" + echo " -w <destdir> Write package to <destdir> instead of the working dir" + echo + echo " if -p is not specified, makepkg will look for a PKGBUILD" + echo " file in the current directory." + echo +} + + +# Options +CLEANUP=0 +CLEANCACHE=0 +INSTALL=0 +GENMD5=0 +DEP_BIN=0 +DEP_SRC=0 +NODEPS=0 +FORCE=0 +NOEXTRACT=0 +NOSTRIP=0 +NOBUILD=0 +RMDEPS=0 +BUILDSCRIPT="./PKGBUILD" + +ARGLIST=$@ + +while [ "$#" -ne "0" ]; do + case $1 in + --clean) CLEANUP=1 ;; + --cleancache) CLEANCACHE=1 ;; + --syncdeps) DEP_BIN=1 ;; + --builddeps) DEP_SRC=1 ;; + --nodeps) NODEPS=1 ;; + --noextract) NOEXTRACT=1 ;; + --install) INSTALL=1 ;; + --force) FORCE=1 ;; + --nostrip) NOSTRIP=1 ;; + --nobuild) NOBUILD=1 ;; + --nocolor) USE_COLOR="n" ;; + --genmd5) GENMD5=1 ;; + --rmdeps) RMDEPS=1 ;; + --help) + usage + exit 0 + ;; + --*) + usage + exit 1 + ;; + -*) + while getopts "cCsbdehifgj:mnorp:w:-" opt; do + case $opt in + c) CLEANUP=1 ;; + C) CLEANCACHE=1 ;; + b) DEP_SRC=1 ;; + d) NODEPS=1 ;; + e) NOEXTRACT=1 ;; + f) FORCE=1 ;; + g) GENMD5=1 ;; + h) + usage + exit 0 + ;; + i) INSTALL=1 ;; + j) export MAKEFLAGS="-j$OPTARG" ;; + m) USE_COLOR="n" ;; + n) NOSTRIP=1 ;; + o) NOBUILD=1 ;; + p) BUILDSCRIPT=$OPTARG ;; + r) RMDEPS=1 ;; + s) DEP_BIN=1 ;; + w) PKGDEST=$OPTARG ;; + -) + OPTIND=0 + break + ;; + *) + usage + exit 1 + ;; + esac + done + ;; + *) + true + ;; + esac + shift +done + +# convert a (possibly) relative path to absolute +cd $PKGDEST 2>/dev/null +if [ $? -ne 0 ]; then + error "Package destination directory does not exist or permission denied." + exit 1 +fi +PKGDEST=`pwd` +cd $OLDPWD + +if [ "$CLEANCACHE" = "1" ]; then + if [ "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then + msg "Cleaning up source files from the cache." + rm -rf /var/cache/pacman/src/* + exit 0 + else + error "You must be root to clean the cache." + exit 1 + fi +fi + +unset pkgname pkgver pkgrel pkgdesc url license groups provides md5sums force +unset replaces depends conflicts backup source install build makedepends +umask 0022 + +if [ ! -f $BUILDSCRIPT ]; then + error "$BUILDSCRIPT does not exist." + exit 1 +fi + +source $BUILDSCRIPT + +# check for no-no's +if [ `echo $pkgver | grep '-'` ]; then + error "pkgver is not allowed to contain hyphens." + exit 1 +fi +if [ `echo $pkgrel | grep '-'` ]; then + error "pkgrel is not allowed to contain hyphens." + exit 1 +fi + +if [ -f $PKGDEST/${pkgname}-${pkgver}-${pkgrel}.pkg.tar.gz -a "$FORCE" = "0" -a "$GENMD5" = "0" ]; then + if [ "$INSTALL" = "1" ]; then + warning "a package has already been built, installing existing package." + pacman --upgrade $PKGDEST/${pkgname}-${pkgver}-${pkgrel}.pkg.tar.gz + exit $? + else + error "a package has already been built. (use -f to overwrite)" + exit 1 + fi +fi + +# Enter the fakeroot environment if necessary. This will call the makepkg script again +# as the fake root user. We detect this by passing a sentinel option (-F) to makepkg +if [ "`id -u`" != "0" ]; then + if [ "$USE_FAKEROOT" = "y" -o "$USE_FAKEROOT" = "Y" ]; then + if [ `type -p fakeroot` ]; then + msg "Entering fakeroot environment" + fakeroot -- $0 -F $ARGLIST + exit $? + else + warning "Fakeroot is not installed. Building as an unprivileged user" + plain "will result in non-root ownership of the packaged files." + plain "Install the fakeroot package to correctly build as a non-root" + plain "user." + plain "" + sleep 1 + fi + else + warning "Running makepkg as an unprivileged user will result in non-root" + plain "ownership of the packaged files. Try using the fakeroot" + plain "environment. (USE_FAKEROOT=y in makepkg.conf)" + plain "" + sleep 1 + fi +fi + +msg "Making package: $pkgname (`date`)" + +unset deplist makedeplist +if [ `type -p pacman` -a "$NODEPS" = "0" ]; then + msg "Checking Runtime Dependencies..." + deplist=`checkdeps ${depends[@]}` + handledeps $deplist + if [ $? -gt 0 ]; then + exit 1 + fi + msg "Checking Buildtime Dependencies..." + makedeplist=`checkdeps ${makedepends[@]}` + handledeps $makedeplist + if [ $? -gt 0 ]; then + exit 1 + fi +elif [ "$NODEPS" = "1" ]; then + warning "skipping dependency checks." +else + warning "pacman was not found in PATH. skipping dependency checks." +fi + +cd $startdir + +# retrieve sources +msg "Retrieving Sources..." +mkdir -p src +cd $startdir/src +for netfile in ${source[@]}; do + file=`strip_url $netfile` + if [ -f ../$file ]; then + msg " Found $file in build dir" + cp ../$file . + elif [ -f /var/cache/pacman/src/$file ]; then + msg " Using local copy of $file" + cp /var/cache/pacman/src/$file . + else + # check for a download utility + if [ -z "$FTPAGENT" ]; then + error "FTPAGENT is not configured. Check the /etc/makepkg.conf file." + msg "Aborting..." + exit 1 + fi + ftpclient=`echo $FTPAGENT | awk {'print $1'}` + if [ ! -x $ftpclient ]; then + error "ftpclient `basename $ftpclient` is not installed." + msg "Aborting..." + exit 1 + fi + proto=`echo $netfile | sed 's|://.*||'` + if [ "$proto" != "ftp" -a "$proto" != "http" -a "$proto" != "https" ]; then + error "$netfile was not found in the build directory and is not a proper URL." + msg "Aborting..." + exit 1 + fi + msg " Downloading $file" + $FTPAGENT $netfile 2>&1 + if [ ! -f $file ]; then + error "Failed to download $file" + msg "Aborting..." + exit 1 + fi + if [ "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then + mkdir -p /var/cache/pacman/src && cp $file /var/cache/pacman/src + else + cp $file .. + fi + fi +done + +if [ "$GENMD5" = "0" ]; then + if [ "$NOEXTRACT" = "1" ]; then + warning "Skipping source extraction -- using existing src/ tree" + warning "Skipping source integrity checks -- using existing src/ tree" + else + # MD5 validation + if [ ${#md5sums[@]} -ne ${#source[@]} ]; then + warning "MD5sums are missing or incomplete. Cannot verify source integrity." + #sleep 1 + elif [ `type -p md5sum` ]; then + msg "Validating source files with MD5sums" + errors=0 + idx=0 + for netfile in ${source[@]}; do + file=`strip_url $netfile` + echo -n " $file ... " >&2 + echo "${md5sums[$idx]} $file" | md5sum -c - >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "FAILED" >&2 + errors=1 + else + echo "Passed" >&2 + fi + idx=$(($idx+1)) + done + if [ $errors -gt 0 ]; then + error "One or more files did not pass the validity check!" + exit 1 + fi + else + warning "The md5sum program is missing. Cannot verify source files!" + sleep 1 + fi + # extract sources + msg "Extracting Sources..." + for netfile in ${source[@]}; do + unziphack=0 + file=`strip_url $netfile` + unset cmd + case $file in + *.tar.gz|*.tar.Z|*.tgz) + cmd="tar --use-compress-program=gzip -xf $file" ;; + *.tar.bz2) + cmd="tar --use-compress-program=bzip2 -xf $file" ;; + *.tar) + cmd="tar -xf $file" ;; + *.zip) + unziphack=1 + cmd="unzip -qqo $file" ;; + *.gz) + cmd="gunzip $file" ;; + *.bz2) + cmd="bunzip2 $file" ;; + esac + if [ "$cmd" != "" ]; then + msg " $cmd" + $cmd + if [ $? -ne 0 ]; then + # unzip will return a 1 as a warning, it is not an error + if [ "$unziphack" != "1" -o $? -ne 1 ]; then + error "Failed to extract $file" + msg "Aborting..." + exit 1 + fi + fi + fi + done + fi +else +# generate md5 hashes + if [ ! `type -p md5sum` ]; then + error "Cannot find the md5sum program." + exit 1 + fi + msg "Generating MD5sums for source files" + plain "" + ct=0 + newline=0 + numsrc=${#source[@]} + for netfile in ${source[@]}; do + file=`strip_url $netfile` + sum=`md5sum $file | cut -d' ' -f 1` + if [ $ct -eq 0 ]; then + echo -n "md5sums=(" + else + if [ $newline -eq 0 ]; then + echo -n " " + fi + fi + echo -n "'$sum'" + ct=$(($ct+1)) + if [ $ct -eq $numsrc ]; then + echo ')' + else + if [ $newline -eq 1 ]; then + echo '\' + newline=0 + else + echo -n ' ' + newline=1 + fi + fi + done + plain "" + exit 0 +fi + + +if [ "`id -u`" = "0" ]; then + # chown all source files to root.root + chown -R root.root $startdir/src +fi + +# check for existing pkg directory +if [ -d $startdir/pkg ]; then + msg "Removing existing pkg/ directory..." + rm -rf $startdir/pkg +fi +mkdir -p $startdir/pkg + +if [ "$NOBUILD" = "1" ]; then + msg "Sources are ready." + exit 0 +fi + +# use ccache if it's available +[ -d /usr/lib/ccache/bin ] && export PATH=/usr/lib/ccache/bin:$PATH + +# build +msg "Starting build()..." +build 2>&1 +if [ $? -gt 0 ]; then + error "Build Failed. Aborting..." + exit 2 +fi + +# remove info/doc files +cd $startdir +rm -rf pkg/usr/info pkg/usr/share/info +rm -rf pkg/usr/doc pkg/usr/share/doc + +# move /usr/share/man files to /usr/man +if [ -d pkg/usr/share/man ]; then + mkdir -p pkg/usr/man + cp -a pkg/usr/share/man/* pkg/usr/man/ + rm -rf pkg/usr/share/man +fi + +# remove /usr/share directory if empty +if [ -d pkg/usr/share ]; then + if [ -z "`ls -1 pkg/usr/share`" ]; then + rm -r pkg/usr/share + fi +fi + +# compress man pages +msg "Compressing man pages..." +find $startdir/pkg/{usr{,/local,/share},opt/*}/man -type f 2>/dev/null | while read i ; do + ext="${i##*.}" + fn="${i##*/}" + if [ "$ext" != "gz" -a "$ext" != "bz2" ]; then + # update symlinks to this manpage + find $startdir/pkg/{usr{,/local,/share},opt/*}/man -lname "$fn" 2> /dev/null | while read ln ; do + rm -f "$ln" + ln -sf "${fn}.gz" "${ln}.gz" + done + # compress the original + gzip -9 "$i" + fi +done + +cd $startdir + +# strip binaries +if [ "$NOSTRIP" = "0" ]; then + msg "Stripping debugging symbols from libraries..." + find pkg/{,usr,usr/local,opt/*}/lib -type f -not -name "*.dll" -not -name "*.exe" \ + -exec /usr/bin/strip --strip-debug '{}' \; 2>&1 \ + | grep -v "No such file" | grep -v "format not recognized" + msg "Stripping symbols from binaries..." + find pkg/{,usr,usr/local,opt/*}/{bin,sbin} -type f -not -name "*.dll" -not -name "*.exe" \ + -exec /usr/bin/strip '{}' \; 2>&1 \ + | grep -v "No such file" | grep -v "format not recognized" +fi + +# get some package meta info +builddate=`LC_ALL= ; LANG= ; date -u "+%a %b %e %H:%M:%S %Y"` +if [ "$PACKAGER" != "" ]; then + packager="$PACKAGER" +else + packager="Arch Linux (http://www.archlinux.org)" +fi +size=`du -cb $startdir/pkg | tail -n 1 | awk '{print $1}'` + +# write the .PKGINFO file +msg "Generating .PKGINFO file..." +cd $startdir/pkg +echo "# Generated by makepkg $myver" >.PKGINFO +echo -n "# " >>.PKGINFO +date >>.PKGINFO +echo "pkgname = $pkgname" >>.PKGINFO +echo "pkgver = $pkgver-$pkgrel" >>.PKGINFO +echo "pkgdesc = $pkgdesc" >>.PKGINFO +echo "url = $url" >>.PKGINFO +echo "license = $license" >>.PKGINFO +echo "builddate = $builddate" >>.PKGINFO +echo "packager = $packager" >>.PKGINFO +echo "size = $size" >>.PKGINFO +if [ "$CARCH" != "" ]; then + echo "arch = $CARCH" >>.PKGINFO +fi + +for it in "${replaces[@]}"; do + echo "replaces = $it" >>.PKGINFO +done +for it in "${groups[@]}"; do + echo "group = $it" >>.PKGINFO +done +for it in "${depends[@]}"; do + echo "depend = $it" >>.PKGINFO +done +for it in "${conflicts[@]}"; do + echo "conflict = $it" >>.PKGINFO +done +for it in "${provides[@]}"; do + echo "provides = $it" >>.PKGINFO +done +for it in "${backup[@]}"; do + echo "backup = $it" >>.PKGINFO +done + +# check for an install script +if [ "$install" != "" ]; then + msg "Copying install script..." + cp $startdir/$install $startdir/pkg/.INSTALL +fi + +# build a filelist +msg "Generating .FILELIST file..." +cd $startdir/pkg +tar cvf /dev/null * | sort >.FILELIST + +# tar it up +msg "Compressing package..." +cd $startdir/pkg +if [ -f $startdir/pkg/.INSTALL ]; then + cmd="tar czvf $PKGDEST/$pkgname-$pkgver-$pkgrel.pkg.tar.gz .PKGINFO .FILELIST .INSTALL *" +else + cmd="tar czvf $PKGDEST/$pkgname-$pkgver-$pkgrel.pkg.tar.gz .PKGINFO .FILELIST *" +fi +$cmd | sort >../filelist + +cd $startdir +if [ "$CLEANUP" = "1" ]; then + msg "Cleaning up..." + rm -rf src pkg filelist +fi + +if [ "$RMDEPS" = "1" -a "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then + msg "Removing installed dependencies..." + pacman -R $makedeplist $deplist +fi + +msg "Finished making: $pkgname (`date`)" + +if [ "$INSTALL" = "1" -a "`id -u`" = "0" -a "$INFAKEROOT" != "1" ]; then + msg "Running pacman --upgrade..." + pacman --upgrade $PKGDEST/${pkgname}-${pkgver}-${pkgrel}.pkg.tar.gz + exit $? +fi + +exit 0 diff --git a/scripts/makeworld b/scripts/makeworld new file mode 100755 index 00000000..ef2c94b1 --- /dev/null +++ b/scripts/makeworld @@ -0,0 +1,143 @@ +#!/bin/bash +# +# makeworld +# +# Copyright (c) 2002-2004 by Judd Vinet <jvinet@zeroflux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# + +toplevel=`pwd` +version="2.9.2" + +usage() { + echo "makeworld version $version" + echo "usage: $0 [options] <destdir> <category> [category] ..." + echo "options:" + echo " -b, --builddeps Build missing dependencies from source" + echo " -c, --clean Clean up work files after build" + echo " -d, --nodeps Skip all dependency checks" + echo " -f, --force Overwrite existing packages" + echo " -i, --install Install package after successful build" + echo " -h, --help This help" + echo " -r, --rmdeps Remove installed dependencies after a successful build" + echo " -s, --syncdeps Install missing dependencies with pacman" + echo + echo " where <category> is one or more directory names under the ABS root" + echo " eg: makeworld -c /packages base lib editors" + echo + echo " this should be run from the toplevel directory of ABS (usually /var/abs)" +} + +if [ $# -lt 2 ]; then + usage + exit 1 +fi + +MAKEPKG_OPTS= +for arg in $*; do + case $arg in + --clean) MAKEPKG_OPTS="$MAKEPKG_OPTS -c" ;; + --install) MAKEPKG_OPTS="$MAKEPKG_OPTS -i" ;; + --syncdeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -s" ;; + --builddeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -b" ;; + --nodeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -d" ;; + --force) MAKEPKG_OPTS="$MAKEPKG_OPTS -f" ;; + --rmdeps) MAKEPKG_OPTS="$MAKEPKG_OPTS -r" ;; + --help) + usage + exit 0 + ;; + --*) + usage + exit 1 + ;; + -*) + while getopts "chisbdfr-" opt; do + case $opt in + c) MAKEPKG_OPTS="$MAKEPKG_OPTS -c" ;; + i) MAKEPKG_OPTS="$MAKEPKG_OPTS -i" ;; + s) MAKEPKG_OPTS="$MAKEPKG_OPTS -s" ;; + b) MAKEPKG_OPTS="$MAKEPKG_OPTS -b" ;; + d) MAKEPKG_OPTS="$MAKEPKG_OPTS -d" ;; + f) MAKEPKG_OPTS="$MAKEPKG_OPTS -f" ;; + r) MAKEPKG_OPTS="$MAKEPKG_OPTS -r" ;; + h) + usage + exit 0 + ;; + -) + OPTIND=0 + break + ;; + esac + done + ;; + *) + dest=$arg + shift + break + ;; + esac + shift + if [ "$dest" != "" ]; then + break + fi +done + +if [ "$dest" = "" ]; then + usage + exit 1 +fi + +# convert a (possibly) relative path to absolute +cd $dest +dest=`pwd` +cd - &>/dev/null + +sd=`date +"[%b %d %H:%M]"` + +for category in $*; do + for port in `find $toplevel/$category -maxdepth 1 -mindepth 1 -type d | sort`; do + cd $port + if [ -f PKGBUILD ]; then + . PKGBUILD + buildstatus=0 + if [ ! -f $dest/$pkgname-$pkgver-$pkgrel.pkg.tar.gz ]; then + makepkg $MAKEPKG_OPTS -m -w $dest 2>>$toplevel/makepkg.log + if [ $? -gt 0 ]; then + buildstatus=2 + else + buildstatus=1 + fi + fi + d=`date +"[%b %d %H:%M]"` + echo -n "$d " >>$toplevel/build.log + case $buildstatus in + 0) echo "$pkgname already built -- skipping" >>$toplevel/build.log ;; + 1) echo "$pkgname was built successfully" >>$toplevel/build.log ;; + 2) echo "$pkgname build failed" >>$toplevel/build.log ;; + esac + fi + done +done +ed=`date +"[%b %d %H:%M]"` + +echo "makeworld complete." >>$toplevel/build.log +echo " started: $sd" >>$toplevel/build.log +echo " finished: $ed" >>$toplevel/build.log + +exit 0 diff --git a/scripts/updatesync b/scripts/updatesync new file mode 100755 index 00000000..943af8a5 --- /dev/null +++ b/scripts/updatesync @@ -0,0 +1,231 @@ +#!/bin/bash +# +# updatesync +# +# Copyright (c) 2004 by Jason Chu <jason@archlinux.org> +# Derived from gensync (c) 2002-2004 Judd Vinet <jvinet@zeroflux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +# USA. +# + +myver='2.9.2' + +usage() { + echo "updatesync $myver" + echo "usage: $0 <action> <destfile> <option> [package_directory]" + echo + echo "updatesync will update a sync database by reading a PKGBUILD and" + echo "modifying the destfile. updatesync updates the database in a temporary" + echo "directory and then compresses it to <destfile>." + echo + echo "There are two types of actions:" + echo + echo "upd - Will update a package's entry or create it if it doesn't exist." + echo " It takes the package's PKGBUILD as an option." + echo "del - Will remove a package's entry from the db." + echo " It takes the package's name as an option." + echo + echo "updatesync will calculate md5sums of packages in the same directory as" + echo "<destfile>, unless an alternate [package_directory] is specified." + echo + echo "example: updatesync upd /home/mypkgs/custom.db.tar.gz PKGBUILD" + echo + echo + exit 0 +} + +die() +{ + echo "updatesync: $*" >&2 + rm -rf $ustmpdir + exit 1 +} + +get_md5checksum() +{ + if [ "$pkgdir" != "" ]; then + pkgfile="$pkgdir/$pkgname-$pkgver-$pkgrel.pkg.tar.gz" + else + pkgfile="$destdir/$pkgname-$pkgver-$pkgrel.pkg.tar.gz" + fi + if [ -f $pkgfile ]; then + md5line=`md5sum $pkgfile` + [ ! -z "$md5line" ] && pkgmd5sum=${md5line% *} + echo $pkgmd5sum + fi + return 0 +} + +db_write_entry() +{ + unset pkgname pkgver pkgrel pkgdesc force + unset groups replaces provides depends conflicts + source $1 || return 1 + cd $ustmpdir + mkdir $pkgname-$pkgver-$pkgrel || return 1 + cd $pkgname-$pkgver-$pkgrel + # desc + : >desc + echo "%NAME%" >>desc + echo "$pkgname" >>desc + echo "" >>desc + echo "%VERSION%" >>desc + echo "$pkgver-$pkgrel" >>desc + echo "" >>desc + echo "%DESC%" >>desc + echo "$pkgdesc" >>desc + echo "" >>desc + echo "%CSIZE%" >>desc + echo "$csize" >>desc + echo "" >>desc + if [ ! -z $pkgmd5sum ]; then + echo "%MD5SUM%" >>desc + echo "$pkgmd5sum" >>desc + echo "" >>desc + fi + if [ ${#groups[*]} -gt 0 ]; then + echo "%GROUPS%" >>desc + for it in "${groups[@]}"; do + echo "$it" >>desc + done + echo "" >>desc + fi + if [ ${#replaces[*]} -gt 0 ]; then + echo "%REPLACES%" >>desc + for it in "${replaces[@]}"; do + echo "$it" >>desc + done + echo "" >>desc + fi + if [ "$force" = "y" -o "$force" = "Y" ]; then + echo "%FORCE%" >>desc + echo "" >>desc + fi + # depends + : >depends + if [ ${#depends[*]} -gt 0 ]; then + echo "%DEPENDS%" >>depends + for it in "${depends[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi + if [ ${#conflicts[*]} -gt 0 ]; then + echo "%CONFLICTS%" >>depends + for it in "${conflicts[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi + if [ ${#provides[*]} -gt 0 ]; then + echo "%PROVIDES%" >>depends + for it in "${provides[@]}"; do + echo "$it" >>depends + done + echo "" >>depends + fi +} + +delete_entry() +{ + echo $1 | grep PKGBUILD 2>&1 >/dev/null + if [ $? -eq 0 ]; then + source $1 + else + pkgname=$1 + fi + for i in *; do + if [ "${i%-*-*}" = "$pkgname" ]; then + echo "updatesync: deleting $i" >&2 + rm -rf $i + fi + done +} + +if [ $# -lt 3 ]; then + usage + exit 0 +fi + +if [ "$1" = "-h" -o "$1" = "--help" ]; then + usage + exit 0 +fi + +action=$1 +pkgdb=$2 +option=$3 +curdir="`pwd`" +pkgdir=$curdir +if [ "$4" != "" ]; then + pkgdir=$4 +fi + +if [ "$action" != "upd" -a "$action" != "del" ]; then + usage + exit 1 +fi + +ustmpdir=$(mktemp -d /tmp/updatesync.XXXXXXXXXX) || exit 1 + +cd $ustmpdir +if [ ! -f $pkgdb ]; then + if [ ! -f $curdir/$pkgdb ]; then + echo "updatesync: $pkgdb not found" + exit 1 + fi + pkgdb=$curdir/$pkgdb +fi + +if [ "$action" = "upd" -a ! -f $option ]; then + if [ ! -f $curdir/$option ]; then + echo "updatesync: $option not found" + exit 1 + fi + option=$curdir/$option +fi + +echo "updatesync: uncompressing to $ustmpdir..." >&2 +tar -xzf $pkgdb || die "error uncompressing $pkgdb" +if [ "$action" = "upd" ]; then + # INSERT / UPDATE + delete_entry $option + + source $option || die "errors parsing $option" + if [ "$pkgdir" != "" ]; then + pkgfile="$pkgdir/$pkgname-$pkgver-$pkgrel.pkg.tar.gz" + else + pkgfile="$destdir/$pkgname-$pkgver-$pkgrel.pkg.tar.gz" + fi + [ -f $pkgfile ] || die "missing package file: $pkgfile" + csize=`du -b $pkgfile | cut -f1` + pkgmd5sum=`get_md5checksum $pkgfile` + [ -z $pkgmd5sum ] && die "error generating checksum for $pkgfile" + echo "updatesync: creating entry for $option" >&2 + db_write_entry $option || die "error writing entry for $option" +else + # DELETE + delete_entry $option +fi + +echo "updatesync: compressing to $pkgdb..." >&2 +cd $ustmpdir +tar c * | gzip -9 >$pkgdb || die "error writing to $pkgdb" + +cd $curdir +rm -rf $ustmpdir + +exit 0 diff --git a/src/pacman/Makefile b/src/pacman/Makefile new file mode 100644 index 00000000..3d2874ac --- /dev/null +++ b/src/pacman/Makefile @@ -0,0 +1,39 @@ + +CC=gcc +CFLAGS=-g -Wall -D_GNU_SOURCE -I. -I../.. -I../../lib/libalpm -I../../lib/libftp +LDFLAGS=-L../../lib/libalpm -lalpm -L../../lib/libftp -lftp -ltar -lz +AR=ar rc +RAN=ranlib + +OBJECTS=util.o \ + list.o \ + package.o \ + db.o \ + download.o \ + add.o \ + remove.o \ + upgrade.o \ + query.o \ + sync.o \ + conf.o \ + pacman.o + +all: pacman + +%.o: %.c %.h + $(CC) -c $(CFLAGS) -o $@ $< + +pacman: $(OBJECTS) ../../lib/libalpm/libalpm.a + $(CC) $(OBJECTS) -o $@ $(CFLAGS) $(LDFLAGS) +# $(CC) $(OBJECTS) -o $@.static $(CFLAGS) $(LDFLAGS) + +clean: + rm -f *.o *~ core + rm -f pacman pacman.static convertdb vercmp + +install: pacman vercmp convertdb + $(INSTALL) -D -m0755 pacman $(DESTDIR)$(BINDIR)/pacman + $(INSTALL) -D -m0755 pacman.static $(DESTDIR)$(BINDIR)/pacman.static + $(INSTALL) -D -m0755 vercmp $(DESTDIR)$(BINDIR)/vercmp + $(INSTALL) -D -m0755 convertdb $(DESTDIR)$(BINDIR)/convertdb + diff --git a/src/pacman/add.c b/src/pacman/add.c new file mode 100644 index 00000000..1bf46f36 --- /dev/null +++ b/src/pacman/add.c @@ -0,0 +1,130 @@ +/* + * add.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <alpm.h> +/* pacman */ +#include "list.h" +#include "download.h" +#include "pacman.h" + +extern unsigned char pmo_upgrade; +extern unsigned char pmo_flags; + +int pacman_add(list_t *targets) +{ + PM_LIST *data; + list_t *i; + + if(targets == NULL) { + return(0); + } + + /* Check for URL targets and process them + */ + for(i = targets; i; i = i->next) { + if(strstr(i->data, "://")) { + char *str = fetch_pkgurl(i->data); + if(str == NULL) { + return(1); + } else { + free(i->data); + i->data = str; + } + } + } + + /* Step 1: create a new transaction + */ + if(alpm_trans_init((pmo_upgrade == 0) ? PM_TRANS_TYPE_ADD : PM_TRANS_TYPE_UPGRADE, + pmo_flags, cb_trans) == -1) { + ERR(NL, "%s\n", alpm_strerror(pm_errno)); + return(1); + } + + /* and add targets to it */ + MSG(NL, "loading package data... "); + for(i = targets; i; i = i->next) { + if(alpm_trans_addtarget(i->data) == -1) { + ERR(NL, "failed to add target '%s' (%s)\n", (char *)i->data, alpm_strerror(pm_errno)); + return(1); + } + } + MSG(CL, "done"); + + /* Step 2: "compute" the transaction based on targets and flags + */ + if(alpm_trans_prepare(&data) == -1) { + PM_LIST *i; + + ERR(NL, "failed to prepare transaction (%s)\n", alpm_strerror(pm_errno)); + switch(pm_errno) { + case PM_ERR_UNSATISFIED_DEPS: + for(i = alpm_list_first(data); i; i = alpm_list_next(i)) { + pmdepmissing_t *miss = alpm_list_getdata(i); + + MSG(NL, ":: %s: requires %s", miss->target, miss->depend.name); + switch(miss->depend.mod) { + case PM_DEP_EQ: MSG(CL, "=%s", miss->depend.version); break; + case PM_DEP_GE: MSG(CL, ">=%s", miss->depend.version); break; + case PM_DEP_LE: MSG(CL, "<=%s", miss->depend.version); break; + } + MSG(CL, "\n"); + } + alpm_list_free(data); + break; + case PM_ERR_CONFLICTING_DEPS: + for(i = alpm_list_first(data); i; i = alpm_list_next(i)) { + pmdepmissing_t *miss = alpm_list_getdata(i); + + MSG(NL, ":: %s: conflicts with %s", + miss->target, miss->depend.name, miss->depend.name); + } + alpm_list_free(data); + break; + case PM_ERR_FILE_CONFLICTS: + for(i = alpm_list_first(data); i; i = alpm_list_next(i)) { + MSG(NL, ":: %s\n", (char *)alpm_list_getdata(i)); + } + alpm_list_free(data); + MSG(NL, "\nerrors occurred, no packages were upgraded.\n\n"); + break; + default: + break; + } + alpm_trans_release(); + return(1); + } + + /* Step 3: actually perform the installation + */ + if(alpm_trans_commit() == -1) { + ERR(NL, "failed to commit transaction (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/add.h b/src/pacman/add.h new file mode 100644 index 00000000..5efe6a8a --- /dev/null +++ b/src/pacman/add.h @@ -0,0 +1,28 @@ +/* + * add.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_ADD_H +#define _PM_ADD_H + +int pacman_add(list_t *targets); + +#endif /* _PM_ADD_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/conf.c b/src/pacman/conf.c new file mode 100644 index 00000000..89389422 --- /dev/null +++ b/src/pacman/conf.c @@ -0,0 +1,308 @@ +/* + * conf.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> + +#include <alpm.h> +/* pacman */ +#include "util.h" +#include "list.h" +#include "sync.h" +#include "download.h" +#include "pacman.h" + +#define min(X, Y) ((X) < (Y) ? (X) : (Y)) + +extern list_t *pmo_holdpkg; +extern char *pmo_proxyhost; +extern unsigned short pmo_proxyport; +extern unsigned short pmo_nopassiveftp; + +extern list_t *pmc_syncs; + +int parseconfig(char *file) +{ + FILE *fp = NULL; + char line[PATH_MAX+1]; + char *ptr = NULL; + char *key = NULL; + int linenum = 0; + char section[256] = ""; + sync_t *sync = NULL; + + fp = fopen(file, "r"); + if(fp == NULL) { + perror(file); + return(1); + } + + while(fgets(line, PATH_MAX, fp)) { + linenum++; + strtrim(line); + if(strlen(line) == 0 || line[0] == '#') { + continue; + } + if(line[0] == '[' && line[strlen(line)-1] == ']') { + /* new config section */ + ptr = line; + ptr++; + strncpy(section, ptr, min(255, strlen(ptr)-1)); + section[min(255, strlen(ptr)-1)] = '\0'; + vprint("config: new section '%s'\n", section); + if(!strlen(section)) { + ERR(NL, "config: line %d: bad section name\n", linenum); + return(1); + } + if(!strcmp(section, "local")) { + ERR(NL, "config: line %d: '%s' is reserved and cannot be used as a package tree\n", + linenum, section); + return(1); + } + if(strcmp(section, "options")) { + list_t *i; + int found = 0; + for(i = pmc_syncs; i && !found; i = i->next) { + sync = (sync_t *)i->data; + if(!strcmp(sync->treename, section)) { + found = 1; + } + } + if(!found) { + /* start a new sync record */ + sync = (sync_t *)malloc(sizeof(sync_t)); + if(sync == NULL) { + ERR(NL, "could not allocate %d bytes\n", sizeof(sync_t)); + return(1); + } + sync->treename = strdup(section); + sync->servers = NULL; + pmc_syncs = list_add(pmc_syncs, sync); + } + } + } else { + /* directive */ + ptr = line; + key = strsep(&ptr, "="); + if(key == NULL) { + ERR(NL, "config: line %d: syntax error\n", linenum); + return(1); + } + strtrim(key); + key = strtoupper(key); + if(!strlen(section) && strcmp(key, "INCLUDE")) { + ERR(NL, "config: line %d: all directives must belong to a section\n", linenum); + return(1); + } + if(ptr == NULL) { + if(!strcmp(key, "NOPASSIVEFTP")) { + pmo_nopassiveftp = 1; + vprint("config: nopassiveftp\n"); + } else if(!strcmp(key, "USESYSLOG")) { + if(alpm_set_option(PM_OPT_USESYSLOG, (long)1) == -1) { + ERR(NL, "failed to set option USESYSLOG (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + vprint("config: usesyslog\n"); + } else { + ERR(NL, "config: line %d: syntax error\n", linenum); + return(1); + } + } else { + strtrim(ptr); + if(!strcmp(key, "INCLUDE")) { + char conf[PATH_MAX]; + strncpy(conf, ptr, PATH_MAX); + vprint("config: including %s\n", conf); + parseconfig(conf); + } else if(!strcmp(section, "options")) { + if(!strcmp(key, "NOUPGRADE")) { + char *p = ptr; + char *q; + while((q = strchr(p, ' '))) { + *q = '\0'; + if(alpm_set_option(PM_OPT_NOUPGRADE, (long)p) == -1) { + ERR(NL, "failed to set option NOUPGRADE (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + vprint("config: noupgrade: %s\n", p); + p = q; + p++; + } + if(alpm_set_option(PM_OPT_NOUPGRADE, (long)p) == -1) { + ERR(NL, "failed to set option NOUPGRADE (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + vprint("config: noupgrade: %s\n", p); + } else if(!strcmp(key, "IGNOREPKG")) { + char *p = ptr; + char *q; + while((q = strchr(p, ' '))) { + *q = '\0'; + if(alpm_set_option(PM_OPT_IGNOREPKG, (long)p) == -1) { + ERR(NL, "failed to set option IGNOREPKG (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + vprint("config: ignorepkg: %s\n", p); + p = q; + p++; + } + if(alpm_set_option(PM_OPT_IGNOREPKG, (long)p) == -1) { + ERR(NL, "failed to set option IGNOREPKG (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + vprint("config: ignorepkg: %s\n", p); + } else if(!strcmp(key, "HOLDPKG")) { + char *p = ptr; + char *q; + while((q = strchr(p, ' '))) { + *q = '\0'; + pmo_holdpkg = list_add(pmo_holdpkg, strdup(p)); + vprint("config: holdpkg: %s\n", p); + p = q; + p++; + } + pmo_holdpkg = list_add(pmo_holdpkg, strdup(p)); + vprint("config: holdpkg: %s\n", p); + } else if(!strcmp(key, "DBPATH")) { + /* shave off the leading slash, if there is one */ + if(*ptr == '/') { + ptr++; + } + if(alpm_set_option(PM_OPT_DBPATH, (long)ptr) == -1) { + ERR(NL, "failed to set option DBPATH (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + vprint("config: dbpath: %s\n", ptr); + } else if (!strcmp(key, "LOGFILE")) { + if(alpm_set_option(PM_OPT_LOGFILE, (long)ptr) == -1) { + ERR(NL, "failed to set option LOGFILE (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + vprint("config: log file: %s\n", ptr); + } else if (!strcmp(key, "PROXYSERVER")) { + char *p; + if(pmo_proxyhost) { + FREE(pmo_proxyhost); + } + p = strstr(ptr, "://"); + if(p) { + p += 3; + if(p == NULL || *p == '\0') { + ERR(NL, "config: line %d: bad server location\n", linenum); + return(1); + } + ptr = p; + } + pmo_proxyhost = strndup(ptr, PATH_MAX); + vprint("config: proxyserver: %s\n", pmo_proxyhost); + } else if (!strcmp(key, "PROXYPORT")) { + pmo_proxyport = (unsigned short)atoi(ptr); + vprint("config: proxyport: %u\n", pmo_proxyport); + } else if (!strcmp(key, "INCLUDE")) { + /* ORE + TBD */ + } else { + ERR(NL, "config: line %d: syntax error\n", linenum); + return(1); + } + } else { + if(!strcmp(key, "SERVER")) { + /* parse our special url */ + server_t *server; + char *p; + + if((server = (server_t *)malloc(sizeof(server_t))) == NULL) { + ERR(NL, "could not allocate %d bytes\n", sizeof(server_t)); + return(1); + } + server->server = server->path = NULL; + server->protocol = NULL; + + p = strstr(ptr, "://"); + if(p == NULL) { + ERR(NL, "config: line %d: bad server location\n", linenum); + return(1); + } + *p = '\0'; + p++; p++; p++; + if(p == NULL || *p == '\0') { + ERR(NL, "config: line %d: bad server location\n", linenum); + return(1); + } + server->protocol = strdup(ptr); + if(!strcmp(server->protocol, "ftp") || !strcmp(server->protocol, "http")) { + char *slash; + /* split the url into domain and path */ + slash = strchr(p, '/'); + if(slash == NULL) { + /* no path included, default to / */ + server->path = strdup("/"); + } else { + /* add a trailing slash if we need to */ + if(slash[strlen(slash)-1] == '/') { + server->path = strdup(slash); + } else { + if((server->path = (char *)malloc(strlen(slash)+2)) == NULL) { + ERR(NL, "could not allocate %d bytes\n", sizeof(strlen(slash+2))); + return(1); + } + sprintf(server->path, "%s/", slash); + } + *slash = '\0'; + } + server->server = strdup(p); + } else if(!strcmp(server->protocol, "file")){ + /* add a trailing slash if we need to */ + if(p[strlen(p)-1] == '/') { + server->path = strdup(p); + } else { + server->path = (char *)malloc(strlen(p)+2); + if(server->path == NULL) { + ERR(NL, "could not allocate %d bytes\n", sizeof(strlen(p+2))); + return(1); + } + sprintf(server->path, "%s/", p); + } + } else { + ERR(NL, "config: line %d: protocol %s is not supported\n", linenum, ptr); + return(1); + } + /* add to the list */ + vprint("config: %s: server: %s %s %s\n", section, server->protocol, server->server, server->path); + sync->servers = list_add(sync->servers, server); + } else { + ERR(NL, "config: line %d: syntax error\n", linenum); + return(1); + } + } + line[0] = '\0'; + } + } + } + fclose(fp); + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/conf.h b/src/pacman/conf.h new file mode 100644 index 00000000..809e702e --- /dev/null +++ b/src/pacman/conf.h @@ -0,0 +1,28 @@ +/* + * conf.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_CONF_H +#define _PM_CONF_H + +int parseconfig(char *file); + +#endif /* _PM_CONF_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/db.c b/src/pacman/db.c new file mode 100644 index 00000000..e6e17775 --- /dev/null +++ b/src/pacman/db.c @@ -0,0 +1,98 @@ +/* + * db.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> + +#include <alpm.h> +/* pacman */ +#include "util.h" +#include "list.h" +#include "sync.h" +#include "db.h" + +int db_search(PM_DB *db, char *treename, char *needle) +{ + PM_LIST *lp; + char *targ; + + targ = strdup(needle); + strtoupper(targ); + + for(lp = alpm_db_getpkgcache(db); lp; lp = alpm_list_next(lp)) { + PM_PKG *pkg = alpm_list_getdata(lp); + char *haystack; + char *pkgname, *pkgdesc; + int match = 0; + + pkgname = alpm_pkg_getinfo(pkg, PM_PKG_NAME); + pkgdesc = alpm_pkg_getinfo(pkg, PM_PKG_DESC); + + /* check name */ + haystack = strdup(pkgname); + strtoupper(haystack); + if(strstr(haystack, targ)) { + match = 1; + } + FREE(haystack); + + /* check description */ + if(!match) { + haystack = strdup(pkgdesc); + strtoupper(haystack); + if(strstr(haystack, targ)) { + match = 1; + } + FREE(haystack); + } + + /* check provides */ + if(!match) { + PM_LIST *m; + + for(m = alpm_pkg_getinfo(pkg, PM_PKG_PROVIDES); m; m = alpm_list_next(m)) { + haystack = strdup(alpm_list_getdata(m)); + strtoupper(haystack); + if(strstr(haystack, targ)) { + match = 1; + } + FREE(haystack); + } + } + + if(match) { + printf("%s/%s %s\n ", treename, pkgname, (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION)); + indentprint(pkgdesc, 4); + printf("\n"); + } + } + + FREE(targ); + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/db.h b/src/pacman/db.h new file mode 100644 index 00000000..7b38719f --- /dev/null +++ b/src/pacman/db.h @@ -0,0 +1,28 @@ +/* + * db.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_DB_H +#define _PM_DB_H + +int db_search(PM_DB *db, char *treename, char *needle); + +#endif /* _PM_DB_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/download.c b/src/pacman/download.c new file mode 100644 index 00000000..e5ed2c6a --- /dev/null +++ b/src/pacman/download.c @@ -0,0 +1,467 @@ +/* + * download.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/time.h> +#include <ftplib.h> + +#include <alpm.h> +/* pacman */ +#include "list.h" +#include "download.h" +#include "pacman.h" + +/* progress bar */ +static char sync_fnm[25]; +static int offset; +static struct timeval t0, t; +static float rate; +static int xfered1; +static unsigned char eta_h, eta_m, eta_s; + +/* pacman options */ +extern char *pmo_root; +extern char *pmo_dbpath; +extern char *pmo_proxyhost; +extern char *pmo_xfercommand; + +extern unsigned short pmo_proxyport; +extern unsigned short pmo_nopassiveftp; + +extern int maxcols; + +static int log_progress(netbuf *ctl, int xfered, void *arg) +{ + int fsz = *(int*)arg; + int pct = ((float)(xfered+offset) / fsz) * 100; + int i, cur; + struct timeval t1; + float timediff; + + gettimeofday(&t1, NULL); + if(xfered+offset == fsz) { + t = t0; + } + timediff = t1.tv_sec-t.tv_sec + (float)(t1.tv_usec-t.tv_usec) / 1000000; + + if(xfered+offset == fsz) { + /* average download rate */ + rate = xfered / (timediff * 1024); + /* total download time */ + eta_s = (int)timediff; + eta_h = eta_s / 3600; + eta_s -= eta_h * 3600; + eta_m = eta_s / 60; + eta_s -= eta_m * 60; + } else if(timediff > 1) { + /* we avoid computing the rate & ETA on too small periods of time, so that + results are more significant */ + rate = (xfered-xfered1) / (timediff * 1024); + xfered1 = xfered; + gettimeofday(&t, NULL); + eta_s = (fsz-(xfered+offset)) / (rate * 1024); + eta_h = eta_s / 3600; + eta_s -= eta_h * 3600; + eta_m = eta_s / 60; + eta_s -= eta_m * 60; + } + + printf(" %s [", sync_fnm); + cur = (int)((maxcols-64)*pct/100); + for(i = 0; i < maxcols-64; i++) { + (i < cur) ? printf("#") : printf(" "); + } + if(rate > 1000) { + printf("] %3d%% %6dK %6.0fK/s %02d:%02d:%02d\r", pct, ((xfered+offset) / 1024), rate, eta_h, eta_m, eta_s); + } else { + printf("] %3d%% %6dK %6.1fK/s %02d:%02d:%02d\r", pct, ((xfered+offset) / 1024), rate, eta_h, eta_m, eta_s); + } + fflush(stdout); + return(1); +} + +static int copyfile(char *src, char *dest) +{ + FILE *in, *out; + size_t len; + char buf[4097]; + + in = fopen(src, "r"); + if(in == NULL) { + return(1); + } + out = fopen(dest, "w"); + if(out == NULL) { + return(1); + } + + while((len = fread(buf, 1, 4096, in))) { + fwrite(buf, 1, len, out); + } + + fclose(in); + fclose(out); + return(0); +} + +int downloadfiles(list_t *servers, const char *localpath, list_t *files) +{ + int fsz; + netbuf *control = NULL; + list_t *lp; + int done = 0; + list_t *complete = NULL; + list_t *i; + + if(files == NULL) { + return(0); + } + + for(i = servers; i && !done; i = i->next) { + server_t *server = (server_t*)i->data; + + if(!pmo_xfercommand && strcmp(server->protocol, "file")) { + if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) { + FtpInit(); + vprint("Connecting to %s:21\n", server->server); + if(!FtpConnect(server->server, &control)) { + fprintf(stderr, "error: cannot connect to %s\n", server->server); + continue; + } + if(!FtpLogin("anonymous", "arch@guest", control)) { + fprintf(stderr, "error: anonymous login failed\n"); + FtpQuit(control); + continue; + } + if(!FtpChdir(server->path, control)) { + fprintf(stderr, "error: could not cwd to %s: %s\n", server->path, + FtpLastResponse(control)); + continue; + } + if(!pmo_nopassiveftp) { + if(!FtpOptions(FTPLIB_CONNMODE, FTPLIB_PASSIVE, control)) { + fprintf(stderr, "warning: failed to set passive mode\n"); + } + } else { + vprint("FTP passive mode not set\n"); + } + } else if(pmo_proxyhost) { + char *host; + unsigned port; + host = (pmo_proxyhost) ? pmo_proxyhost : server->server; + port = (pmo_proxyhost) ? pmo_proxyport : 80; + if(strchr(host, ':')) { + vprint("Connecting to %s\n", host); + } else { + vprint("Connecting to %s:%u\n", host, port); + } + if(!HttpConnect(host, port, &control)) { + fprintf(stderr, "error: cannot connect to %s\n", host); + continue; + } + } + + /* set up our progress bar's callback (and idle timeout) */ + if(strcmp(server->protocol, "file") && control) { + FtpOptions(FTPLIB_CALLBACK, (long)log_progress, control); + FtpOptions(FTPLIB_IDLETIME, (long)1000, control); + FtpOptions(FTPLIB_CALLBACKARG, (long)&fsz, control); + FtpOptions(FTPLIB_CALLBACKBYTES, (10*1024), control); + } + } + + /* get each file in the list */ + for(lp = files; lp; lp = lp->next) { + char *fn = (char *)lp->data; + + if(list_is_strin(fn, complete)) { + continue; + } + + if(pmo_xfercommand && strcmp(server->protocol, "file")) { + int ret; + int usepart = 0; + char *ptr1, *ptr2; + char origCmd[PATH_MAX]; + char parsedCmd[PATH_MAX] = ""; + char url[PATH_MAX]; + char cwd[PATH_MAX]; + /* build the full download url */ + snprintf(url, PATH_MAX, "%s://%s%s%s", server->protocol, server->server, + server->path, fn); + /* replace all occurrences of %o with fn.part */ + strncpy(origCmd, pmo_xfercommand, sizeof(origCmd)); + ptr1 = origCmd; + while((ptr2 = strstr(ptr1, "%o"))) { + usepart = 1; + ptr2[0] = '\0'; + strcat(parsedCmd, ptr1); + strcat(parsedCmd, fn); + strcat(parsedCmd, ".part"); + ptr1 = ptr2 + 2; + } + strcat(parsedCmd, ptr1); + /* replace all occurrences of %u with the download URL */ + strncpy(origCmd, parsedCmd, sizeof(origCmd)); + parsedCmd[0] = '\0'; + ptr1 = origCmd; + while((ptr2 = strstr(ptr1, "%u"))) { + ptr2[0] = '\0'; + strcat(parsedCmd, ptr1); + strcat(parsedCmd, url); + ptr1 = ptr2 + 2; + } + strcat(parsedCmd, ptr1); + /* cwd to the download directory */ + getcwd(cwd, PATH_MAX); + if(chdir(localpath)) { + fprintf(stderr, "error: could not chdir to %s\n", localpath); + return(1); + } + /* execute the parsed command via /bin/sh -c */ + vprint("running command: %s\n", parsedCmd); + ret = system(parsedCmd); + if(ret == -1) { + fprintf(stderr, "error running XferCommand: fork failed!\n"); + return(1); + } else if(ret != 0) { + /* download failed */ + vprint("XferCommand command returned non-zero status code (%d)\n", ret); + } else { + /* download was successful */ + complete = list_add(complete, fn); + if(usepart) { + char fnpart[PATH_MAX]; + /* rename "output.part" file to "output" file */ + snprintf(fnpart, PATH_MAX, "%s.part", fn); + rename(fnpart, fn); + } + } + chdir(cwd); + } else { + char output[PATH_MAX]; + int j, filedone = 0; + char *ptr; + struct stat st; + snprintf(output, PATH_MAX, "%s/%s.part", localpath, fn); + strncpy(sync_fnm, fn, 24); + /* drop filename extension */ + ptr = strstr(fn, ".db.tar.gz"); + if(ptr && (ptr-fn) < 24) { + sync_fnm[ptr-fn] = '\0'; + } + ptr = strstr(fn, ".pkg.tar.gz"); + if(ptr && (ptr-fn) < 24) { + sync_fnm[ptr-fn] = '\0'; + } + for(j = strlen(sync_fnm); j < 24; j++) { + sync_fnm[j] = ' '; + } + sync_fnm[24] = '\0'; + offset = 0; + + /* ETA setup */ + gettimeofday(&t0, NULL); + t = t0; + rate = 0; + xfered1 = 0; + eta_h = 0; + eta_m = 0; + eta_s = 0; + + if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) { + if(!FtpSize(fn, &fsz, FTPLIB_IMAGE, control)) { + fprintf(stderr, "warning: failed to get filesize for %s\n", fn); + } + if(!stat(output, &st)) { + offset = (int)st.st_size; + if(!FtpRestart(offset, control)) { + fprintf(stderr, "warning: failed to resume download -- restarting\n"); + /* can't resume: */ + /* unlink the file in order to restart download from scratch */ + unlink(output); + } + } + if(!FtpGet(output, fn, FTPLIB_IMAGE, control)) { + fprintf(stderr, "\nfailed downloading %s from %s: %s\n", + fn, server->server, FtpLastResponse(control)); + /* we leave the partially downloaded file in place so it can be resumed later */ + } else { + filedone = 1; + } + } else if(!strcmp(server->protocol, "http") || (pmo_proxyhost && strcmp(server->protocol, "file"))) { + char src[PATH_MAX]; + char *host; + unsigned port; + if(!strcmp(server->protocol, "http") && !pmo_proxyhost) { + /* HTTP servers hang up after each request (but not proxies), so + * we have to re-connect for each file. + */ + host = (pmo_proxyhost) ? pmo_proxyhost : server->server; + port = (pmo_proxyhost) ? pmo_proxyport : 80; + if(strchr(host, ':')) { + vprint("Connecting to %s\n", host); + } else { + vprint("Connecting to %s:%u\n", host, port); + } + if(!HttpConnect(host, port, &control)) { + fprintf(stderr, "error: cannot connect to %s\n", host); + continue; + } + /* set up our progress bar's callback (and idle timeout) */ + if(strcmp(server->protocol, "file") && control) { + FtpOptions(FTPLIB_CALLBACK, (long)log_progress, control); + FtpOptions(FTPLIB_IDLETIME, (long)1000, control); + FtpOptions(FTPLIB_CALLBACKARG, (long)&fsz, control); + FtpOptions(FTPLIB_CALLBACKBYTES, (10*1024), control); + } + } + + if(!stat(output, &st)) { + offset = (int)st.st_size; + } + if(!pmo_proxyhost) { + snprintf(src, PATH_MAX, "%s%s", server->path, fn); + } else { + snprintf(src, PATH_MAX, "%s://%s%s%s", server->protocol, server->server, server->path, fn); + } + if(!HttpGet(server->server, output, src, &fsz, control, offset)) { + fprintf(stderr, "\nfailed downloading %s from %s: %s\n", + src, server->server, FtpLastResponse(control)); + /* we leave the partially downloaded file in place so it can be resumed later */ + } else { + filedone = 1; + } + } else if(!strcmp(server->protocol, "file")) { + char src[PATH_MAX]; + snprintf(src, PATH_MAX, "%s%s", server->path, fn); + vprint("copying %s to %s/%s\n", src, localpath, fn); + /* local repository, just copy the file */ + if(copyfile(src, output)) { + fprintf(stderr, "failed copying %s\n", src); + } else { + filedone = 1; + } + } + + if(filedone) { + char completefile[PATH_MAX]; + if(!strcmp(server->protocol, "file")) { + char out[56]; + printf(" %s [", sync_fnm); + strncpy(out, server->path, 33); + printf("%s", out); + for(j = strlen(out); j < maxcols-64; j++) { + printf(" "); + } + fputs("] 100% LOCAL ", stdout); + } else { + log_progress(control, fsz-offset, &fsz); + } + complete = list_add(complete, fn); + /* rename "output.part" file to "output" file */ + snprintf(completefile, PATH_MAX, "%s/%s", localpath, fn); + rename(output, completefile); + } + printf("\n"); + fflush(stdout); + } + } + if(!pmo_xfercommand) { + if(!strcmp(server->protocol, "ftp") && !pmo_proxyhost) { + FtpQuit(control); + } else if(!strcmp(server->protocol, "http") || (pmo_proxyhost && strcmp(server->protocol, "file"))) { + HttpQuit(control); + } + } + + if(list_count(complete) == list_count(files)) { + done = 1; + } + } + + return(!done); +} + +char *fetch_pkgurl(char *target) +{ + char spath[PATH_MAX]; + char url[PATH_MAX]; + server_t *server; + list_t *servers = NULL; + list_t *files = NULL; + char *host, *path, *fn; + + /* ORE + do not download the file if it exists in the current dir */ + + strncpy(url, target, PATH_MAX); + host = strstr(url, "://"); + *host = '\0'; + host += 3; + path = strchr(host, '/'); + *path = '\0'; + path++; + fn = strrchr(path, '/'); + if(fn) { + *fn = '\0'; + if(path[0] == '/') { + snprintf(spath, PATH_MAX, "%s/", path); + } else { + snprintf(spath, PATH_MAX, "/%s/", path); + } + fn++; + } else { + fn = path; + strcpy(spath, "/"); + } + + server = (server_t *)malloc(sizeof(server_t)); + if(server == NULL) { + fprintf(stderr, "error: failed to allocate %d bytes\n", sizeof(server_t)); + return(NULL); + } + server->protocol = url; + server->server = host; + server->path = spath; + servers = list_add(servers, server); + + files = list_add(files, fn); + if(downloadfiles(servers, ".", files)) { + fprintf(stderr, "error: failed to download %s\n", target); + return(NULL); + } + + FREELIST(servers); + files->data = NULL; + FREELIST(files); + + /* return the target with the raw filename, no URL */ + return(strndup(fn, PATH_MAX)); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/download.h b/src/pacman/download.h new file mode 100644 index 00000000..22ecf574 --- /dev/null +++ b/src/pacman/download.h @@ -0,0 +1,38 @@ +/* + * download.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_DOWNLOAD_H +#define _PM_DOWNLOAD_H + +#include "list.h" + +/* Servers */ +typedef struct __server_t { + char *protocol; + char *server; + char *path; +} server_t; + +int downloadfiles(list_t *servers, const char *localpath, list_t *files); +char *fetch_pkgurl(char *target); + +#endif /* _PM_DOWNLOAD_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/list.c b/src/pacman/list.c new file mode 100644 index 00000000..84af1784 --- /dev/null +++ b/src/pacman/list.c @@ -0,0 +1,176 @@ +/* + * list.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +/* pacman */ +#include "list.h" + +extern int maxcols; + +static list_t *list_last(list_t *list); + +list_t *list_new() +{ + list_t *list = NULL; + + list = (list_t *)malloc(sizeof(list_t)); + if(list == NULL) { + return(NULL); + } + list->data = NULL; + list->next = NULL; + return(list); +} + +void list_free(list_t *list) +{ + if(list == NULL) { + return; + } + if(list->data != NULL) { + free(list->data); + list->data = NULL; + } + if(list->next != NULL) { + list_free(list->next); + } + free(list); + return; +} + +list_t *list_add(list_t *list, void *data) +{ + list_t *ptr, *lp; + + ptr = list; + if(ptr == NULL) { + ptr = list_new(); + } + + lp = list_last(ptr); + if(lp == ptr && lp->data == NULL) { + /* nada */ + } else { + lp->next = list_new(); + if(lp->next == NULL) { + return(NULL); + } + lp = lp->next; + } + lp->data = data; + return(ptr); +} + +int list_count(list_t *list) +{ + int i; + list_t *lp; + + for(lp = list, i = 0; lp; lp = lp->next, i++); + + return(i); +} + +static list_t *list_last(list_t *list) +{ + list_t *ptr; + + for(ptr = list; ptr && ptr->next; ptr = ptr->next); + return(ptr); +} + +/* Test for existence of a string in a list_t + */ +int list_is_strin(char *needle, list_t *haystack) +{ + list_t *lp; + + for(lp = haystack; lp; lp = lp->next) { + if(lp->data && !strcmp(lp->data, needle)) { + return(1); + } + } + return(0); +} + +/* Display the content of a list_t struct of strings + */ + +void list_display(const char *title, list_t *list) +{ + list_t *lp; + int cols, len; + + len = strlen(title); + printf("%s ", title); + + if(list) { + for(lp = list, cols = len; lp; lp = lp->next) { + int s = strlen((char *)lp->data)+1; + if(s+cols >= maxcols) { + int i; + cols = len; + printf("\n"); + for (i = 0; i < len+1; i++) { + printf(" "); + } + } + printf("%s ", (char *)lp->data); + cols += s; + } + printf("\n"); + } else { + printf("None\n"); + } +} + +void PM_LIST_display(const char *title, PM_LIST *list) +{ + PM_LIST *lp; + int cols, len; + + len = strlen(title); + printf("%s ", title); + + if(list) { + for(lp = list, cols = len; lp; lp = alpm_list_next(lp)) { + int s = strlen(alpm_list_getdata(lp))+1; + if(s+cols >= maxcols) { + int i; + cols = len; + printf("\n"); + for (i = 0; i < len+1; i++) { + printf(" "); + } + } + printf("%s ", (char *)alpm_list_getdata(lp)); + cols += s; + } + printf("\n"); + } else { + printf("None\n"); + } +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/list.h b/src/pacman/list.h new file mode 100644 index 00000000..ca738f9e --- /dev/null +++ b/src/pacman/list.h @@ -0,0 +1,45 @@ +/* + * list.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_LIST_H +#define _PM_LIST_H + +#include <alpm.h> + +/* Chained list struct */ +typedef struct __list_t { + void *data; + struct __list_t *next; +} list_t; + +#define FREELIST(p) do { if(p) { list_free(p); p = NULL; } } while(0) + +list_t *list_new(); +void list_free(list_t* list); +list_t *list_add(list_t* list, void *data); +int list_count(list_t* list); +int list_is_strin(char *needle, list_t *haystack); +void list_display(const char *title, list_t *list); + +void PM_LIST_display(const char *title, PM_LIST *list); + +#endif /* _PM_LIST_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/package.c b/src/pacman/package.c new file mode 100644 index 00000000..aa47d77f --- /dev/null +++ b/src/pacman/package.c @@ -0,0 +1,203 @@ +/* + * package.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <alpm.h> +/* pacman */ +#include "util.h" +#include "list.h" +#include "package.h" + +extern char *pmo_root; + +/* Display the content of an installed package + */ +void dump_pkg_full(PM_PKG *pkg, int level) +{ + char *date; + + if(pkg == NULL) { + return; + } + + printf("Name : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_NAME)); + printf("Version : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION)); + + PM_LIST_display("Groups :", alpm_pkg_getinfo(pkg, PM_PKG_GROUPS)); + + printf("Packager : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_PACKAGER)); + printf("URL : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_URL)); + printf("License : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_LICENSE)); + printf("Architecture : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_ARCH)); + printf("Size : %ld\n", (long int)alpm_pkg_getinfo(pkg, PM_PKG_SIZE)); + + date = alpm_pkg_getinfo(pkg, PM_PKG_BUILDDATE); + printf("Build Date : %s %s\n", date, strlen(date) ? "UTC" : ""); + + date = alpm_pkg_getinfo(pkg, PM_PKG_INSTALLDATE); + printf("Install Date : %s %s\n", date, strlen(date) ? "UTC" : ""); + + printf("Install Script : %s\n", (alpm_pkg_getinfo(pkg, PM_PKG_SCRIPLET) ? "Yes" : "No")); + + printf("Reason: : "); + switch((int)alpm_pkg_getinfo(pkg, PM_PKG_REASON)) { + case PM_PKG_REASON_EXPLICIT: + printf("explicitly installed\n"); + break; + case PM_PKG_REASON_DEPEND: + printf("installed as a dependency for another package\n"); + break; + default: + printf("unknown\n"); + break; + } + + PM_LIST_display("Provides :", alpm_pkg_getinfo(pkg, PM_PKG_PROVIDES)); + PM_LIST_display("Depends On :", alpm_pkg_getinfo(pkg, PM_PKG_DEPENDS)); + PM_LIST_display("Required By :", alpm_pkg_getinfo(pkg, PM_PKG_REQUIREDBY)); + PM_LIST_display("Conflicts With :", alpm_pkg_getinfo(pkg, PM_PKG_CONFLICTS)); + + printf("Description : "); + indentprint(alpm_pkg_getinfo(pkg, PM_PKG_DESC), 17); + printf("\n"); + + /*if(level > 1 && info->backup) { + PM_LIST *i; + fprintf(stdout, "\n"); + for(i = alpm_first_entry(info->backup); i; i = alpm_next_entry(i)) { + struct stat buf; + char path[PATH_MAX]; + char *md5sum; + char *str = strdup(alpm_get_entry(i)); + char *ptr = index(str, '\t'); + if(ptr == NULL) { + free(str); + str = NULL; + continue; + } + *ptr = '\0'; + ptr++; + snprintf(path, PATH_MAX-1, "%s%s", pmo_root, str); + if(!stat(path, &buf)) { + md5sum = alpm_get_md5sum(path); + if(md5sum == NULL) { + fprintf(stderr, "error calculating md5sum for %s\n", path); + continue; + } + if(strcmp(md5sum, ptr)) { + fprintf(stdout, "MODIFIED\t%s\n", path); + } else { + fprintf(stdout, "NOT MODIFIED\t%s\n", path); + } + } else { + fprintf(stdout, "MISSING\t\t%s\n", path); + } + free(str); + str = NULL; + } + }*/ + + printf("\n"); +} + + +/* Display the content of a sync package + */ +void dump_pkg_sync(PM_PKG *pkg, char *treename) +{ + if(pkg == NULL) { + return; + } + + printf("Repository : %s\n", treename); + printf("Name : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_NAME)); + printf("Version : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION)); + + PM_LIST_display("Groups :", alpm_pkg_getinfo(pkg, PM_PKG_GROUPS)); + PM_LIST_display("Provides :", alpm_pkg_getinfo(pkg, PM_PKG_PROVIDES)); + PM_LIST_display("Depends On :", alpm_pkg_getinfo(pkg, PM_PKG_DEPENDS)); + PM_LIST_display("Conflicts With :", alpm_pkg_getinfo(pkg, PM_PKG_CONFLICTS)); + PM_LIST_display("Replaces :", alpm_pkg_getinfo(pkg, PM_PKG_REPLACES)); + + printf("Size (compressed) : %ld\n", (long)alpm_pkg_getinfo(pkg, PM_PKG_SIZE)); + printf("Description : "); + indentprint(alpm_pkg_getinfo(pkg, PM_PKG_DESC), 17); + printf("\nMD5 Sum : %s\n", (char *)alpm_pkg_getinfo(pkg, PM_PKG_MD5SUM)); +} + +void dump_pkg_files(PM_PKG *pkg) +{ + char *pkgname; + PM_LIST *i, *pkgfiles; + + pkgname = alpm_pkg_getinfo(pkg, PM_PKG_NAME); + pkgfiles = alpm_pkg_getinfo(pkg, PM_PKG_FILES); + + for(i = pkgfiles; i; i = alpm_list_next(i)) { + fprintf(stdout, "%s %s\n", (char *)pkgname, (char *)alpm_list_getdata(i)); + } + + fflush(stdout); +} + +int split_pkgname(char *target, char *name, char *version) +{ + char tmp[512]; + char *p, *q; + + if(target == NULL) { + return(-1); + } + + /* trim path name (if any) */ + if((p = strrchr(target, '/')) == NULL) { + p = target; + } else { + p++; + } + strncpy(tmp, p, 512); + /* trim file extension (if any) */ + if((p = strstr(tmp, ".pkg.tar.gz"))) { + *p = 0; + } + + p = tmp + strlen(tmp); + + for(q = --p; *q && *q != '-'; q--); + if(*q != '-' || q == tmp) { + return(-1); + } + for(p = --q; *p && *p != '-'; p--); + if(*p != '-' || p == tmp) { + return(-1); + } + strncpy(version, p+1, 64); + *p = 0; + + strncpy(name, tmp, 256); + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/package.h b/src/pacman/package.h new file mode 100644 index 00000000..5ffdd278 --- /dev/null +++ b/src/pacman/package.h @@ -0,0 +1,35 @@ +/* + * package.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_PACKAGE_H +#define _PM_PACKAGE_H + +void dump_pkg_full(PM_PKG *pkg, int level); +void dump_pkg_sync(PM_PKG *pkg, char *treename); + +void dump_pkg_files(PM_PKG *pkg); + +int split_pkgname(char *target, char *name, char *version); + +#define FREEPKG(p) { alpm_pkg_free(p); p = NULL; } + +#endif /* _PM_PACKAGE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c new file mode 100644 index 00000000..1ecc3ec6 --- /dev/null +++ b/src/pacman/pacman.c @@ -0,0 +1,688 @@ +/* + * pacman.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <getopt.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <mcheck.h> /* debug */ + +#include <alpm.h> +/* pacman */ +#include "list.h" +#include "util.h" +#include "download.h" +#include "conf.h" +#include "package.h" +#include "add.h" +#include "remove.h" +#include "upgrade.h" +#include "query.h" +#include "sync.h" +#include "pacman.h" + +/* command line options */ +char *pmo_root = NULL; +char *pmo_dbpath = NULL; +char *pmo_configfile = NULL; +unsigned short pmo_op = PM_OP_MAIN; +unsigned short pmo_verbose = 0; +unsigned short pmo_version = 0; +unsigned short pmo_help = 0; +unsigned short pmo_upgrade = 0; +unsigned short pmo_nosave = 0; +unsigned short pmo_noconfirm = 0; +unsigned short pmo_d_vertest = 0; +unsigned short pmo_d_resolve = 0; +unsigned short pmo_q_isfile = 0; +unsigned short pmo_q_info = 0; +unsigned short pmo_q_list = 0; +unsigned short pmo_q_orphans = 0; +unsigned short pmo_q_owns = 0; +unsigned short pmo_q_search = 0; +unsigned short pmo_s_upgrade = 0; +unsigned short pmo_s_downloadonly = 0; +unsigned short pmo_s_printuris = 0; +unsigned short pmo_s_sync = 0; +unsigned short pmo_s_search = 0; +unsigned short pmo_s_clean = 0; +unsigned short pmo_group = 0; +unsigned char pmo_flags = 0; +/* configuration file option */ +list_t *pmo_holdpkg = NULL; +char *pmo_proxyhost = NULL; +unsigned short pmo_proxyport = 0; +char *pmo_xfercommand = NULL; +unsigned short pmo_nopassiveftp = 0; + +PM_DB *db_local; +/* list of (sync_t *) structs for sync locations */ +list_t *pmc_syncs = NULL; +/* list of targets specified on command line */ +list_t *pm_targets = NULL; + +int maxcols = 80; + +int neednl = 0; /* for cleaner message output */ + +int main(int argc, char *argv[]) +{ + int ret = 0; + char *cenv = NULL; + + /* debug */ + mtrace(); + + cenv = getenv("COLUMNS"); + if(cenv != NULL) { + maxcols = atoi(cenv); + } + + if(argc < 2) { + usage(PM_OP_MAIN, basename(argv[0])); + return(0); + } + + /* set signal handlers */ + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + + /* parse the command line */ + ret = parseargs(argc, argv); + if(ret != 0) { + exit(ret); + } + + if(pmo_root == NULL) { + pmo_root = strdup("/"); + } + + /* initialize pm library */ + if(alpm_initialize(pmo_root) == -1) { + ERR(NL, "failed to initilize alpm library (%s)\n", alpm_strerror(pm_errno)); + cleanup(1); + } + + /* add a trailing '/' if there isn't one */ + if(pmo_root[strlen(pmo_root)-1] != '/') { + char *ptr; + MALLOC(ptr, strlen(pmo_root)+2); + strcpy(ptr, pmo_root); + strcat(ptr, "/"); + FREE(pmo_root); + pmo_root = ptr; + } + + if(pmo_configfile == NULL) { + pmo_configfile = strdup(PACCONF); + } + if(parseconfig(pmo_configfile) == -1) { + cleanup(1); + } + if(pmo_dbpath == NULL) { + pmo_dbpath = strdup("var/lib/pacman"); + } + + /* set library parameters */ + if(pmo_verbose == 1) { + if(alpm_set_option(PM_OPT_LOGMASK, (long)0xFF) == -1) { + ERR(NL, "failed to set option LOGMASK (%s)\n", alpm_strerror(pm_errno)); + cleanup(1); + } + if(alpm_set_option(PM_OPT_LOGCB, (long)cb_log) == -1) { + ERR(NL, "failed to set option LOGCB (%s)\n", alpm_strerror(pm_errno)); + cleanup(1); + } + } + if(alpm_set_option(PM_OPT_DBPATH, (long)pmo_dbpath) == -1) { + ERR(NL, "failed to set option DBPATH (%s)\n", alpm_strerror(pm_errno)); + cleanup(1); + } + + if(pmo_verbose > 1) { + printf("Root : %s\n", pmo_root); + printf("DBPath: %s\n", pmo_dbpath); + list_display("Targets:", pm_targets); + } + + /* Opening local database */ + if(alpm_db_register("local", &db_local) == -1) { + ERR(NL, "could not register 'local' database (%s)\n", alpm_strerror(pm_errno)); + cleanup(1); + } + + /* start the requested operation */ + switch(pmo_op) { + case PM_OP_ADD: ret = pacman_add(pm_targets); break; + case PM_OP_REMOVE: ret = pacman_remove(pm_targets); break; + case PM_OP_UPGRADE: ret = pacman_upgrade(pm_targets); break; + case PM_OP_QUERY: ret = pacman_query(pm_targets); break; + case PM_OP_SYNC: ret = pacman_sync(pm_targets); break; + case PM_OP_DEPTEST: ret = pacman_deptest(pm_targets); break; + case PM_OP_MAIN: ret = 0; break; + default: + ERR(NL, "no operation specified (use -h for help)\n\n"); + ret = 1; + } + + cleanup(ret); + /* not reached */ + return(0); +} + +void cleanup(int signum) +{ + list_t *lp; + + /* free pm library resources */ + if(alpm_release() == -1) { + ERR(NL, "%s\n", alpm_strerror(pm_errno)); + } + + /* free memory */ + for(lp = pmc_syncs; lp; lp = lp->next) { + sync_t *sync = lp->data; + list_t *i; + for(i = sync->servers; i; i = i->next) { + server_t *server = i->data; + FREE(server->protocol); + FREE(server->server); + FREE(server->path); + } + FREELIST(sync->servers); + FREE(sync->treename); + } + FREELIST(pmc_syncs); + FREE(pmo_root); + FREE(pmo_dbpath); + FREE(pmo_configfile); + FREE(pmo_proxyhost); + FREE(pmo_xfercommand); + + FREELIST(pm_targets); + + /* debug */ + muntrace(); + + fflush(stdout); + + exit(signum); +} + +/* Callback to handle notifications from the library + */ +void cb_log(unsigned short level, char *msg) +{ + char str[8] = ""; + + switch(level) { + case PM_LOG_DEBUG: + sprintf(str, "DEBUG"); + break; + case PM_LOG_ERROR: + sprintf(str, "ERROR"); + break; + case PM_LOG_WARNING: + sprintf(str, "WARNING"); + break; + case PM_LOG_FLOW1: + sprintf(str, "FLOW1"); + break; + case PM_LOG_FLOW2: + sprintf(str, "FLOW2"); + break; + case PM_LOG_FUNCTION: + sprintf(str, "FUNCTION"); + break; + default: + sprintf(str, "???"); + break; + } + + if(strlen(str) > 0) { + MSG(NL, "%s: %s\n", str, msg); + } +} + +/* Callback to handle transaction events + */ +void cb_trans(unsigned short event, void *data1, void *data2) +{ + char str[256] = ""; + + switch(event) { + case PM_TRANS_CB_DEPS_START: + MSG(NL, "checking dependencies... "); + break; + case PM_TRANS_CB_CONFLICTS_START: + MSG(NL, "checking for file conflicts... "); + break; + case PM_TRANS_CB_DEPS_DONE: + case PM_TRANS_CB_CONFLICTS_DONE: + MSG(CL, "done.\n"); + break; + case PM_TRANS_CB_ADD_START: + MSG(NL, "installing %s... ", (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME)); + break; + case PM_TRANS_CB_ADD_DONE: + MSG(CL, "done.\n"); + snprintf(str, 256, "installed %s (%s)", + (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME), + (char *)alpm_pkg_getinfo(data1, PM_PKG_VERSION)); + alpm_logaction(str); + break; + case PM_TRANS_CB_REMOVE_START: + MSG(NL, "removing %s... ", (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME)); + break; + case PM_TRANS_CB_REMOVE_DONE: + MSG(CL, "done.\n"); + snprintf(str, 256, "removed %s (%s)", + (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME), + (char *)alpm_pkg_getinfo(data1, PM_PKG_VERSION)); + alpm_logaction(str); + break; + case PM_TRANS_CB_UPGRADE_START: + MSG(NL, "upgrading %s... ", (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME)); + break; + case PM_TRANS_CB_UPGRADE_DONE: + MSG(CL, "done.\n"); + snprintf(str, 256, "upgraded %s (%s -> %s)", + (char *)alpm_pkg_getinfo(data1, PM_PKG_NAME), + (char *)alpm_pkg_getinfo(data1, PM_PKG_VERSION), + (char *)alpm_pkg_getinfo(data2, PM_PKG_VERSION)); + alpm_logaction(str); + break; + } +} + +int pacman_deptest(list_t *targets) +{ + PM_LIST *lp, *data; + PM_PKG *dummy; + + if(pmo_d_vertest) { + /* ORE + if(targets && targets->data && targets->next && targets->next->data) { + int ret = rpmvercmp(targets->data, targets->next->data); + printf("%d\n", ret); + return(ret); + }*/ + return(0); + } + + /* we create a transaction to hold a dummy package to be able to use + * pm_trans_prepare() */ + /* ORE + trans = alpm_trans_new(PM_TRANS_ADD, 0); + if(trans == NULL) { + FREEPKG(dummy); + ERR(NL, "error: can't allocate %d bytes\n", sizeof(pm_pkginfo_t)); + exit(1); + } + + dummy = (pm_pkginfo_t *)malloc(sizeof(pm_pkginfo_t)); + if(dummy == NULL) { + ERR(NL, "error: can't allocate %d bytes\n", sizeof(pm_pkginfo_t)); + exit(1); + } + sprintf(dummy->name, "_dummy_"); + sprintf(dummy->version, "1.0-1"); + + for(i = targets; i; i = i->next) { + if(i->data == NULL) continue; + dummy->depends = list_add(dummy->depends, strdup(i->data))); + } + + trans->targets = list_add(trans->targets, strdup(dummy->name));*/ + + if(alpm_trans_prepare(&data) == -1) { + int ret = 126; + list_t *synctargs = NULL; + switch(pm_errno) { + case PM_ERR_UNSATISFIED_DEPS: + for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) { + pmdepmissing_t *miss = alpm_list_getdata(lp); + if(!pmo_d_resolve) { + MSG(NL, "requires: %s", miss->depend.name); + switch(miss->depend.mod) { + case PM_DEP_EQ: MSG(CL, "=%s", miss->depend.version); break; + case PM_DEP_GE: MSG(CL, ">=%s", miss->depend.version); break; + case PM_DEP_LE: MSG(CL, "<=%s", miss->depend.version); break; + } + MSG(CL, "\n"); + } + synctargs = list_add(synctargs, strdup(miss->depend.name)); + } + alpm_list_free(data); + break; + case PM_ERR_CONFLICTING_DEPS: + /* we can't auto-resolve conflicts */ + for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) { + pmdepmissing_t *miss = alpm_list_getdata(lp); + MSG(NL, "conflict: %s", miss->depend.name); + } + ret = 127; + alpm_list_free(data); + break; + default: + ret = 127; + break; + } + /* attempt to resolve missing dependencies */ + /* TODO: handle version comparators (eg, glibc>=2.2.5) */ + if(ret == 126 && synctargs != NULL) { + if(!pmo_d_resolve || pacman_sync(synctargs) != 0) { + /* error (or -D not used) */ + ret = 127; + } + } + FREELIST(synctargs); + FREEPKG(dummy); + return(ret); + } + + FREEPKG(dummy); + + return(0); +} + +/* Parse command-line arguments for each operation + * argc: argc + * argv: argv + * + * Returns: 0 on success, 1 on error + */ +int parseargs(int argc, char **argv) +{ + int opt; + int option_index = 0; + static struct option opts[] = + { + {"add", no_argument, 0, 'A'}, + {"remove", no_argument, 0, 'R'}, + {"upgrade", no_argument, 0, 'U'}, + {"freshen", no_argument, 0, 'F'}, + {"query", no_argument, 0, 'Q'}, + {"sync", no_argument, 0, 'S'}, + {"deptest", no_argument, 0, 'T'}, + {"vertest", no_argument, 0, 'Y'}, + {"resolve", no_argument, 0, 'D'}, + {"root", required_argument, 0, 'r'}, + {"dbpath", required_argument, 0, 'b'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"search", no_argument, 0, 's'}, + {"clean", no_argument, 0, 'c'}, + {"force", no_argument, 0, 'f'}, + {"nodeps", no_argument, 0, 'd'}, + {"orphans", no_argument, 0, 'e'}, + {"nosave", no_argument, 0, 'n'}, + {"owns", no_argument, 0, 'o'}, + {"list", no_argument, 0, 'l'}, + {"file", no_argument, 0, 'p'}, + {"info", no_argument, 0, 'i'}, + {"sysupgrade", no_argument, 0, 'u'}, + {"downloadonly", no_argument, 0, 'w'}, + {"refresh", no_argument, 0, 'y'}, + {"dbonly", no_argument, 0, 'k'}, + {"cascade", no_argument, 0, 'c'}, + {"recursive", no_argument, 0, 's'}, + {"groups", no_argument, 0, 'g'}, + {"noconfirm", no_argument, 0, 1000}, + {"config", required_argument, 0, 1001}, + {0, 0, 0, 0} + }; + char root[256]; + + while((opt = getopt_long(argc, argv, "ARUFQSTDYr:b:vkhscVfnoldepiuwyg", opts, &option_index))) { + if(opt < 0) { + break; + } + switch(opt) { + case 0: break; + case 1000: pmo_noconfirm = 1; break; + case 1001: pmo_configfile = strndup(optarg, PATH_MAX); break; + case 'A': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_ADD); break; + case 'R': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break; + case 'U': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break; + case 'F': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); pmo_flags |= PM_TRANS_FLAG_FRESHEN; break; + case 'Q': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break; + case 'S': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break; + case 'T': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break; + case 'Y': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); pmo_d_vertest = 1; break; + case 'D': pmo_op = (pmo_op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); pmo_d_resolve = 1; break; + case 'h': pmo_help = 1; break; + case 'V': pmo_version = 1; break; + case 'b': pmo_dbpath = strdup(optarg); break; + case 'c': pmo_s_clean++; pmo_flags |= PM_TRANS_FLAG_CASCADE; break; + case 'd': pmo_flags |= PM_TRANS_FLAG_NODEPS; break; + case 'e': pmo_q_orphans = 1; break; + case 'f': pmo_flags |= PM_TRANS_FLAG_FORCE; break; + case 'g': pmo_group = 1; break; + case 'i': pmo_q_info++; break; + case 'k': pmo_flags |= PM_TRANS_FLAG_DBONLY; break; + case 'l': pmo_q_list = 1; break; + case 'n': pmo_flags |= PM_TRANS_FLAG_NOSAVE; break; + case 'p': pmo_q_isfile = 1; break; + case 'o': pmo_q_owns = 1; break; + case 'r': + if(realpath(optarg, root) == NULL) { + perror("bad root path"); + return(1); + } + pmo_root = strdup(root); + break; + case 's': pmo_s_search = 1; pmo_q_search = 1; pmo_flags |= PM_TRANS_FLAG_RECURSE; break; + case 'u': pmo_s_upgrade = 1; break; + case 'v': pmo_verbose++; break; + case 'w': pmo_s_downloadonly = 1; break; + case 'y': pmo_s_sync = 1; break; + case '?': return(1); + default: return(1); + } + } + + if(pmo_op == 0) { + ERR(NL, "only one operation may be used at a time\n\n"); + return(1); + } + + if(pmo_help) { + usage(pmo_op, basename(argv[0])); + return(2); + } + if(pmo_version) { + version(); + return(2); + } + + while(optind < argc) { + /* add the target to our target array */ + char *s = strdup(argv[optind]); + pm_targets = list_add(pm_targets, s); + optind++; + } + + return(0); +} + +/* Display usage/syntax for the specified operation. + * op: the operation code requested + * myname: basename(argv[0]) + */ +void usage(int op, char *myname) +{ + if(op == PM_OP_MAIN) { + printf("usage: %s {-h --help}\n", myname); + printf(" %s {-V --version}\n", myname); + printf(" %s {-A --add} [options] <file>\n", myname); + printf(" %s {-R --remove} [options] <package>\n", myname); + printf(" %s {-U --upgrade} [options] <file>\n", myname); + printf(" %s {-F --freshen} [options] <file>\n", myname); + printf(" %s {-Q --query} [options] [package]\n", myname); + printf(" %s {-S --sync} [options] [package]\n", myname); + printf("\nuse '%s --help' with other options for more syntax\n\n", myname); + } else { + if(op == PM_OP_ADD) { + printf("usage: %s {-A --add} [options] <file>\n", myname); + printf("options:\n"); + printf(" -d, --nodeps skip dependency checks\n"); + printf(" -f, --force force install, overwrite conflicting files\n"); + } else if(op == PM_OP_REMOVE) { + printf("usage: %s {-R --remove} [options] <package>\n", myname); + printf("options:\n"); + printf(" -c, --cascade remove packages and all packages that depend on them\n"); + printf(" -d, --nodeps skip dependency checks\n"); + printf(" -k, --dbonly only remove database entry, do not remove files\n"); + printf(" -n, --nosave remove configuration files as well\n"); + printf(" -s, --recursive remove dependencies also (that won't break packages)\n"); + } else if(op == PM_OP_UPGRADE) { + if(pmo_flags & PM_TRANS_FLAG_FRESHEN) { + printf("usage: %s {-F --freshen} [options] <file>\n", myname); + } else { + printf("usage: %s {-U --upgrade} [options] <file>\n", myname); + } + printf("options:\n"); + printf(" -d, --nodeps skip dependency checks\n"); + printf(" -f, --force force install, overwrite conflicting files\n"); + } else if(op == PM_OP_QUERY) { + printf("usage: %s {-Q --query} [options] [package]\n", myname); + printf("options:\n"); + printf(" -e, --orphans list all packages that were explicitly installed\n"); + printf(" and are not required by any other packages\n"); + printf(" -g, --groups view all members of a package group\n"); + printf(" -i, --info view package information\n"); + printf(" -l, --list list the contents of the queried package\n"); + printf(" -o, --owns <file> query the package that owns <file>\n"); + printf(" -p, --file pacman will query the package file [package] instead of\n"); + printf(" looking in the database\n"); + printf(" -s, --search search locally-installed packages for matching strings\n"); + } else if(op == PM_OP_SYNC) { + printf("usage: %s {-S --sync} [options] [package]\n", myname); + printf("options:\n"); + printf(" -c, --clean remove old packages from cache directory (use -cc for all)\n"); + printf(" -d, --nodeps skip dependency checks\n"); + printf(" -f, --force force install, overwrite conflicting files\n"); + printf(" -g, --groups view all members of a package group\n"); + printf(" -s, --search search remote repositories for matching strings\n"); + printf(" -u, --sysupgrade upgrade all packages that are out of date\n"); + printf(" -w, --downloadonly download packages but do not install/upgrade anything\n"); + printf(" -y, --refresh download fresh package databases from the server\n"); + } + printf(" --config <path> set an alternate configuration file\n"); + printf(" --noconfirm do not ask for anything confirmation\n"); + printf(" -v, --verbose be verbose\n"); + printf(" -r, --root <path> set an alternate installation root\n"); + printf(" -b, --dbpath <path> set an alternate database location\n"); + } +} + +/* Version + */ +void version() +{ + printf("\n"); + printf(" .--. Pacman v%s\n", ALPM_VERSION); + printf("/ _.-' .-. .-. .-. Copyright (C) 2002-2003 Judd Vinet <jvinet@zeroflux.org>\n"); + printf("\\ '-. '-' '-' '-' \n"); + printf(" '--' This program may be freely redistributed under\n"); + printf(" the terms of the GNU General Public License\n\n"); +} + +/* + * Misc functions + */ + +/* Condense a list of strings into one long (space-delimited) string + */ +char *buildstring(list_t *strlist) +{ + char *str; + int size = 1; + list_t *lp; + + for(lp = strlist; lp; lp = lp->next) { + size += strlen(lp->data) + 1; + } + str = (char *)malloc(size); + if(str == NULL) { + ERR(NL, "failed to allocated %d bytes\n", size); + } + str[0] = '\0'; + for(lp = strlist; lp; lp = lp->next) { + strcat(str, lp->data); + strcat(str, " "); + } + /* shave off the last space */ + str[strlen(str)-1] = '\0'; + + return(str); +} + +/* Check verbosity option and, if set, print the + * string to stdout + */ +void vprint(char *fmt, ...) +{ + va_list args; + + if(pmo_verbose > 1) { + if(neednl == 1) { + fprintf(stdout, "\n"); + neednl = 0; + } + va_start(args, fmt); + pm_fprintf(stdout, NL, fmt, args); + va_end(args); + } +} + +void pm_fprintf(FILE *file, unsigned short line, char *fmt, ...) +{ + va_list args; + + char str[256]; + + if(neednl == 1 && line == NL) { + fprintf(stdout, "\n"); + neednl = 0; + } + + va_start(args, fmt); + vsnprintf(str, 256, fmt, args); + va_end(args); + + fprintf(file, str); + fflush(file); + + neednl = (str[strlen(str)-1] == 10) ? 0 : 1; +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/pacman.h b/src/pacman/pacman.h new file mode 100644 index 00000000..e4e99b50 --- /dev/null +++ b/src/pacman/pacman.h @@ -0,0 +1,75 @@ +/* + * pacman.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_PACMAN_H +#define _PM_PACMAN_H + +#ifndef PACCONF +#define PACCONF "/etc/pacman.conf" +#endif +#ifndef CACHEDIR +#define CACHEDIR "var/cache/pacman/pkg" +#endif + +/* Operations */ +#define PM_OP_MAIN 1 +#define PM_OP_ADD 2 +#define PM_OP_REMOVE 3 +#define PM_OP_UPGRADE 4 +#define PM_OP_QUERY 5 +#define PM_OP_SYNC 6 +#define PM_OP_DEPTEST 7 + +#define MSG(line, fmt, args...) pm_fprintf(stdout, line, fmt, ##args) +#define ERR(line, fmt, args...) do { \ + pm_fprintf(stderr, line, "error: "); \ + pm_fprintf(stderr, CL, fmt, ##args); \ +} while(0) +#define DBG(line, fmt, args...) do { \ + char str[256]; \ + snprintf(str, 256, fmt, ##args); \ + cb_log(PM_LOG_DEBUG, str); \ +} while(0) + +enum { + NL, /* new line */ + CL /* current line */ +}; +/* callback to handle messages/notifications from pacman library */ +void cb_log(unsigned short level, char *msg); +/* callback to handle messages/notifications from pacman transactions */ +void cb_trans(unsigned short event, void *data1, void *data2); + +void cleanup(int signum); + +int pacman_deptest(list_t *targets); + +int parseargs(int argc, char **argv); + +void usage(int op, char *myname); +void version(); + +char *buildstring(list_t *strlist); +void vprint(char *fmt, ...); +void pm_fprintf(FILE *file, unsigned short line, char *fmt, ...); + +#endif /* _PM_PACMAN_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/query.c b/src/pacman/query.c new file mode 100644 index 00000000..5eec0004 --- /dev/null +++ b/src/pacman/query.c @@ -0,0 +1,247 @@ +/* + * query.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <string.h> +#include <sys/stat.h> + +#include <alpm.h> +/* pacman */ +#include "list.h" +#include "package.h" +#include "db.h" +#include "query.h" +#include "pacman.h" + +extern char *pmo_root; +extern unsigned short pmo_q_isfile; +extern unsigned short pmo_q_info; +extern unsigned short pmo_q_list; +extern unsigned short pmo_q_orphans; +extern unsigned short pmo_q_owns; +extern unsigned short pmo_q_search; +extern unsigned short pmo_group; +extern PM_DB *db_local; + +static int query_fileowner(PM_DB *db, char *filename) +{ + struct stat buf; + int gotcha = 0; + char rpath[PATH_MAX]; + PM_LIST *lp; + + if(db == NULL) { + return(0); + } + if(filename == NULL || strlen(filename) == 0) { + fprintf(stderr, "error: no file was specified for --owns\n"); + return(1); + } + + if(stat(filename, &buf) == -1 || S_ISDIR(buf.st_mode) || realpath(filename, rpath) == NULL) { + fprintf(stderr, "error: %s is not a file.\n", filename); + return(1); + } + + for(lp = alpm_db_getpkgcache(db); lp && !gotcha; lp = alpm_list_next(lp)) { + PM_PKG *info; + char *pkgname; + PM_LIST *i; + + pkgname = alpm_pkg_getinfo(alpm_list_getdata(lp), PM_PKG_NAME); + + info = alpm_db_readpkg(db, pkgname); + if(info == NULL) { + fprintf(stderr, "error: package %s not found\n", pkgname); + return(1); + } + + for(i = alpm_pkg_getinfo(info, PM_PKG_FILES); i && !gotcha; i = alpm_list_next(i)) { + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%s%s", pmo_root, (char *)alpm_list_getdata(i)); + if(!strcmp(path, rpath)) { + printf("%s is owned by %s %s\n", filename, pkgname, + (char *)alpm_pkg_getinfo(info, PM_PKG_VERSION)); + gotcha = 1; + break; + } + } + } + if(!gotcha) { + fprintf(stderr, "No package owns %s\n", filename); + return(1); + } + + return(0); +} + +int pacman_query(list_t *targets) +{ + PM_PKG *info = NULL; + list_t *targ; + char *package = NULL; + int done = 0; + + if(pmo_q_search) { + for(targ = targets; targ; targ = targ->next) { + db_search(db_local, "local", targ->data); + } + return(0); + } + + for(targ = targets; !done; targ = (targ ? targ->next : NULL)) { + if(targets == NULL) { + done = 1; + } else { + if(targ->next == NULL) { + done = 1; + } + package = targ->data; + } + + /* looking for groups */ + if(pmo_group) { + PM_LIST *lp; + if(targets == NULL) { + for(lp = alpm_db_getgrpcache(db_local); lp; lp = alpm_list_next(lp)) { + PM_GRP *grp = alpm_list_getdata(lp); + PM_LIST *i, *pkgnames; + char *grpname; + + grpname = alpm_grp_getinfo(grp, PM_GRP_NAME); + pkgnames = alpm_grp_getinfo(grp, PM_GRP_PKGNAMES); + + for(i = pkgnames; i; i = alpm_list_next(i)) { + MSG(NL, "%s %s\n", grpname, (char *)alpm_list_getdata(i)); + } + } + } else { + PM_GRP *grp = alpm_db_readgrp(db_local, package); + if(grp) { + PM_LIST *i, *pkgnames = alpm_grp_getinfo(grp, PM_GRP_PKGNAMES); + for(i = pkgnames; i; i = alpm_list_next(i)) { + MSG(NL, "%s %s\n", package, (char *)alpm_list_getdata(i)); + } + } else { + ERR(NL, "group \"%s\" was not found\n", package); + return(2); + } + } + continue; + } + + /* output info for a .tar.gz package */ + if(pmo_q_isfile) { + if(package == NULL) { + ERR(NL, "no package file was specified for --file\n"); + return(1); + } + if(alpm_pkg_load(package, &info) == -1) { + ERR(NL, "failed to load package '%s' (%s)\n", package, alpm_strerror(pm_errno)); + return(1); + } + if(pmo_q_info) { + dump_pkg_full(info, 0); + MSG(NL, "\n"); + } + if(pmo_q_list) { + dump_pkg_files(info); + } + if(!pmo_q_info && !pmo_q_list) { + MSG(NL, "%s %s\n", (char *)alpm_pkg_getinfo(info, PM_PKG_NAME), + (char *)alpm_pkg_getinfo(info, PM_PKG_VERSION)); + } + FREEPKG(info); + continue; + } + + /* determine the owner of a file */ + if(pmo_q_owns) { + return(query_fileowner(db_local, package)); + } + + /* find packages in the db */ + if(package == NULL) { + PM_LIST *lp; + /* no target */ + for(lp = alpm_db_getpkgcache(db_local); lp; lp = alpm_list_next(lp)) { + PM_PKG *tmpp = alpm_list_getdata(lp); + char *pkgname, *pkgver; + + pkgname = alpm_pkg_getinfo(tmpp, PM_PKG_NAME); + pkgver = alpm_pkg_getinfo(tmpp, PM_PKG_VERSION); + + if(pmo_q_list || pmo_q_orphans) { + info = alpm_db_readpkg(db_local, pkgname); + if(info == NULL) { + /* something weird happened */ + ERR(NL, "package \"%s\" not found\n", pkgname); + return(1); + } + if(pmo_q_list) { + dump_pkg_files(info); + } + if(pmo_q_orphans) { + if(alpm_pkg_getinfo(info, PM_PKG_REQUIREDBY) == NULL + && alpm_pkg_getinfo(info, PM_PKG_REASON) == PM_PKG_REASON_EXPLICIT) { + MSG(NL, "%s %s\n", pkgname, pkgver); + } + } + } else { + MSG(NL, "%s %s\n", pkgname, pkgver); + } + } + } else { + char *pkgname, *pkgver; + + info = alpm_db_readpkg(db_local, package); + if(info == NULL) { + ERR(NL, "package \"%s\" not found\n", package); + return(2); + } + + /* find a target */ + if(pmo_q_info || pmo_q_list) { + if(pmo_q_info) { + dump_pkg_full(info, pmo_q_info); + } + if(pmo_q_list) { + dump_pkg_files(info); + } + } else if(pmo_q_orphans) { + if(alpm_pkg_getinfo(info, PM_PKG_REQUIREDBY) == NULL) { + MSG(NL, "%s %s\n", pkgname, pkgver); + } + } else { + pkgname = alpm_pkg_getinfo(info, PM_PKG_NAME); + pkgver = alpm_pkg_getinfo(info, PM_PKG_VERSION); + MSG(NL, "%s %s\n", pkgname, pkgver); + } + } + } + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/query.h b/src/pacman/query.h new file mode 100644 index 00000000..6f1143d2 --- /dev/null +++ b/src/pacman/query.h @@ -0,0 +1,28 @@ +/* + * query.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_QUERY_H +#define _PM_QUERY_H + +int pacman_query(list_t *targets); + +#endif /* _PM_QUERY_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/remove.c b/src/pacman/remove.c new file mode 100644 index 00000000..2f543106 --- /dev/null +++ b/src/pacman/remove.c @@ -0,0 +1,137 @@ +/* + * remove.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <alpm.h> +/* pacman */ +#include "util.h" +#include "list.h" +#include "pacman.h" + +extern unsigned char pmo_flags; + +extern PM_DB *db_local; + +int pacman_remove(list_t *targets) +{ + PM_LIST *data; + list_t *i; + list_t *finaltargs = NULL; + + if(targets == NULL) { + return(0); + } + + /* If the target is a group, ask if its packages should be removed + * (the library can't remove groups for now) + */ + for(i = targets; i; i = i->next) { + PM_GRP *grp; + + grp = alpm_db_readgrp(db_local, i->data); + if(grp) { + PM_LIST *lp, *pkgnames; + int all; + + pkgnames = alpm_grp_getinfo(grp, PM_GRP_PKGNAMES); + + MSG(NL, ":: group %s:\n", alpm_grp_getinfo(grp, PM_GRP_NAME)); + PM_LIST_display(" ", pkgnames); + all = yesno(" Remove whole content? [Y/n] "); + for(lp = alpm_list_first(pkgnames); lp; lp = alpm_list_next(lp)) { + if(all || yesno(":: Remove %s from group %s? [Y/n] ", (char *)alpm_list_getdata(lp), i->data)) { + finaltargs = list_add(finaltargs, strdup(alpm_list_getdata(lp))); + } + } + } else { + /* not a group, so add it to the final targets */ + finaltargs = list_add(finaltargs, strdup(i->data)); + } + } + + /* Step 1: create a new transaction + */ + if(alpm_trans_init(PM_TRANS_TYPE_REMOVE, pmo_flags, cb_trans) == -1) { + ERR(NL, "failed to init transaction (%s)\n", alpm_strerror(pm_errno)); + goto error; + } + /* and add targets to it */ + for(i = finaltargs; i; i = i->next) { + if(alpm_trans_addtarget(i->data) == -1) { + ERR(NL, "failed to add target '%s' (%s)\n", (char *)i->data, alpm_strerror(pm_errno)); + goto error; + } + } + + /* Step 2: prepare the transaction based on its type, targets and flags + */ + if(alpm_trans_prepare(&data) == -1) { + PM_LIST *i; + ERR(NL, "failed to prepare transaction (%s)\n", alpm_strerror(pm_errno)); + switch(pm_errno) { + case PM_ERR_UNSATISFIED_DEPS: + for(i = alpm_list_first(data); i; i = alpm_list_next(i)) { + pmdepmissing_t *miss = alpm_list_getdata(i); + MSG(NL, " %s: is required by %s\n", miss->target, miss->depend.name); + } + alpm_list_free(data); + break; + default: + ERR(NL, "%s\n", alpm_strerror(pm_errno)); + } + goto error; + } + + /* Warn user in case of dangerous operation + */ + if(pmo_flags & PM_TRANS_FLAG_RECURSE || pmo_flags & PM_TRANS_FLAG_CASCADE) { + /* list transaction targets */ + PM_LIST_display("\nTargets:", alpm_trans_getinfo(PM_TRANS_TARGETS)); + /* get confirmation */ + if(yesno("\nDo you want to remove these packages? [Y/n] ") == 0) { + goto error; + } + MSG(NL, "\n"); + } + + /* Step 3: actually perform the removal + */ + if(alpm_trans_commit() == -1) { + ERR(NL, "failed to commit transaction (%s)\n", alpm_strerror(pm_errno)); + goto error; + } + + /* Step 4: cleanup */ + FREELIST(finaltargs); + + return(0); + +error: + FREELIST(finaltargs); + alpm_trans_release(); + + return(1); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/remove.h b/src/pacman/remove.h new file mode 100644 index 00000000..20595337 --- /dev/null +++ b/src/pacman/remove.h @@ -0,0 +1,28 @@ +/* + * remove.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_REMOVE_H +#define _PM_REMOVE_H + +int pacman_remove(list_t *targets); + +#endif /* _PM_REMOVE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/sync.c b/src/pacman/sync.c new file mode 100644 index 00000000..da89a2b9 --- /dev/null +++ b/src/pacman/sync.c @@ -0,0 +1,806 @@ +/* + * sync.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> + +#include <alpm.h> +/* pacman */ +#include "util.h" +#include "download.h" +#include "list.h" +#include "package.h" +#include "db.h" +#include "sync.h" +#include "pacman.h" + +extern char *pmo_root; +extern char *pmo_dbpath; + +extern unsigned short pmo_noconfirm; +extern unsigned short pmo_d_resolve; +extern unsigned short pmo_q_info; +extern unsigned short pmo_q_list; +extern unsigned short pmo_s_upgrade; +extern unsigned short pmo_s_downloadonly; +extern unsigned short pmo_s_printuris; +extern unsigned short pmo_s_sync; +extern unsigned short pmo_s_search; +extern unsigned short pmo_s_clean; +extern unsigned short pmo_group; +extern unsigned char pmo_flags; + +extern PM_DB *db_local; +extern list_t *pmc_syncs; + +extern int maxcols; + +static int sync_cleancache(int level) +{ + if(level == 1) { + /* incomplete cleanup: we keep latest packages and partial downloads */ + DIR *dir; + struct dirent *ent; + list_t *cache = NULL; + list_t *clean = NULL; + list_t *i, *j; + + printf("removing old packages from cache... "); + dir = opendir("/var/cache/pacman/pkg"); + if(dir == NULL) { + fprintf(stderr, "error: could not access cache directory\n"); + return(1); + } + rewinddir(dir); + while((ent = readdir(dir)) != NULL) { + if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + continue; + } + cache = list_add(cache, strdup(ent->d_name)); + } + closedir(dir); + + for(i = cache; i; i = i->next) { + char *str = i->data; + char name[256], version[64]; + + if(strstr(str, ".pkg.tar.gz") == NULL) { + clean = list_add(clean, strdup(str)); + continue; + } + /* we keep partially downloaded files */ + if(strstr(str, ".pkg.tar.gz.part")) { + continue; + } + if(split_pkgname(str, name, version) != 0) { + clean = list_add(clean, strdup(str)); + continue; + } + for(j = i->next; j; j = j->next) { + char *s = j->data; + char n[256], v[64]; + + if(strstr(s, ".pkg.tar.gz") == NULL) { + continue; + } + if(strstr(s, ".pkg.tar.gz.part")) { + continue; + } + if(split_pkgname(s, n, v) != 0) { + continue; + } + if(!strcmp(name, n)) { + char *ptr = (alpm_pkg_vercmp(version, v) < 0) ? str : s; + if(!list_is_strin(ptr, clean)) { + clean = list_add(clean, strdup(ptr)); + } + } + } + } + FREELIST(cache); + + for(i = clean; i; i = i->next) { + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%s%s/%s", pmo_root, CACHEDIR, (char *)i->data); + unlink(path); + } + FREELIST(clean); + } else { + /* ORE + // full cleanup + mode_t oldmask; + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%s%s", pmo_root, CACHEDIR); + + printf("removing all packages from cache... "); + if(rmrf(path)) { + fprintf(stderr, "error: could not remove cache directory\n"); + return(1); + } + + oldmask = umask(0000); + if(makepath(path)) { + fprintf(stderr, "error: could not create new cache directory\n"); + return(1); + } + umask(oldmask);*/ + } + printf("done.\n"); + return(0); +} + +static int sync_synctree(list_t *syncs) +{ + char path[PATH_MAX]; + mode_t oldmask; + list_t *files = NULL; + list_t *i; + int success = 0; + + for(i = syncs; i; i = i->next) { + sync_t *sync = (sync_t *)i->data; + + /* build a one-element list */ + snprintf(path, PATH_MAX, "%s.db.tar.gz", sync->treename); + files = list_add(files, strdup(path)); + + success = 1; + if(downloadfiles(sync->servers, pmo_dbpath, files)) { + fprintf(stderr, "failed to synchronize %s\n", sync->treename); + success = 0; + } + + FREELIST(files); + snprintf(path, PATH_MAX, "%s/%s.db.tar.gz", pmo_dbpath, sync->treename); + + if(success) { + char ldir[PATH_MAX]; + + snprintf(ldir, PATH_MAX, "%s/%s", pmo_dbpath, sync->treename); + /* remove the old dir */ + vprint("removing %s (if it exists)\n", ldir); + rmrf(ldir); + + /* make the new dir */ + oldmask = umask(0000); + mkdir(ldir, 0755); + umask(oldmask); + + /* uncompress the sync database */ + vprint("Unpacking %s...\n", path); + if(unpack(path, ldir, NULL)) { + return(1); + } + } + /* remove the .tar.gz */ + unlink(path); + } + + return(!success); +} + +static int sync_search(list_t *syncs, list_t *targets) +{ + list_t *i; + + for(i = syncs; i; i = i->next) { + sync_t *sync = i->data; + if(targets) { + list_t *j; + + for(j = targets; j; j = j->next) { + db_search(sync->db, sync->treename, j->data); + } + } else { + PM_LIST *lp; + + for(lp = alpm_db_getpkgcache(sync->db); lp; lp = alpm_list_next(lp)) { + PM_PKG *pkg = alpm_list_getdata(lp); + + printf("%s/%s %s\n ", sync->treename, (char *)alpm_pkg_getinfo(pkg, PM_PKG_NAME), (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION)); + indentprint(alpm_pkg_getinfo(pkg, PM_PKG_DESC), 4); + printf("\n"); + } + } + } + + return(0); +} + +static int sync_group(list_t *syncs, list_t *targets) +{ + list_t *i, *j; + + if(targets) { + for(i = targets; i; i = i->next) { + for(j = syncs; j; j = j->next) { + sync_t *sync = j->data; + PM_GRP *grp = alpm_db_readgrp(sync->db, i->data); + + if(grp) { + printf("%s/%s\n", sync->treename, (char *)alpm_grp_getinfo(grp, PM_GRP_NAME)); + PM_LIST_display(" ", alpm_grp_getinfo(grp, PM_GRP_PKGNAMES)); + } + } + } + } else { + for(j = syncs; j; j = j->next) { + sync_t *sync = j->data; + PM_LIST *lp; + + for(lp = alpm_db_getpkgcache(sync->db); lp; lp = alpm_list_next(lp)) { + PM_GRP *grp = alpm_list_getdata(lp); + + printf("%s/%s\n", (char *)sync->treename, (char *)alpm_grp_getinfo(grp, PM_GRP_NAME)); + PM_LIST_display(" ", alpm_grp_getinfo(grp, PM_GRP_PKGNAMES)); + } + } + } + + return(0); +} + +static int sync_info(list_t *syncs, list_t *targets) +{ + list_t *i, *j; + + if(targets) { + for(i = targets; i; i = i->next) { + int found = 0; + + for(j = syncs; j && !found; j = j->next) { + sync_t *sync = j->data; + PM_LIST *lp; + + for(lp = alpm_db_getpkgcache(sync->db); !found && lp; lp = alpm_list_next(lp)) { + PM_PKG *pkg = alpm_list_getdata(lp); + + if(!strcmp(alpm_pkg_getinfo(pkg, PM_PKG_NAME), i->data)) { + dump_pkg_sync(pkg, sync->treename); + printf("\n"); + found = 1; + } + } + } + if(!found) { + fprintf(stderr, "Package \"%s\" was not found.\n", (char *)i->data); + break; + } + } + } else { + for(j = syncs; j; j = j->next) { + sync_t *sync = j->data; + PM_LIST *lp; + + for(lp = alpm_db_getpkgcache(sync->db); lp; lp = alpm_list_next(lp)) { + dump_pkg_sync(alpm_list_getdata(lp), sync->treename); + printf("\n"); + } + } + } + + return(0); +} + +static int sync_list(list_t *syncs, list_t *targets) +{ + list_t *i, *treenames = NULL; + + if(targets) { + for(i = targets; i; i = i->next) { + list_t *j; + sync_t *sync = NULL; + + for(j = syncs; j; j = j->next) { + sync_t *s = j->data; + + if(strcmp(i->data, s->treename) == 0) { + MALLOC(sync, sizeof(sync_t)); + sync->treename = i->data; + sync->db = s->db; + } + } + + if(sync == NULL) { + fprintf(stderr, "Repository \"%s\" was not found.\n\n", (char *)i->data); + list_free(treenames); + return(1); + } + + treenames = list_add(treenames, sync); + } + } else { + treenames = syncs; + } + + for(i = treenames; i; i = i->next) { + PM_LIST *lp; + sync_t *sync = i->data; + + for(lp = alpm_db_getpkgcache(sync->db); lp; lp = alpm_list_next(lp)) { + PM_PKG *pkg = alpm_list_getdata(lp); + + printf("%s %s %s\n", (char *)sync->treename, (char *)alpm_pkg_getinfo(pkg, PM_PKG_NAME), (char *)alpm_pkg_getinfo(pkg, PM_PKG_VERSION)); + } + } + + if(targets) { + list_free(treenames); + } + + return(0); +} + +int pacman_sync(list_t *targets) +{ + int allgood = 1, confirm = 0; + int retval = 0; + list_t *final = NULL; + list_t *i, *j; + PM_LIST *lp, *data; + + char ldir[PATH_MAX]; + int varcache = 1; + int done = 0; + int count = 0; + sync_t *current = NULL; + list_t *processed = NULL; + list_t *files = NULL; + + if(pmc_syncs == NULL || !list_count(pmc_syncs)) { + ERR(NL, "error: no usable package repositories configured."); + return(1); + } + + if(pmo_s_clean) { + return(sync_cleancache(pmo_s_clean)); + } + + if(pmo_s_sync) { + /* grab a fresh package list */ + MSG(NL, ":: Synchronizing package databases...\n"); + alpm_logaction("synchronizing package lists"); + sync_synctree(pmc_syncs); + } + + /* open the database(s) */ + for(i = pmc_syncs; i; i = i->next) { + sync_t *sync = i->data; + if(alpm_db_register(sync->treename, &sync->db) == -1) { + ERR(NL, "%s\n", alpm_strerror(pm_errno)); + return(1); + } + } + + if(pmo_s_search) { + return(sync_search(pmc_syncs, targets)); + } + + if(pmo_group) { + return(sync_group(pmc_syncs, targets)); + } + + if(pmo_q_info) { + return(sync_info(pmc_syncs, targets)); + } + + if(pmo_q_list) { + return(sync_list(pmc_syncs, targets)); + } + + if(pmo_s_upgrade) { + /* ORE + alpm_logaction(NULL, "starting full system upgrade");*/ + if(alpm_sync_sysupgrade(&data) == -1) { + if(pm_errno == PM_ERR_UNRESOLVABLE_DEPS) { + ERR(NL, "cannot resolve dependencies\n"); + for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) { + pmdepmissing_t *miss = alpm_list_getdata(lp); + ERR(NL, " %s: \"%s\" is not in the package set\n", miss->target, miss->depend.name); + } + alpm_list_free(data); + } else { + ERR(NL, "%s\n", alpm_strerror(pm_errno)); + } + return(1); + } + + /* check if pacman itself is one of the packages to upgrade. If so, we + * we should upgrade ourselves first and then re-exec as the new version. + * + * this can prevent some of the "syntax error" problems users can have + * when sysupgrade'ing with an older version of pacman. + */ + for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) { + PM_SYNC *sync = alpm_list_getdata(lp); + + if(!strcmp("pacman", alpm_pkg_getinfo(alpm_sync_getinfo(sync, PM_SYNC_SYNCPKG), PM_PKG_NAME))) { + ERR(NL, "\n:: pacman has detected a newer version of the \"pacman\" package.\n"); + ERR(NL, ":: It is recommended that you allow pacman to upgrade itself\n"); + ERR(NL, ":: first, then you can re-run the operation with the newer version.\n"); + ERR(NL, "::\n"); + if(yesno(":: Upgrade pacman first? [Y/n] ")) { + alpm_list_free(data); + data = NULL; + } + } + } + + for(lp = alpm_list_first(data); lp; lp = alpm_list_next(lp)) { + PM_SYNC *sync = alpm_list_getdata(lp); + PM_PKG *lpkg, *spkg; + char *spkgname, *spkgver, *lpkgname, *lpkgver; + + lpkg = alpm_sync_getinfo(sync, PM_SYNC_LOCALPKG); + lpkgname = alpm_pkg_getinfo(lpkg, PM_PKG_NAME); + lpkgver = alpm_pkg_getinfo(lpkg, PM_PKG_VERSION); + + spkg = alpm_sync_getinfo(sync, PM_SYNC_SYNCPKG); + spkgname = alpm_pkg_getinfo(spkg, PM_PKG_NAME); + spkgver = alpm_pkg_getinfo(spkg, PM_PKG_VERSION); + + switch((int)alpm_sync_getinfo(sync, PM_SYNC_TYPE)) { + case PM_SYSUPG_REPLACE: + if(yesno(":: Replace %s with %s from \"%s\"? [Y/n] ", lpkgname, spkgname, NULL/*dbs->db->treename*/)) { + DBG("adding '%s-%s' to replaces candidates\n", spkgname, spkgver); + final = list_add(final, spkg); + } + + break; + case PM_SYSUPG_UPGRADE: + DBG("Upgrade %s (%s => %s)\n", lpkgname, lpkgver, spkgver); + final = list_add(final, spkg); + break; + default: + break; + } + } + alpm_list_free(data); + } else { + /* process targets */ + for(i = targets; i; i = i->next) { + char *treename; + char *targ; + char *targline; + PM_PKG *local = NULL; + + targline = strdup((char *)i->data); + targ = index(targline, '/'); + if(targ) { + *targ = '\0'; + targ++; + treename = targline; + } else { + targ = targline; + treename = NULL; + } + + if(treename == NULL) { + for(j = pmc_syncs; j && !local; j = j->next) { + sync_t *sync = j->data; + local = alpm_db_readpkg(sync->db, targ); + } + } else { + for(j = pmc_syncs; j && !local; j = j->next) { + sync_t *sync = j->data; + if(strcmp(sync->treename, treename) == 0) { + local = alpm_db_readpkg(sync->db, targ); + } + } + } + + if(local == NULL) { + PM_GRP *grp = NULL; + /* target not found: check if it's a group */ + for(j = pmc_syncs; j && !grp; j = j->next) { + sync_t *sync = j->data; + grp = alpm_db_readgrp(sync->db, targ); + if(grp) { + PM_LIST *k, *pkgs; + MSG(NL, ":: group %s:\n", targ); + + pkgs = alpm_grp_getinfo(grp, PM_GRP_PKGNAMES); + PM_LIST_display(" ", pkgs); + + if(yesno(" Install whole content? [Y/n] ")) { + for(k = alpm_list_first(pkgs); k; k = alpm_list_next(k)) { + targets = list_add(targets, strdup(alpm_list_getdata(k))); + } + } else { + for(k = alpm_list_first(pkgs); k; k = alpm_list_next(k)) { + char *pkgname = alpm_list_getdata(k); + if(yesno(":: Install %s from group %s? [Y/n] ", pkgname, targ)) { + targets = list_add(targets, strdup(pkgname)); + } + } + } + } + } + if(grp == NULL) { + ERR(NL, "package \"%s\" not found", targ); + return(1); + } + } + if(treename) { + FREE(targline); + } + + } + + if(!pmo_s_downloadonly && !pmo_s_printuris) { + /* this is an upgrade, compare versions and determine if it is necessary */ + for(i = targets; i; i = i->next) { + int cmp; + PM_PKG *local, *sync; + char *lpkgname, *lpkgver, *spkgver; + + local = alpm_db_readpkg(db_local, i->data); + lpkgname = alpm_pkg_getinfo(local, PM_PKG_NAME); + lpkgver = alpm_pkg_getinfo(local, PM_PKG_VERSION); + + sync = alpm_db_readpkg(db_local, i->data); + spkgver = alpm_pkg_getinfo(sync, PM_PKG_VERSION); + + cmp = alpm_pkg_vercmp(lpkgver, spkgver); + if(cmp > 0) { + /* local version is newer - get confirmation first */ + if(!yesno(":: %s-%s: local version is newer. Upgrade anyway? [Y/n] ", lpkgname, lpkgver)) { + /* ORE + char *data = list_remove(targets, lpkgname); + free(data);*/ + } + } else if(cmp == 0) { + /* versions are identical */ + if(!yesno(":: %s-%s: is up to date. Upgrade anyway? [Y/n] ", lpkgname, lpkgver)) { + /* ORE + char *data = list_remove(targets, lpkgname); + free(data);*/ + } + } + } + } + } + + /* Step 1: create a new transaction */ + if(alpm_trans_init(PM_TRANS_TYPE_SYNC, pmo_flags, NULL) == -1) { + ERR(NL, "failed to init transaction (%s)\n", alpm_strerror(pm_errno)); + retval = 1; + goto cleanup; + } + /* and add targets to it */ + for(i = targets; i; i = i->next) { + if(alpm_trans_addtarget(i->data) == -1) { + ERR(NL, "failed to add target '%s' (%s)\n", (char *)i->data, alpm_strerror(pm_errno)); + retval = 1; + goto cleanup; + } + } + + PM_LIST_display("target :", alpm_trans_getinfo(PM_TRANS_TARGETS)); + /* ORE + TBD */ + + /* Step 2: "compute" the transaction based on targets and flags */ + if(alpm_trans_prepare(&data) == -1) { + ERR(NL, "failed to prepare transaction (%s)\n", alpm_strerror(pm_errno)); + return(1); + } + + /* list targets */ + if(final && !pmo_s_printuris) { + list_t *list = NULL; + char *str; + unsigned long totalsize = 0; + double mb; + /* ORE + for(i = rmtargs; i; i = i->next) { + list = list_add(list, strdup(i->data)); + } + for(i = final; i; i = i->next) { + syncpkg_t *s = (syncpkg_t*)i->data; + for(j = s->replaces; j; j = j->next) { + pkginfo_t *p = (pkginfo_t*)j->data; + list = list_add(list, strdup(p->name)); + } + } + if(list) { + printf("\nRemove: "); + str = buildstring(list); + indentprint(str, 9); + printf("\n"); + FREELIST(list); + FREE(str); + }*/ + /* ORE + for(i = final; i; i = i->next) { + MALLOC(str, strlen(s->pkg->name)+strlen(s->pkg->version)+2); + sprintf(str, "%s-%s", s->pkg->name, s->pkg->version); + list = list_add(list, str); + totalsize += s->pkg->size; + }*/ + mb = (double)(totalsize / 1048576.0); + /* round up to 0.1 */ + if(mb < 0.1) { + mb = 0.1; + } + printf("\nTargets: "); + str = buildstring(list); + indentprint(str, 9); + printf("\n\nTotal Package Size: %.1f MB\n", mb); + FREELIST(list); + FREE(str); + } + + /* get confirmation */ + if(pmo_s_downloadonly) { + if(pmo_noconfirm) { + MSG(NL, "\nBeginning download...\n"); + confirm = 1; + } else { + confirm = yesno("\nProceed with download? [Y/n] "); + } + } else { + /* don't get any confirmation if we're called from makepkg */ + if(pmo_d_resolve || pmo_s_printuris) { + confirm = 1; + } else { + if(pmo_noconfirm) { + MSG(NL, "\nBeginning upgrade process...\n"); + confirm = 1; + } else { + confirm = yesno("\nProceed with upgrade? [Y/n] "); + } + } + } + if(!confirm) { + retval = 1; + goto cleanup; + } + + /* ORE + group sync records by repository and download */ + + snprintf(ldir, PATH_MAX, "%svar/cache/pacman/pkg", pmo_root); + + while(!done) { + if(current) { + processed = list_add(processed, current); + current = NULL; + } + for(i = final; i; i = i->next) { + if(current == NULL) { + /* we're starting on a new repository */ + } + /*if(current && !strcmp(current->treename, sync->dbs->sync->treename)) { + }*/ + } + + if(files) { + if(pmo_s_printuris) { + server_t *server = (server_t*)current->servers->data; + for(j = files; j; j = j->next) { + if(!strcmp(server->protocol, "file")) { + MSG(NL, "%s://%s%s\n", server->protocol, server->path, + (char *)j->data); + } else { + MSG(NL, "%s://%s%s%s\n", server->protocol, + server->server, server->path, (char *)j->data); + } + } + } else { + struct stat buf; + + MSG(NL, "\n:: Retrieving packages from %s...\n", current->treename); + fflush(stdout); + if(stat(ldir, &buf)) { + mode_t oldmask; + + /* no cache directory.... try creating it */ + /* ORE + * alpm_logaction(stderr, "warning: no %s cache exists. creating...", ldir);*/ + oldmask = umask(0000); + if(makepath(ldir)) { + /* couldn't mkdir the cache directory, so fall back to /tmp and unlink + * the package afterwards. + */ + /* ORE + * logaction(stderr, "warning: couldn't create package cache, using /tmp instead");*/ + snprintf(ldir, PATH_MAX, "/tmp"); + varcache = 0; + } + umask(oldmask); + } + if(downloadfiles(current->servers, ldir, files)) { + ERR(NL, "failed to retrieve some files from %s\n", current->treename); + retval = 1; + goto cleanup; + } + } + + count += list_count(files); + FREELIST(files); + } + if(count == list_count(final)) { + done = 1; + } + } + printf("\n"); + + /* Check integrity of files */ + MSG(NL, "checking package integrity... "); + + allgood = 1; + for(i = final; i; i = i->next) { + char /*str[PATH_MAX],*/ pkgname[PATH_MAX]; + char *md5sum1, *md5sum2; + + snprintf(pkgname, PATH_MAX, "%s-%s.pkg.tar.gz", "", ""); + + md5sum1 = NULL; + md5sum2 = NULL; + + if(strcmp(md5sum1, md5sum2) != 0) { + if(allgood) { + printf("\n"); + } + ERR(NL, "error: archive %s is corrupted\n", ""); + allgood = 0; + } + + FREE(md5sum2); + + } + if(!allgood) { + retval = 1; + goto cleanup; + } + MSG(CL, "done.\n"); + + /* Step 3: actually perform the installation */ + if(!pmo_s_downloadonly) { + if(alpm_trans_commit(&data) == -1) { + ERR(NL, "failed to commit transaction (%s)\n", alpm_strerror(pm_errno)); + retval = 1; + goto cleanup; + } + } + + if(!varcache && !pmo_s_downloadonly) { + /* delete packages */ + for(i = files; i; i = i->next) { + unlink(i->data); + } + } + +cleanup: + + return(retval); + +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/sync.h b/src/pacman/sync.h new file mode 100644 index 00000000..7780caa1 --- /dev/null +++ b/src/pacman/sync.h @@ -0,0 +1,35 @@ +/* + * sync.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_SYNC_H +#define _PM_SYNC_H + +/* Repositories */ +typedef struct __sync_t { + char *treename; + PM_DB *db; + list_t *servers; /* List of server_t */ +} sync_t; + +int pacman_sync(list_t *targets); + +#endif /* _PM_SYNC_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/upgrade.c b/src/pacman/upgrade.c new file mode 100644 index 00000000..9149ee69 --- /dev/null +++ b/src/pacman/upgrade.c @@ -0,0 +1,42 @@ +/* + * upgrade.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <alpm.h> +/* pacman */ +#include "list.h" +#include "add.h" + +extern unsigned char pmo_upgrade; + +int pacman_upgrade(list_t *targets) +{ + /* this is basically just a remove-then-add process. pacman_add() will */ + /* handle it */ + pmo_upgrade = 1; + return(pacman_add(targets)); +} + + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/upgrade.h b/src/pacman/upgrade.h new file mode 100644 index 00000000..4eeac4ed --- /dev/null +++ b/src/pacman/upgrade.h @@ -0,0 +1,28 @@ +/* + * upgrade.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_UPGRADE_H +#define _PM_UPGRADE_H + +int pacman_upgrade(list_t *targets); + +#endif /* _PM_UPGRADE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/util.c b/src/pacman/util.c new file mode 100644 index 00000000..56c3349b --- /dev/null +++ b/src/pacman/util.c @@ -0,0 +1,297 @@ +/* + * util.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <ctype.h> +#include <dirent.h> +#include <zlib.h> +#include <libtar.h> + +/* pacman */ +#include "util.h" + +/* borrowed and modified from Per Liden's pkgutils (http://crux.nu) */ +long gzopen_frontend(char *pathname, int oflags, int mode) +{ + char *gzoflags; + int fd; + gzFile gzf; + + switch (oflags & O_ACCMODE) { + case O_WRONLY: + gzoflags = "w"; + break; + case O_RDONLY: + gzoflags = "r"; + break; + case O_RDWR: + default: + errno = EINVAL; + return -1; + } + + if((fd = open(pathname, oflags, mode)) == -1) { + return -1; + } + if((oflags & O_CREAT) && fchmod(fd, mode)) { + return -1; + } + if(!(gzf = gzdopen(fd, gzoflags))) { + errno = ENOMEM; + return -1; + } + + return (long)gzf; +} + +int unpack(char *archive, const char *prefix, const char *fn) +{ + TAR *tar = NULL; + char expath[PATH_MAX]; + tartype_t gztype = { + (openfunc_t) gzopen_frontend, + (closefunc_t)gzclose, + (readfunc_t) gzread, + (writefunc_t)gzwrite + }; + + /* open the .tar.gz package */ + if(tar_open(&tar, archive, &gztype, O_RDONLY, 0, TAR_GNU) == -1) { + perror(archive); + return(1); + } + while(!th_read(tar)) { + if(fn && strcmp(fn, th_get_pathname(tar))) { + if(TH_ISREG(tar) && tar_skip_regfile(tar)) { + char errorstr[255]; + snprintf(errorstr, 255, "bad tar archive: %s", archive); + perror(errorstr); + tar_close(tar); + return(1); + } + continue; + } + snprintf(expath, PATH_MAX, "%s/%s", prefix, th_get_pathname(tar)); + if(tar_extract_file(tar, expath)) { + fprintf(stderr, "could not extract %s: %s\n", th_get_pathname(tar), strerror(errno)); + } + if(fn) break; + } + tar_close(tar); + + return(0); +} + +/* does the same thing as 'mkdir -p' */ +int makepath(char *path) +{ + char *orig, *str, *ptr; + char full[PATH_MAX] = ""; + mode_t oldmask; + + oldmask = umask(0000); + + orig = strdup(path); + str = orig; + while((ptr = strsep(&str, "/"))) { + if(strlen(ptr)) { + struct stat buf; + + strcat(full, "/"); + strcat(full, ptr); + if(stat(full, &buf)) { + if(mkdir(full, 0755)) { + free(orig); + umask(oldmask); + return(1); + } + } + } + } + free(orig); + umask(oldmask); + return(0); +} + +/* does the same thing as 'rm -rf' */ +int rmrf(char *path) +{ + int errflag = 0; + struct dirent *dp; + DIR *dirp; + char name[PATH_MAX]; + extern int errno; + + if(!unlink(path)) { + return(0); + } else { + if(errno == ENOENT) { + return(0); + } else if(errno == EPERM) { + /* fallthrough */ + } else if(errno == EISDIR) { + /* fallthrough */ + } else if(errno == ENOTDIR) { + return(1); + } else { + /* not a directory */ + return(1); + } + + if((dirp = opendir(path)) == (DIR *)-1) { + return(1); + } + for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + if(dp->d_ino) { + sprintf(name, "%s/%s", path, dp->d_name); + if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) { + errflag += rmrf(name); + } + } + } + closedir(dirp); + if(rmdir(path)) { + errflag++; + } + return(errflag); + } + return(0); +} + +/* output a string, but wrap words properly with a specified indentation + */ +void indentprint(char *str, int indent) +{ + char *p = str; + char *cenv = NULL; + int cols = 80; + int cidx = indent; + + cenv = getenv("COLUMNS"); + if(cenv) { + cols = atoi(cenv); + } + + while(*p) { + if(*p == ' ') { + char *next = NULL; + int len; + p++; + if(p == NULL || *p == ' ') continue; + next = strchr(p, ' '); + if(next == NULL) { + next = p + strlen(p); + } + len = next - p; + if(len > (cols-cidx-1)) { + /* newline */ + int i; + fprintf(stdout, "\n"); + for(i = 0; i < indent; i++) { + fprintf(stdout, " "); + } + cidx = indent; + } else { + printf(" "); + cidx++; + } + } + fprintf(stdout, "%c", *p); + p++; + cidx++; + } +} + +/* Convert a string to uppercase + */ +char *strtoupper(char *str) +{ + char *ptr = str; + + while(*ptr) { + (*ptr) = toupper(*ptr); + ptr++; + } + return str; +} + +/* Trim whitespace and newlines from a string + */ +char *strtrim(char *str) +{ + char *pch = str; + while(isspace(*pch)) { + pch++; + } + if(pch != str) { + memmove(str, pch, (strlen(pch) + 1)); + } + + pch = (char *)(str + (strlen(str) - 1)); + while(isspace(*pch)) { + pch--; + } + *++pch = '\0'; + + return str; +} + +/* presents a prompt and gets a Y/N answer + */ +int yesno(char *fmt, ...) +{ + char response[32]; + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + fflush(stdout); + if(fgets(response, 32, stdin)) { + /* trim whitespace and newlines */ + char *pch = response; + while(isspace(*pch)) { + pch++; + } + if(pch != response) { + memmove(response, pch, strlen(pch) + 1); + } + pch = response + strlen(response) - 1; + while(isspace(*pch)) { + pch--; + } + *++pch = 0; + strtrim(response); + + if(!strcasecmp(response, "Y") || !strcasecmp(response, "YES") || !strlen(response)) { + return(1); + } + } + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/util.h b/src/pacman/util.h new file mode 100644 index 00000000..15ce8043 --- /dev/null +++ b/src/pacman/util.h @@ -0,0 +1,42 @@ +/* + * util.h + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef _PM_UTIL_H +#define _PM_UTIL_H + +#define MALLOC(p, b) do { if((b) > 0) { \ + p = malloc(b); if (!(p)) { \ + fprintf(stderr, "malloc failure: could not allocate %d bytes\n", b); \ + exit(1); }} else p = NULL; } while(0) + +#define FREE(p) do { if (p) { free(p); (p)= NULL; }} while(0) + +long gzopen_frontend(char *pathname, int oflags, int mode); +int unpack(char *archive, const char *prefix, const char *fn); +int makepath(char *path); +int rmrf(char *path); +void indentprint(char *str, int indent); +char *strtrim(char *str); +char *strtoupper(char *str); +int yesno(char *fmt, ...); + +#endif /* _PM_UTIL_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/util/convertdb.c b/src/util/convertdb.c new file mode 100644 index 00000000..6fbebb23 --- /dev/null +++ b/src/util/convertdb.c @@ -0,0 +1,146 @@ +/* + * convertdb.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> +#include <string.h> +#include <libgen.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "pacconf.h" +#include "list.h" +#include "util.h" + +unsigned short pmo_verbose = 0; + +int main(int argc, char* argv[]) +{ + FILE* db = NULL; + FILE* fp = NULL; + char* ptr = NULL; + char name[256]; + char ver[256]; + char line[PATH_MAX+1]; + char topdir[PATH_MAX+1]; + char path[PATH_MAX+1]; + mode_t oldumask; + struct stat buf; + char dbdir[PATH_MAX]; + + sprintf(dbdir, "/%s", PACDBDIR); + + if(argc < 2) { + printf("converts a pacman 1.x database to a pacman 2.0 format\n"); + printf("usage: %s <target_dir>\n\n", basename(argv[0])); + printf("convertdb will convert all package data from /var/lib/pacman/pacman.db\n"); + printf("to a 2.0 format and place it in target_dir.\n\n"); + return(0); + } + + db = fopen(dbdir, "r"); + if(db == NULL) { + perror(dbdir); + return(1); + } + + oldumask = umask(0000); + while(fgets(name, 255, db)) { + PMList *backup = NULL; + PMList *lp; + + if(!fgets(ver, 255, db)) { + perror(dbdir); + return(1); + } + trim(name); + trim(ver); + fprintf(stderr, "converting %s\n", name); + /* package directory */ + snprintf(topdir, PATH_MAX, "%s/%s-%s", argv[1], name, ver); + mkdir(topdir, 0755); + + /* DESC */ + snprintf(path, PATH_MAX, "%s/desc", topdir); + if(!(fp = fopen(path, "w"))) { + perror(path); + return(1); + } + fputs("%NAME%\n", fp); + fprintf(fp, "%s\n\n", name); + fputs("%VERSION%\n", fp); + fprintf(fp, "%s\n\n", ver); + fputs("%DESC%\n\n", fp); + fclose(fp); + + /* DEPENDS */ + snprintf(path, PATH_MAX, "%s/depends", topdir); + if(!(fp = fopen(path, "w"))) { + perror(path); + return(1); + } + fputs("%DEPENDS%\n\n", fp); + fputs("%REQUIREDBY%\n\n", fp); + fputs("%CONFLICTS%\n\n", fp); + fclose(fp); + + /* FILES */ + snprintf(path, PATH_MAX, "%s/files", topdir); + if(!(fp = fopen(path, "w"))) { + perror(path); + return(1); + } + fputs("%FILES%\n", fp); + while(fgets(line, 255, db) && strcmp(trim(line), "")) { + trim(line); + ptr = line; + + /* check for backup designation and frontslashes that shouldn't be there */ + if(line[0] == '*') ptr++; + if(*ptr == '/') ptr++; + if(line[0] == '*') backup = list_add(backup, strdup(ptr)); + + fprintf(fp, "%s\n", ptr); + } + fprintf(fp, "\n"); + fputs("%BACKUP%\n", fp); + for(lp = backup; lp; lp = lp->next) { + /* print the filename and a bad md5 hash. we just use 32 f's cuz we can't + * md5 the original file since we don't have it + */ + fprintf(fp, "%s\tffffffffffffffffffffffffffffffff\n", (char*)lp->data); + } + fputs("\n", fp); + fclose(fp); + snprintf(path, PATH_MAX, "/var/lib/pacman/scripts/%s", name); + if(!stat(path, &buf)) { + snprintf(line, PATH_MAX, "/bin/cp %s %s/install", path, topdir); + system(line); + } + list_free(backup); + } + umask(oldumask); + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/util/vercmp.c b/src/util/vercmp.c new file mode 100644 index 00000000..0ed3da2e --- /dev/null +++ b/src/util/vercmp.c @@ -0,0 +1,45 @@ +/* + * vercmp.c + * + * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdio.h> +#include <string.h> +#include "rpmvercmp.h" + +int main(int argc, char *argv[]) +{ + char s1[255] = ""; + char s2[255] = ""; + int ret; + + if(argc > 1) { + strncpy(s1, argv[1], 255); + } + if(argc > 2) { + strncpy(s2, argv[2], 255); + } else { + printf("0\n"); + return(0); + } + + ret = rpmvercmp(s1, s2); + printf("%d\n", ret); + return(ret); +} |