summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJudd Vinet <judd@archlinux.org>2005-03-15 01:51:43 +0000
committerJudd Vinet <judd@archlinux.org>2005-03-15 01:51:43 +0000
commitd04baabafa2ebbad92741d1f87e6ff32999f894a (patch)
tree5a2280176812b80c28ca77bfa8e0655c16f4db7e
Initial revision
-rw-r--r--COPYING340
-rw-r--r--ChangeLog268
-rw-r--r--README288
-rw-r--r--TODO23
-rw-r--r--config.h0
-rw-r--r--doc/makepkg.8.in407
-rw-r--r--doc/pacman.8.in312
-rw-r--r--etc/makepkg.conf36
-rw-r--r--etc/pacman.conf53
-rw-r--r--lib/libalpm/Makefile39
-rw-r--r--lib/libalpm/add.c580
-rw-r--r--lib/libalpm/add.h34
-rw-r--r--lib/libalpm/alpm.c577
-rw-r--r--lib/libalpm/alpm.h330
-rw-r--r--lib/libalpm/backup.c63
-rw-r--r--lib/libalpm/backup.h30
-rw-r--r--lib/libalpm/cache.c203
-rw-r--r--lib/libalpm/cache.h42
-rw-r--r--lib/libalpm/db.c651
-rw-r--r--lib/libalpm/db.h60
-rw-r--r--lib/libalpm/deps.c685
-rw-r--r--lib/libalpm/deps.h35
-rw-r--r--lib/libalpm/error.c90
-rw-r--r--lib/libalpm/error.h30
-rw-r--r--lib/libalpm/group.c67
-rw-r--r--lib/libalpm/group.h48
-rw-r--r--lib/libalpm/handle.c229
-rw-r--r--lib/libalpm/handle.h63
-rw-r--r--lib/libalpm/list.c210
-rw-r--r--lib/libalpm/list.h50
-rw-r--r--lib/libalpm/log.c52
-rw-r--r--lib/libalpm/log.h32
-rw-r--r--lib/libalpm/md5.c338
-rw-r--r--lib/libalpm/md5.h51
-rw-r--r--lib/libalpm/md5driver.c81
-rw-r--r--lib/libalpm/package.c342
-rw-r--r--lib/libalpm/package.h78
-rw-r--r--lib/libalpm/provide.c53
-rw-r--r--lib/libalpm/provide.h33
-rw-r--r--lib/libalpm/remove.c259
-rw-r--r--lib/libalpm/remove.h34
-rw-r--r--lib/libalpm/rpmvercmp.c237
-rw-r--r--lib/libalpm/rpmvercmp.h32
-rw-r--r--lib/libalpm/sync.c248
-rw-r--r--lib/libalpm/sync.h47
-rw-r--r--lib/libalpm/trans.c187
-rw-r--r--lib/libalpm/trans.h54
-rw-r--r--lib/libalpm/util.c401
-rw-r--r--lib/libalpm/util.h57
-rw-r--r--lib/libftp/Makefile68
-rw-r--r--lib/libftp/ftplib.c1569
-rw-r--r--lib/libftp/ftplib.h131
-rwxr-xr-xscripts/gensync186
-rwxr-xr-xscripts/makepkg701
-rwxr-xr-xscripts/makeworld143
-rwxr-xr-xscripts/updatesync231
-rw-r--r--src/pacman/Makefile39
-rw-r--r--src/pacman/add.c130
-rw-r--r--src/pacman/add.h28
-rw-r--r--src/pacman/conf.c308
-rw-r--r--src/pacman/conf.h28
-rw-r--r--src/pacman/db.c98
-rw-r--r--src/pacman/db.h28
-rw-r--r--src/pacman/download.c467
-rw-r--r--src/pacman/download.h38
-rw-r--r--src/pacman/list.c176
-rw-r--r--src/pacman/list.h45
-rw-r--r--src/pacman/package.c203
-rw-r--r--src/pacman/package.h35
-rw-r--r--src/pacman/pacman.c688
-rw-r--r--src/pacman/pacman.h75
-rw-r--r--src/pacman/query.c247
-rw-r--r--src/pacman/query.h28
-rw-r--r--src/pacman/remove.c137
-rw-r--r--src/pacman/remove.h28
-rw-r--r--src/pacman/sync.c806
-rw-r--r--src/pacman/sync.h35
-rw-r--r--src/pacman/upgrade.c42
-rw-r--r--src/pacman/upgrade.h28
-rw-r--r--src/pacman/util.c297
-rw-r--r--src/pacman/util.h42
-rw-r--r--src/util/convertdb.c146
-rw-r--r--src/util/vercmp.c45
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
+
diff --git a/README b/README
new file mode 100644
index 00000000..db345460
--- /dev/null
+++ b/README
@@ -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...
+
+- ...
+
diff --git a/TODO b/TODO
new file mode 100644
index 00000000..08c7aa7b
--- /dev/null
+++ b/TODO
@@ -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);
+}