[darwinbuild-changes] [732] trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Mar 3 13:37:30 PST 2010


Revision: 732
          http://trac.macosforge.org/projects/darwinbuild/changeset/732
Author:   wsiegrist at apple.com
Date:     2010-03-03 13:37:30 -0800 (Wed, 03 Mar 2010)
Log Message:
-----------
Merge PR-7489777, PR-7598640, and PR-7638434

Modified Paths:
--------------
    trunk/CHANGES
    trunk/darwinbuild.xcodeproj/project.pbxproj
    trunk/darwintrace/darwintrace.c
    trunk/darwinup/Archive.cpp
    trunk/darwinup/Archive.h
    trunk/darwinup/Depot.cpp
    trunk/darwinup/Depot.h
    trunk/darwinup/Digest.cpp
    trunk/darwinup/Digest.h
    trunk/darwinup/File.cpp
    trunk/darwinup/File.h
    trunk/darwinup/SerialSet.cpp
    trunk/darwinup/SerialSet.h
    trunk/darwinup/Utils.cpp
    trunk/darwinup/Utils.h
    trunk/darwinup/main.cpp
    trunk/darwinup/redo_prebinding.h
    trunk/testing/darwinup/run-tests.sh

Added Paths:
-----------
    trunk/darwinup/Column.cpp
    trunk/darwinup/Column.h
    trunk/darwinup/DB.cpp
    trunk/darwinup/DB.h
    trunk/darwinup/Database.cpp
    trunk/darwinup/Database.h
    trunk/darwinup/Table.cpp
    trunk/darwinup/Table.h
    trunk/testing/darwinup/300dirs.tbz2
    trunk/testing/darwinup/300files.tbz2

Property Changed:
----------------
    trunk/
    trunk/darwinbuild/darwinbuild.in
    trunk/darwinbuild/darwinmaster.in
    trunk/darwinbuild/installXcode.in
    trunk/darwinbuild/packageRoots.in
    trunk/darwinbuild/thinPackages.in


Property changes on: trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388:399-419
/branches/PR-6358021:442-443
/branches/PR-6392966:423-427
/branches/PR-6398060:433-434
/branches/PR-6493844:460-461
/branches/PR-6497694:466-468,471
/branches/PR-6634286:632-650
/branches/PR-6688645:479-490
/branches/PR-6722857:495-499
/branches/PR-6729491:655-664
/branches/PR-7250612:635-650
/branches/PR-7341154:682-694
/branches/PR-7431723:660-664
/branches/PR-7461534:650-664
/branches/PR-7482850:670-671
/branches/PR-7529688:692-694
/trunk:432-434
   + /branches/PR-4841388:399-419
/branches/PR-6358021:442-443
/branches/PR-6392966:423-427
/branches/PR-6398060:433-434
/branches/PR-6493844:460-461
/branches/PR-6497694:466-468,471
/branches/PR-6634286:632-650
/branches/PR-6688645:479-490
/branches/PR-6722857:495-499
/branches/PR-6729491:655-664
/branches/PR-7250612:635-650
/branches/PR-7341154:682-694
/branches/PR-7431723:660-664
/branches/PR-7461534:650-664
/branches/PR-7482850:670-671
/branches/PR-7489777:676-731
/branches/PR-7529688:692-694
/branches/PR-7598640:703-731
/trunk:432-434

Modified: trunk/CHANGES
===================================================================
--- trunk/CHANGES	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/CHANGES	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,6 +1,17 @@
 Darwin Build Scripts Change History
 -----------------------------------
 
+Release 15 [03-Mar-2010]
+	- darwinup: added new database abstraction layer to simplify Depot code,
+	  reduce copy-paste, and reduce unnecessary statement preparation
+	- darwinup: allow list subcommand to run as not-root
+	- darwinup: support multiple command line arguments (ex: globbing)
+
+Release 14 [05-Feb-2010]
+	- darwinup: add upgrade command to automatically replace last root
+	  installed with the same name.
+	- darwinup: added ability to install roots which have broken symlinks
+
 Release 13.1 [22-Dec-2009]
 	- darwinup: fix bug where rollback archives for user modifications
 	  did not contain parent directories and thus certain


Property changes on: trunk/darwinbuild/darwinbuild.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/darwinbuild:399-419
/branches/PR-6358021/darwinbuild/darwinbuild:442-443
/branches/PR-6392966/darwinbuild/darwinbuild:423-427
/branches/PR-6398060/darwinbuild/darwinbuild:433-434
/branches/PR-6493844/darwinbuild/darwinbuild.in:460-461
/branches/PR-6497694/darwinbuild/darwinbuild.in:466-468,471
/branches/PR-6634286/darwinbuild/darwinbuild.in:632-650
/branches/PR-6688645/darwinbuild/darwinbuild.in:479-490
/branches/PR-6722857/darwinbuild/darwinbuild.in:495-499
/branches/PR-6729491/darwinbuild/darwinbuild.in:655-664
/branches/PR-7250612/darwinbuild/darwinbuild.in:635-650
/branches/PR-7341154/darwinbuild/darwinbuild.in:682-694
/branches/PR-7431723/darwinbuild/darwinbuild.in:660-664
/branches/PR-7461534/darwinbuild/darwinbuild.in:650-664
/branches/PR-7529688/darwinbuild/darwinbuild.in:692-694
/trunk/darwinbuild/darwinbuild:432-434
   + /branches/PR-4841388/darwinbuild/darwinbuild:399-419
/branches/PR-6358021/darwinbuild/darwinbuild:442-443
/branches/PR-6392966/darwinbuild/darwinbuild:423-427
/branches/PR-6398060/darwinbuild/darwinbuild:433-434
/branches/PR-6493844/darwinbuild/darwinbuild.in:460-461
/branches/PR-6497694/darwinbuild/darwinbuild.in:466-468,471
/branches/PR-6634286/darwinbuild/darwinbuild.in:632-650
/branches/PR-6688645/darwinbuild/darwinbuild.in:479-490
/branches/PR-6722857/darwinbuild/darwinbuild.in:495-499
/branches/PR-6729491/darwinbuild/darwinbuild.in:655-664
/branches/PR-7250612/darwinbuild/darwinbuild.in:635-650
/branches/PR-7341154/darwinbuild/darwinbuild.in:682-694
/branches/PR-7431723/darwinbuild/darwinbuild.in:660-664
/branches/PR-7461534/darwinbuild/darwinbuild.in:650-664
/branches/PR-7489777/darwinbuild/darwinbuild.in:676-731
/branches/PR-7529688/darwinbuild/darwinbuild.in:692-694
/branches/PR-7598640/darwinbuild/darwinbuild.in:703-731
/trunk/darwinbuild/darwinbuild:432-434


Property changes on: trunk/darwinbuild/darwinmaster.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/darwinmaster.sh:399-419
/branches/PR-6358021/darwinbuild/darwinmaster.sh:442-443
/branches/PR-6392966/darwinbuild/darwinmaster.sh:423-427
/branches/PR-6398060/darwinbuild/darwinmaster.sh:433-434
/branches/PR-6493844/darwinbuild/darwinmaster.sh.in:460-461
/branches/PR-6497694/darwinbuild/darwinmaster.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/darwinmaster.in:632-650
/branches/PR-6688645/darwinbuild/darwinmaster.sh.in:479-490
/branches/PR-6722857/darwinbuild/darwinmaster.sh.in:495-499
/branches/PR-6729491/darwinbuild/darwinmaster.in:655-664
/branches/PR-7250612/darwinbuild/darwinmaster.in:635-650
/branches/PR-7341154/darwinbuild/darwinmaster.in:682-694
/branches/PR-7431723/darwinbuild/darwinmaster.in:660-664
/branches/PR-7461534/darwinbuild/darwinmaster.in:650-664
/branches/PR-7529688/darwinbuild/darwinmaster.in:692-694
/trunk/darwinbuild/darwinmaster.sh:432-434
   + /branches/PR-4841388/darwinbuild/darwinmaster.sh:399-419
/branches/PR-6358021/darwinbuild/darwinmaster.sh:442-443
/branches/PR-6392966/darwinbuild/darwinmaster.sh:423-427
/branches/PR-6398060/darwinbuild/darwinmaster.sh:433-434
/branches/PR-6493844/darwinbuild/darwinmaster.sh.in:460-461
/branches/PR-6497694/darwinbuild/darwinmaster.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/darwinmaster.in:632-650
/branches/PR-6688645/darwinbuild/darwinmaster.sh.in:479-490
/branches/PR-6722857/darwinbuild/darwinmaster.sh.in:495-499
/branches/PR-6729491/darwinbuild/darwinmaster.in:655-664
/branches/PR-7250612/darwinbuild/darwinmaster.in:635-650
/branches/PR-7341154/darwinbuild/darwinmaster.in:682-694
/branches/PR-7431723/darwinbuild/darwinmaster.in:660-664
/branches/PR-7461534/darwinbuild/darwinmaster.in:650-664
/branches/PR-7489777/darwinbuild/darwinmaster.in:676-731
/branches/PR-7529688/darwinbuild/darwinmaster.in:692-694
/branches/PR-7598640/darwinbuild/darwinmaster.in:703-731
/trunk/darwinbuild/darwinmaster.sh:432-434


Property changes on: trunk/darwinbuild/installXcode.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/installXcode:399-419
/branches/PR-6358021/darwinbuild/installXcode:442-443
/branches/PR-6392966/darwinbuild/installXcode:423-427
/branches/PR-6398060/darwinbuild/installXcode:433-434
/branches/PR-6493844/darwinbuild/installXcode.in:460-461
/branches/PR-6497694/darwinbuild/installXcode.in:466-468,471
/branches/PR-6634286/darwinbuild/installXcode.in:632-650
/branches/PR-6688645/darwinbuild/installXcode.in:479-490
/branches/PR-6722857/darwinbuild/installXcode.in:495-499
/branches/PR-6729491/darwinbuild/installXcode.in:655-664
/branches/PR-7250612/darwinbuild/installXcode.in:635-650
/branches/PR-7341154/darwinbuild/installXcode.in:682-694
/branches/PR-7431723/darwinbuild/installXcode.in:660-664
/branches/PR-7461534/darwinbuild/installXcode.in:650-664
/branches/PR-7529688/darwinbuild/installXcode.in:692-694
/trunk/darwinbuild/installXcode:432-434
   + /branches/PR-4841388/darwinbuild/installXcode:399-419
/branches/PR-6358021/darwinbuild/installXcode:442-443
/branches/PR-6392966/darwinbuild/installXcode:423-427
/branches/PR-6398060/darwinbuild/installXcode:433-434
/branches/PR-6493844/darwinbuild/installXcode.in:460-461
/branches/PR-6497694/darwinbuild/installXcode.in:466-468,471
/branches/PR-6634286/darwinbuild/installXcode.in:632-650
/branches/PR-6688645/darwinbuild/installXcode.in:479-490
/branches/PR-6722857/darwinbuild/installXcode.in:495-499
/branches/PR-6729491/darwinbuild/installXcode.in:655-664
/branches/PR-7250612/darwinbuild/installXcode.in:635-650
/branches/PR-7341154/darwinbuild/installXcode.in:682-694
/branches/PR-7431723/darwinbuild/installXcode.in:660-664
/branches/PR-7461534/darwinbuild/installXcode.in:650-664
/branches/PR-7489777/darwinbuild/installXcode.in:676-731
/branches/PR-7529688/darwinbuild/installXcode.in:692-694
/branches/PR-7598640/darwinbuild/installXcode.in:703-731
/trunk/darwinbuild/installXcode:432-434


Property changes on: trunk/darwinbuild/packageRoots.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/packageRoots.sh:399-419
/branches/PR-6358021/darwinbuild/packageRoots.sh:442-443
/branches/PR-6392966/darwinbuild/packageRoots.sh:423-427
/branches/PR-6398060/darwinbuild/packageRoots.sh:433-434
/branches/PR-6493844/darwinbuild/packageRoots.sh.in:460-461
/branches/PR-6497694/darwinbuild/packageRoots.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/packageRoots.in:632-650
/branches/PR-6688645/darwinbuild/packageRoots.sh.in:479-490
/branches/PR-6722857/darwinbuild/packageRoots.sh.in:495-499
/branches/PR-6729491/darwinbuild/packageRoots.in:655-664
/branches/PR-7250612/darwinbuild/packageRoots.in:635-650
/branches/PR-7341154/darwinbuild/packageRoots.in:682-694
/branches/PR-7431723/darwinbuild/packageRoots.in:660-664
/branches/PR-7461534/darwinbuild/packageRoots.in:650-664
/branches/PR-7529688/darwinbuild/packageRoots.in:692-694
/trunk/darwinbuild/packageRoots.sh:432-434
   + /branches/PR-4841388/darwinbuild/packageRoots.sh:399-419
/branches/PR-6358021/darwinbuild/packageRoots.sh:442-443
/branches/PR-6392966/darwinbuild/packageRoots.sh:423-427
/branches/PR-6398060/darwinbuild/packageRoots.sh:433-434
/branches/PR-6493844/darwinbuild/packageRoots.sh.in:460-461
/branches/PR-6497694/darwinbuild/packageRoots.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/packageRoots.in:632-650
/branches/PR-6688645/darwinbuild/packageRoots.sh.in:479-490
/branches/PR-6722857/darwinbuild/packageRoots.sh.in:495-499
/branches/PR-6729491/darwinbuild/packageRoots.in:655-664
/branches/PR-7250612/darwinbuild/packageRoots.in:635-650
/branches/PR-7341154/darwinbuild/packageRoots.in:682-694
/branches/PR-7431723/darwinbuild/packageRoots.in:660-664
/branches/PR-7461534/darwinbuild/packageRoots.in:650-664
/branches/PR-7489777/darwinbuild/packageRoots.in:676-731
/branches/PR-7529688/darwinbuild/packageRoots.in:692-694
/branches/PR-7598640/darwinbuild/packageRoots.in:703-731
/trunk/darwinbuild/packageRoots.sh:432-434


Property changes on: trunk/darwinbuild/thinPackages.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/thinPackages.sh:399-419
/branches/PR-6358021/darwinbuild/thinPackages.sh:442-443
/branches/PR-6392966/darwinbuild/thinPackages.sh:423-427
/branches/PR-6398060/darwinbuild/thinPackages.sh:433-434
/branches/PR-6493844/darwinbuild/thinPackages.sh.in:460-461
/branches/PR-6497694/darwinbuild/thinPackages.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/thinPackages.in:632-650
/branches/PR-6688645/darwinbuild/thinPackages.sh.in:479-490
/branches/PR-6722857/darwinbuild/thinPackages.sh.in:495-499
/branches/PR-6729491/darwinbuild/thinPackages.in:655-664
/branches/PR-7250612/darwinbuild/thinPackages.in:635-650
/branches/PR-7341154/darwinbuild/thinPackages.in:682-694
/branches/PR-7431723/darwinbuild/thinPackages.in:660-664
/branches/PR-7461534/darwinbuild/thinPackages.in:650-664
/branches/PR-7529688/darwinbuild/thinPackages.in:692-694
/trunk/darwinbuild/thinPackages.sh:432-434
   + /branches/PR-4841388/darwinbuild/thinPackages.sh:399-419
/branches/PR-6358021/darwinbuild/thinPackages.sh:442-443
/branches/PR-6392966/darwinbuild/thinPackages.sh:423-427
/branches/PR-6398060/darwinbuild/thinPackages.sh:433-434
/branches/PR-6493844/darwinbuild/thinPackages.sh.in:460-461
/branches/PR-6497694/darwinbuild/thinPackages.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/thinPackages.in:632-650
/branches/PR-6688645/darwinbuild/thinPackages.sh.in:479-490
/branches/PR-6722857/darwinbuild/thinPackages.sh.in:495-499
/branches/PR-6729491/darwinbuild/thinPackages.in:655-664
/branches/PR-7250612/darwinbuild/thinPackages.in:635-650
/branches/PR-7341154/darwinbuild/thinPackages.in:682-694
/branches/PR-7431723/darwinbuild/thinPackages.in:660-664
/branches/PR-7461534/darwinbuild/thinPackages.in:650-664
/branches/PR-7489777/darwinbuild/thinPackages.in:676-731
/branches/PR-7529688/darwinbuild/thinPackages.in:692-694
/branches/PR-7598640/darwinbuild/thinPackages.in:703-731
/trunk/darwinbuild/thinPackages.sh:432-434

Modified: trunk/darwinbuild.xcodeproj/project.pbxproj
===================================================================
--- trunk/darwinbuild.xcodeproj/project.pbxproj	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinbuild.xcodeproj/project.pbxproj	2010-03-03 21:37:30 UTC (rev 732)
@@ -163,6 +163,10 @@
 		72C86C9F109745BC00C66E90 /* Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 72C86BE610965E4F00C66E90 /* Utils.cpp */; };
 		72C86CE210974CC800C66E90 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72C86CE110974CC700C66E90 /* libcrypto.dylib */; };
 		72C86CE410974CC800C66E90 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72C86CE310974CC800C66E90 /* libsqlite3.dylib */; };
+		DF12E2821119E2B0007587C1 /* DB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF12E2811119E2B0007587C1 /* DB.cpp */; };
+		DFC9772D11138F9400CAE084 /* Column.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC9772711138F9400CAE084 /* Column.cpp */; };
+		DFC9772E11138F9400CAE084 /* Database.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC9772911138F9400CAE084 /* Database.cpp */; };
+		DFC9772F11138F9400CAE084 /* Table.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC9772B11138F9400CAE084 /* Table.cpp */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -741,6 +745,14 @@
 		72C86CDD10974C3A00C66E90 /* libredo_prebinding.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libredo_prebinding.a; path = /usr/local/lib/libredo_prebinding.a; sourceTree = "<absolute>"; };
 		72C86CE110974CC700C66E90 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = /usr/lib/libcrypto.dylib; sourceTree = "<absolute>"; };
 		72C86CE310974CC800C66E90 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = /usr/lib/libsqlite3.dylib; sourceTree = "<absolute>"; };
+		DF12E2801119E2B0007587C1 /* DB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DB.h; path = darwinup/DB.h; sourceTree = "<group>"; };
+		DF12E2811119E2B0007587C1 /* DB.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DB.cpp; path = darwinup/DB.cpp; sourceTree = "<group>"; };
+		DFC9772711138F9400CAE084 /* Column.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Column.cpp; path = darwinup/Column.cpp; sourceTree = "<group>"; };
+		DFC9772811138F9400CAE084 /* Column.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Column.h; path = darwinup/Column.h; sourceTree = "<group>"; };
+		DFC9772911138F9400CAE084 /* Database.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Database.cpp; path = darwinup/Database.cpp; sourceTree = "<group>"; };
+		DFC9772A11138F9400CAE084 /* Database.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Database.h; path = darwinup/Database.h; sourceTree = "<group>"; };
+		DFC9772B11138F9400CAE084 /* Table.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Table.cpp; path = darwinup/Table.cpp; sourceTree = "<group>"; };
+		DFC9772C11138F9400CAE084 /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Table.h; path = darwinup/Table.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -1006,6 +1018,12 @@
 		72C86BD710965DD800C66E90 /* darwinup */ = {
 			isa = PBXGroup;
 			children = (
+				DFC9772711138F9400CAE084 /* Column.cpp */,
+				DFC9772811138F9400CAE084 /* Column.h */,
+				DFC9772911138F9400CAE084 /* Database.cpp */,
+				DFC9772A11138F9400CAE084 /* Database.h */,
+				DFC9772B11138F9400CAE084 /* Table.cpp */,
+				DFC9772C11138F9400CAE084 /* Table.h */,
 				72C86BDA10965E4F00C66E90 /* Archive.cpp */,
 				72C86BDB10965E4F00C66E90 /* Archive.h */,
 				72C86BDC10965E4F00C66E90 /* Depot.cpp */,
@@ -1020,6 +1038,8 @@
 				72C86BE510965E4F00C66E90 /* SerialSet.h */,
 				72C86BE610965E4F00C66E90 /* Utils.cpp */,
 				72C86BE710965E4F00C66E90 /* Utils.h */,
+				DF12E2801119E2B0007587C1 /* DB.h */,
+				DF12E2811119E2B0007587C1 /* DB.cpp */,
 			);
 			name = darwinup;
 			sourceTree = "<group>";
@@ -2158,6 +2178,10 @@
 				72C86C9D109745BC00C66E90 /* main.cpp in Sources */,
 				72C86C9E109745BC00C66E90 /* SerialSet.cpp in Sources */,
 				72C86C9F109745BC00C66E90 /* Utils.cpp in Sources */,
+				DFC9772D11138F9400CAE084 /* Column.cpp in Sources */,
+				DFC9772E11138F9400CAE084 /* Database.cpp in Sources */,
+				DFC9772F11138F9400CAE084 /* Table.cpp in Sources */,
+				DF12E2821119E2B0007587C1 /* DB.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

Modified: trunk/darwintrace/darwintrace.c
===================================================================
--- trunk/darwintrace/darwintrace.c	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwintrace/darwintrace.c	2010-03-03 21:37:30 UTC (rev 732)
@@ -76,6 +76,7 @@
 					      "/Volumes/BuildRoot_",
 					      "/usr/bin/xcrun",
 					      "/usr/bin/xcode",
+					      "/usr/local/share/darwin",
 					      "/usr/share/xcode",
 					      "/var/folders/",
 					      "/var/tmp/",

Modified: trunk/darwinup/Archive.cpp
===================================================================
--- trunk/darwinup/Archive.cpp	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/Archive.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -89,7 +89,6 @@
 char* Archive::create_directory(const char* prefix) {
 	int res = 0;
 	char* path = this->directory_name(prefix);
-	IF_DEBUG("creating directory: %s\n", path);
 	if (path && res == 0) res = mkdir(path, 0777);
 	if (res != 0) {
 		fprintf(stderr, "%s:%d: could not create directory: %s: %s (%d)\n", __FILE__, __LINE__, path, strerror(errno), errno);
@@ -106,7 +105,6 @@
 	char uuidstr[37];
 	uuid_unparse_upper(m_uuid, uuidstr);
 	asprintf(&tarpath, "%s/%s.tar.bz2", prefix, uuidstr);
-	IF_DEBUG("compacting %s/%s to %s\n", prefix, uuidstr, tarpath);
 	if (tarpath) {
 		const char* args[] = {
 			"/usr/bin/tar",
@@ -130,7 +128,6 @@
 	char uuidstr[37];
 	uuid_unparse_upper(m_uuid, uuidstr);
 	asprintf(&tarpath, "%s/%s.tar.bz2", prefix, uuidstr);
-	IF_DEBUG("expanding %s to %s\n", tarpath, prefix);
 	if (tarpath) {
 		const char* args[] = {
 			"/usr/bin/tar",

Modified: trunk/darwinup/Archive.h
===================================================================
--- trunk/darwinup/Archive.h	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/Archive.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -30,13 +30,23 @@
  * @APPLE_BSD_LICENSE_HEADER_END@
  */
 
+#ifndef _ARCHIVE_H
+#define _ARCHIVE_H
+
 #include <stdint.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <time.h>
 #include <uuid/uuid.h>
 
+typedef char* archive_name_t;
 
+enum archive_keyword_t {
+	DEPOT_ARCHIVE_NEWEST,
+	DEPOT_ARCHIVE_OLDEST
+};
+
+
 //
 // ARCHIVE_INFO flags stored in the database
 //
@@ -131,6 +141,7 @@
 	time_t		m_date_installed;
 	
 	friend struct Depot;
+	friend struct DarwinupDatabase;
 };
 
 
@@ -301,3 +312,6 @@
 	ZipArchive(const char* path);
 	virtual int extract(const char* destdir);
 };
+
+#endif
+

Copied: trunk/darwinup/Column.cpp (from rev 731, branches/PR-7489777/darwinup/Column.cpp)
===================================================================
--- trunk/darwinup/Column.cpp	                        (rev 0)
+++ trunk/darwinup/Column.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_START@
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "Column.h"
+
+Column::Column(const char* name, uint32_t type) {
+	m_name       = strdup(name);
+	m_create_sql = NULL;
+	m_type       = type;
+	m_is_index   = false;
+	m_is_pk      = false;
+	m_is_unique  = false;
+}
+
+Column::Column(const char* name, uint32_t type, 
+			   bool is_index, bool is_pk, bool is_unique) {
+	m_name       = strdup(name);
+	m_create_sql = NULL;
+	m_type       = type;
+	m_is_index   = is_index;
+	m_is_pk      = is_pk;
+	m_is_unique  = is_unique;
+}
+
+Column::~Column() {
+	free(m_name);
+	free(m_create_sql);
+}
+
+const char* Column::name() {
+	return m_name;
+}
+
+uint32_t Column::type() {
+	return m_type;
+}
+
+const bool Column::is_index() {
+	return m_is_index;
+}
+
+const bool Column::is_pk() {
+	return m_is_pk;
+}
+
+const bool Column::is_unique() {
+	return m_is_unique;
+}
+
+
+const char* Column::typestr() {
+	switch(m_type) {
+		case SQLITE_INTEGER:
+			return "INTEGER";
+			break;
+		case SQLITE_TEXT:
+			return "TEXT";
+			break;
+		case SQLITE_BLOB:
+			return "BLOB";
+			break;
+		default:
+			fprintf(stderr, "Error: unknown column type: %d \n", m_type);
+			return "UNKNOWN";
+	}
+}
+
+uint32_t Column::size() {
+	// integers are stored inband in the record
+	if (m_type == SQLITE_INTEGER) return sizeof(uint64_t);
+	// everything else is stored out of band
+	return sizeof(void*);
+}
+
+int Column::offset() {
+	return m_offset;
+}
+
+const char* Column::create() {
+	if (!m_create_sql) {
+		asprintf(&m_create_sql, "%s %s%s%s", m_name, this->typestr(),
+				(this->is_pk() ? " PRIMARY KEY AUTOINCREMENT" : ""),
+				(this->is_unique() ? " UNIQUE" : ""));
+	}
+	return (const char*)m_create_sql;
+}

Copied: trunk/darwinup/Column.h (from rev 731, branches/PR-7489777/darwinup/Column.h)
===================================================================
--- trunk/darwinup/Column.h	                        (rev 0)
+++ trunk/darwinup/Column.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_START@
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_END@
+ */
+
+#ifndef _COLUMN_H
+#define _COLUMN_H
+
+#include <stdint.h>
+#include <sqlite3.h>
+
+#include "Utils.h"
+
+/**
+ * Column objects represent a column in a database table. They store
+ *  type information and chunks of sql for their Table to build
+ *  queries with. 
+ */
+struct Column {
+	Column(const char* name, uint32_t type);
+	Column(const char* name, uint32_t type,
+		   bool is_index, bool is_pk, bool is_unique);
+	virtual ~Column();
+	
+	const char*    name();
+	uint32_t       type();
+	const bool     is_index();
+	const bool     is_pk();
+	const bool     is_unique();
+
+	// return size of this column when packed into a result record
+	uint32_t       size();
+
+protected:
+	// return a string representation  of this columns type suitable 
+	//  for sql queries
+	const char*    typestr();
+	
+	// return the offset of this column in the Table's result record
+	int            offset();
+	
+	// generate the sql needed to create this column
+	const char*    create();
+
+	char*          m_name;
+	char*          m_create_sql;
+	uint32_t       m_type; // SQLITE_* type definition
+	bool           m_is_index;
+	bool           m_is_pk;
+	bool           m_is_unique;
+	int            m_offset;
+	
+	friend struct Table;
+};
+
+#endif

Copied: trunk/darwinup/DB.cpp (from rev 731, branches/PR-7489777/darwinup/DB.cpp)
===================================================================
--- trunk/darwinup/DB.cpp	                        (rev 0)
+++ trunk/darwinup/DB.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_START@
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_END@
+ */
+
+#include "DB.h"
+
+
+DarwinupDatabase::DarwinupDatabase(const char* path) : Database(path) {
+	this->connect();
+}
+
+DarwinupDatabase::~DarwinupDatabase() {
+	// parent automatically deallocates schema objects
+}
+
+void DarwinupDatabase::init_schema() {
+	this->m_archives_table = new Table("archives");
+	ADD_PK(m_archives_table, "serial");
+	ADD_INDEX(m_archives_table, "uuid", TYPE_BLOB, true); 
+	ADD_TEXT(m_archives_table, "name");
+	ADD_INTEGER(m_archives_table, "date_added");
+	ADD_INTEGER(m_archives_table, "active");
+	ADD_INTEGER(m_archives_table, "info");	
+	assert(this->add_table(this->m_archives_table)==0);
+	
+	this->m_files_table = new Table("files");
+	ADD_PK(m_files_table, "serial");
+	ADD_INDEX(m_files_table, "archive", TYPE_INTEGER, false);
+	ADD_INTEGER(m_files_table, "info");
+	ADD_INTEGER(m_files_table, "mode");
+	ADD_INTEGER(m_files_table, "uid");
+	ADD_INTEGER(m_files_table, "gid");
+	ADD_INTEGER(m_files_table, "size");
+	ADD_BLOB(m_files_table, "digest");
+	ADD_INDEX(m_files_table, "path", TYPE_TEXT, false);
+	// custom index to protect from duplicate files
+	assert(this->m_files_table->set_custom_create("CREATE UNIQUE INDEX files_archive_path " 
+												  "ON files (archive, path);") == 0);
+	assert(this->add_table(this->m_files_table)==0);
+}
+
+int DarwinupDatabase::activate_archive(uint64_t serial) {
+	uint64_t active = 1;
+	return this->set_archive_active(serial, &active);
+}
+
+int DarwinupDatabase::deactivate_archive(uint64_t serial) {
+	uint64_t active = 0;
+	return this->set_archive_active(serial, &active);
+}
+
+int DarwinupDatabase::set_archive_active(uint64_t serial, uint64_t* active) {
+	return this->update_value("activate_archive", 
+							  this->m_archives_table,
+							  this->m_archives_table->column(4), // active
+							  (void**)active,
+							  1,                                 // number of where conditions
+							  this->m_archives_table->column(0), // serial
+							  '=', serial);
+}
+
+int DarwinupDatabase::update_archive(uint64_t serial, uuid_t uuid, const char* name,
+									  time_t date_added, uint32_t active, uint32_t info) {
+	return this->update(this->m_archives_table, serial,
+						(uint8_t*)uuid,
+						(uint32_t)sizeof(uuid_t),
+						name,
+						(uint64_t)date_added,
+						(uint64_t)active,
+						(uint64_t)info);
+}
+
+uint64_t DarwinupDatabase::insert_archive(uuid_t uuid, uint32_t info, const char* name, 
+										  time_t date_added) {
+	
+	int res = this->insert(this->m_archives_table,
+						   (uint8_t*)uuid,
+						   (uint32_t)sizeof(uuid_t),
+						   name,
+						   (uint64_t)date_added,
+						   (uint64_t)0,
+						   (uint64_t)info);
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: unable to insert archive %s: %s \n",
+				name, this->error());
+		return 0;
+	}
+	
+	return this->last_insert_id();
+}
+
+File* DarwinupDatabase::make_file(uint8_t* data) {
+	// XXX do this with a for loop and column->type()
+	uint64_t serial;
+	memcpy(&serial, &data[this->file_offset(0)], sizeof(uint64_t));
+	uint64_t archive_serial;
+	memcpy(&archive_serial, &data[this->file_offset(1)], sizeof(uint64_t));
+	uint64_t info;
+	memcpy(&info, &data[this->file_offset(2)], sizeof(uint64_t));
+	uint64_t mode;
+	memcpy(&mode, &data[this->file_offset(3)], sizeof(uint64_t));
+	uint64_t uid;
+	memcpy(&uid, &data[this->file_offset(4)], sizeof(uint64_t));
+	uint64_t gid;
+	memcpy(&gid, &data[this->file_offset(5)], sizeof(uint64_t));
+	uint64_t size;
+	memcpy(&size, &data[this->file_offset(6)], sizeof(uint64_t));
+
+	Digest* digest = NULL;
+	uint8_t* dp;
+	memcpy(&dp, &data[this->file_offset(7)], sizeof(uint8_t*));
+	if (dp) {
+		digest = new Digest();
+		digest->m_size = 20; // size of SHA1 hash
+		memcpy(digest->m_data, dp, 20);
+	}
+	
+	char* path;
+	memcpy(&path, &data[this->file_offset(8)], sizeof(char*));
+	
+	uint8_t* archive_data;
+	int res = this->get_archive(&archive_data, archive_serial);
+	Archive* archive = NULL;
+	if (FOUND(res)) {
+		archive = this->make_archive(archive_data);
+	} else {
+		fprintf(stderr, "Error: DB::make_file could not find the archive for file: %s: %d \n", path, res);
+		return NULL;
+	}
+	this->m_archives_table->free_result(archive_data);
+	
+	File* result = FileFactory(serial, archive, info, (const char*)path, mode, uid, gid, size, digest);
+	this->m_files_table->free_result(data);
+	
+	return result;
+}
+
+
+
+int DarwinupDatabase::get_next_file(uint8_t** data, File* file, file_starseded_t star) {
+	int res = SQLITE_OK;
+	
+	char comp = '<';
+	const char* name = "file_preceded";
+	int order = ORDER_BY_DESC;
+	if (star == FILE_SUPERSEDED) {
+		comp = '>';
+		name = "file_superseded";
+		order = ORDER_BY_ASC;
+	}
+	res = this->get_row_ordered(name,
+								data,
+								this->m_files_table,
+								this->m_files_table->column(1), // order by archive
+								order,
+								2,
+								this->m_files_table->column(1), // archive
+								comp, file->archive()->serial(),
+								this->m_files_table->column(8), // path
+								'=', file->path());
+	
+	if (res == SQLITE_ROW) return (DB_FOUND | DB_OK);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;
+}
+
+int DarwinupDatabase::get_file_serial_from_archive(Archive* archive, const char* path, uint64_t** serial) {
+	int res = this->get_value("file_serial__archive_path",
+							  (void**)serial,
+							  this->m_files_table,
+							  this->m_files_table->column(0), // serial
+							  2,                              // number of where conditions
+							  this->m_files_table->column(1), // archive
+							  '=', (uint64_t)archive->serial(),
+							  this->m_files_table->column(8), // path
+							  '=', path);
+	
+	if (res == SQLITE_ROW) return (DB_FOUND | DB_OK);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;
+}
+
+int DarwinupDatabase::update_file(uint64_t serial, Archive* archive, uint32_t info, mode_t mode, 
+								   uid_t uid, gid_t gid, Digest* digest, const char* path) {
+
+	int res = SQLITE_OK;
+								  
+	// update the information
+	res = this->update(this->m_files_table, serial,
+					   (uint64_t)archive->serial(),
+					   (uint64_t)info,
+					   (uint64_t)mode,
+					   (uint64_t)uid,
+					   (uint64_t)gid,
+					   (uint64_t)0, 
+					   (uint8_t*)(digest ? digest->data() : NULL), 
+					   (uint32_t)(digest ? digest->size() : 0), 
+					   path);
+
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: unable to update file with serial %llu and path %s: %s \n",
+				serial, path, this->error());
+	}
+	
+	return res;
+}
+										  
+uint64_t DarwinupDatabase::insert_file(uint32_t info, mode_t mode, uid_t uid, gid_t gid, 
+									   Digest* digest, Archive* archive, const char* path) {
+	
+	int res = this->insert(this->m_files_table,
+							(uint64_t)archive->serial(),
+							(uint64_t)info,
+							(uint64_t)mode,
+							(uint64_t)uid,
+							(uint64_t)gid,
+							(uint64_t)0, 
+							(uint8_t*)(digest ? digest->data() : NULL), 
+							(uint32_t)(digest ? digest->size() : 0), 
+							path);
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: unable to insert file at %s: %s \n",
+				path, this->error());
+		return 0;
+	}
+	
+	return this->last_insert_id();
+}
+
+uint64_t DarwinupDatabase::count_files(Archive* archive, const char* path) {
+	int res = SQLITE_OK;
+	uint64_t* c;
+	res = this->count("count_files",
+					  (void**)&c,
+					  this->m_files_table,
+					  2,                              // number of where conditions
+					  this->m_files_table->column(1), // archive
+					  '=', (uint64_t)archive->serial(),
+					  this->m_files_table->column(8), // path
+					  '=', path);	
+	if (res != SQLITE_ROW) {
+		fprintf(stderr, "Error: unable to count files: %d \n", res);
+		return 0;
+	}
+	return *c;
+}
+
+uint64_t DarwinupDatabase::count_archives(bool include_rollbacks) {
+	int res = SQLITE_OK;
+	uint64_t* c;
+	if (include_rollbacks) {
+		res = this->count("count_archives",
+						  (void**)&c,
+						  this->m_archives_table, 0);				
+	} else {
+		res = this->count("count_archives_norollback",
+						  (void**)&c,
+						  this->m_archives_table,
+						  1,
+						  this->m_archives_table->column(2), // name
+						  '!', "<Rollback>");		
+	}
+	if (res != SQLITE_ROW) {
+		fprintf(stderr, "Error: unable to count archives: %d \n", res);
+		return 0;
+	}	
+	return *c;	
+}
+
+int DarwinupDatabase::delete_archive(Archive* archive) {
+	int res = this->del(this->m_archives_table, archive->serial());
+	if (res != SQLITE_OK) return DB_ERROR;
+	return DB_OK;
+}
+
+int DarwinupDatabase::delete_archive(uint64_t serial) {
+	int res = this->del(this->m_archives_table, serial);
+	if (res != SQLITE_OK) return DB_ERROR;
+	return DB_OK;
+}
+
+int DarwinupDatabase::delete_empty_archives() {
+	int res = this->sql("delete_empty_archives", 
+						"DELETE FROM archives "
+						"WHERE serial IN "
+						" (SELECT serial FROM archives "
+						"  WHERE serial NOT IN "
+						"   (SELECT DISTINCT archive FROM files));");	
+	if (res != SQLITE_OK) return DB_ERROR;
+	return DB_OK;
+}
+
+int DarwinupDatabase::delete_file(File* file) {
+	int res = this->del(this->m_files_table, file->serial());
+	if (res != SQLITE_OK) return DB_ERROR;
+	return DB_OK;
+}
+
+int DarwinupDatabase::delete_file(uint64_t serial) {
+	int res = this->del(this->m_files_table, serial);
+	if (res != SQLITE_OK) return DB_ERROR;
+	return DB_OK;
+}
+
+int DarwinupDatabase::delete_files(Archive* archive) {
+	int res = this->del("delete_files__archive",
+						this->m_files_table,
+						1,                               // number of where conditions
+						this->m_files_table->column(1),  // archive
+						'=', (uint64_t)archive->serial());
+	if (res != SQLITE_OK) return DB_ERROR;
+	return DB_OK;
+}
+
+
+int DarwinupDatabase::get_inactive_archive_serials(uint64_t** serials, uint32_t* count) {
+	int res = this->get_column("inactive_archive_serials",
+							   (void**)serials, count,
+							   this->m_archives_table,
+							   this->m_archives_table->column(0), // serial
+							   1,
+							   this->m_archives_table->column(4), // active
+							   '=', (uint64_t)0);
+	if (res == SQLITE_DONE && *count) return (DB_OK | DB_FOUND);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;
+}
+
+int DarwinupDatabase::get_files(uint8_t*** data, uint32_t* count, Archive* archive) {
+	int res = this->get_all_ordered("files__archive",
+									data, count,
+									this->m_files_table,
+									this->m_files_table->column(8), // order by path
+									ORDER_BY_ASC,
+									1,
+									this->m_files_table->column(1),
+									'=', archive->serial());
+	
+	if ((res == SQLITE_DONE) && *count) return (DB_OK | DB_FOUND);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;
+}
+
+int DarwinupDatabase::get_file_serials(uint64_t** serials, uint32_t* count) {
+	int res = this->get_column("file_serials", (void**)serials, count, 
+							   this->m_files_table,
+							   this->m_files_table->column(0),
+							   0);
+	if (res == SQLITE_DONE && *count) return (DB_OK | DB_FOUND);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;	
+}
+
+
+Archive* DarwinupDatabase::make_archive(uint8_t* data) {
+	// XXX do this with a for loop and column->type()	
+	uint64_t serial;
+	memcpy(&serial, &data[this->archive_offset(0)], sizeof(uint64_t));
+	uuid_t* uuid;
+	memcpy(&uuid, &data[this->archive_offset(1)], sizeof(uuid_t*));
+	char* name;
+	memcpy(&name, &data[this->archive_offset(2)], sizeof(char*));
+	time_t date_added;
+	memcpy(&date_added, &data[this->archive_offset(3)], sizeof(time_t));
+	uint64_t info;
+	memcpy(&info, &data[this->archive_offset(5)], sizeof(uint64_t));
+
+	Archive* archive = new Archive(serial, *uuid, name, NULL, info, date_added);
+	this->m_archives_table->free_result(data);
+	return archive;
+}
+
+int DarwinupDatabase::get_archives(uint8_t*** data, uint32_t* count, bool include_rollbacks) {
+	int res = this->get_all_ordered("get_archives",
+									data, count,
+									this->m_archives_table,
+									this->m_archives_table->column(0), // order by path
+									ORDER_BY_DESC,
+									1,
+									this->m_archives_table->column(2),  // name
+									'!', (include_rollbacks ? "" : "<Rollback>") );
+	
+	if ((res == SQLITE_DONE) && *count) return (DB_OK | DB_FOUND);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;	
+}
+
+int DarwinupDatabase::get_archive(uint8_t** data, uuid_t uuid) {
+	int res = this->get_row("archive__uuid",
+							data,
+							this->m_archives_table,
+							1,
+							this->m_archives_table->column(1), // uuid
+							'=', uuid, sizeof(uuid_t));
+	if (res == SQLITE_ROW) return (DB_FOUND | DB_OK);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;	
+}
+
+int DarwinupDatabase::get_archive(uint8_t** data, uint64_t serial) {
+	int res = this->get_row("archive__serial",
+							data,
+							this->m_archives_table,
+							1,
+							this->m_archives_table->column(0), // serial
+							'=', serial);
+	if (res == SQLITE_ROW) return (DB_FOUND | DB_OK);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;	
+}
+
+int DarwinupDatabase::get_archive(uint8_t** data, const char* name) {
+	int res = this->get_row("archive__name",
+							data,
+							this->m_archives_table,
+							1,
+							this->m_archives_table->column(2), // name
+							'=', name);
+	if (res == SQLITE_ROW) return (DB_FOUND | DB_OK);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;	
+}
+
+int DarwinupDatabase::get_archive(uint8_t** data, archive_keyword_t keyword) {
+	int res = SQLITE_OK;
+	int order = ORDER_BY_DESC;
+
+	if (keyword == DEPOT_ARCHIVE_OLDEST) {
+		order = ORDER_BY_ASC;
+	}
+	
+	res = this->get_row_ordered("archive__keyword",
+								data,
+								this->m_archives_table,
+								this->m_archives_table->column(3), // order by date_added
+								order,
+								1,
+								this->m_archives_table->column(2), // name
+								'!', "<Rollback>");
+	
+	if (res == SQLITE_ROW) return (DB_FOUND | DB_OK);
+	if (res == SQLITE_DONE) return DB_OK;
+	return DB_ERROR;	
+}
+
+int DarwinupDatabase::archive_offset(int column) {
+	return this->m_archives_table->offset(column);
+}
+
+int DarwinupDatabase::file_offset(int column) {
+	return this->m_files_table->offset(column);
+}

Copied: trunk/darwinup/DB.h (from rev 731, branches/PR-7489777/darwinup/DB.h)
===================================================================
--- trunk/darwinup/DB.h	                        (rev 0)
+++ trunk/darwinup/DB.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_START@
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_END@
+ */
+
+#ifndef _DB_H
+#define _DB_H
+
+#include <stdint.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <time.h>
+
+#include "Database.h"
+#include "Table.h"
+#include "Archive.h"
+#include "Digest.h"
+#include "File.h"
+
+
+/**
+ *
+ * Darwinup database abstraction. This class is responsible
+ *  for generating the Table and Column objects that make
+ *  up the darwinup database schema, but the parent handles
+ *  deallocation. 
+ *
+ */
+struct DarwinupDatabase : Database {
+	DarwinupDatabase(const char* path);
+	virtual ~DarwinupDatabase();
+	void init_schema();
+	
+	uint64_t count_files(Archive* archive, const char* path);
+	uint64_t count_archives(bool include_rollbacks);
+	
+	// Archives
+	Archive* make_archive(uint8_t* data);
+	int      get_archives(uint8_t*** data, uint32_t* count, bool include_rollbacks);
+	int      get_archive(uint8_t** data, uuid_t uuid);
+	int      get_archive(uint8_t** data, uint64_t serial);
+	int      get_archive(uint8_t** data, const char* name);
+	int      get_archive(uint8_t** data, archive_keyword_t keyword);
+	int      get_inactive_archive_serials(uint64_t** serials, uint32_t* count);
+	int      archive_offset(int column);
+	int      activate_archive(uint64_t serial);
+	int      deactivate_archive(uint64_t serial);
+	int      update_archive(uint64_t serial, uuid_t uuid, const char* name,
+							time_t date_added, uint32_t active, uint32_t info);
+	uint64_t insert_archive(uuid_t uuid, uint32_t info, const char* name, time_t date);
+	int      delete_empty_archives();
+	int      delete_archive(Archive* archive);
+	int      delete_archive(uint64_t serial);
+
+	// Files
+	File*    make_file(uint8_t* data);
+	int      get_next_file(uint8_t** data, File* file, file_starseded_t star);
+	int      get_file_serials(uint64_t** serials, uint32_t* count);
+	int      get_file_serial_from_archive(Archive* archive, const char* path, uint64_t** serial);
+	int      get_files(uint8_t*** data, uint32_t* count, Archive* archive);
+	int      file_offset(int column);
+	int      update_file(uint64_t serial, Archive* archive, uint32_t info, mode_t mode, 
+						 uid_t uid, gid_t gid, Digest* digest, const char* path);
+	uint64_t insert_file(uint32_t info, mode_t mode, uid_t uid, gid_t gid, 
+						 Digest* digest, Archive* archive, const char* path);
+	int      delete_file(uint64_t serial);
+	int      delete_file(File* file);
+	int      delete_files(Archive* archive);
+	
+
+protected:
+	
+	int      set_archive_active(uint64_t serial, uint64_t* active);
+	
+	Table*        m_archives_table;
+	Table*        m_files_table;
+	
+};
+
+#endif
+

Copied: trunk/darwinup/Database.cpp (from rev 731, branches/PR-7489777/darwinup/Database.cpp)
===================================================================
--- trunk/darwinup/Database.cpp	                        (rev 0)
+++ trunk/darwinup/Database.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -0,0 +1,708 @@
+/*
+ * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_START@
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_END@
+ */
+
+#include "Database.h"
+
+/**
+ * sqlite3_trace callback for debugging
+ */
+void dbtrace(void* context, const char* sql) {
+	IF_DEBUG("[TRACE] %s \n", sql);
+}
+
+Database::Database() {
+	m_table_max = 2;
+	m_table_count = 0;
+	m_tables = (Table**)malloc(sizeof(Table*) * m_table_max);
+	this->init_cache();
+	m_db = NULL;	
+	m_path = NULL;
+	m_error_size = ERROR_BUF_SIZE;
+	m_error = (char*)malloc(m_error_size);
+}
+
+Database::Database(const char* path) {
+	m_table_max = 2;
+	m_table_count = 0;
+	m_tables = (Table**)malloc(sizeof(Table*) * m_table_max);
+	this->init_cache();
+	m_db = NULL;		
+	m_path = strdup(path);
+	if (!m_path) {
+		fprintf(stderr, "Error: ran out of memory when constructing "
+				        "database object.\n");
+	}
+	m_error_size = ERROR_BUF_SIZE;
+	m_error = (char*)malloc(m_error_size);
+}
+
+Database::~Database() {
+	for (uint32_t i = 0; i < m_table_count; i++) {
+		delete m_tables[i];
+	}
+	this->destroy_cache();
+	
+	sqlite3_finalize(m_begin_transaction);
+	sqlite3_finalize(m_rollback_transaction);
+	sqlite3_finalize(m_commit_transaction);
+
+	free(m_tables);
+	free(m_path);
+	free(m_error);
+}
+
+
+void Database::init_schema() {
+	// do nothing... children should implement this
+}
+
+const char* Database::path() {
+	return m_path;
+}
+
+const char* Database::error() {
+	return m_error;
+}
+
+int Database::connect() {
+	if (!m_path) {
+		fprintf(stderr, "Error: need to specify a path to Database.\n");
+		return -1;
+	}
+	int res = SQLITE_OK;
+	this->init_schema();
+	res = sqlite3_open(m_path, &m_db);
+	if (res) {
+		sqlite3_close(m_db);
+		m_db = NULL;
+		fprintf(stderr, "Error: unable to connect to database at: %s \n", 
+				m_path);
+		return res;
+	}	
+	sqlite3_trace(m_db, dbtrace, NULL);
+	if (this->is_empty()) {
+		assert(this->create_tables() == 0);
+	}
+	
+	// prepare transaction statements
+	if (res == SQLITE_OK) 
+		res = sqlite3_prepare_v2(m_db, "BEGIN TRANSACTION", 18,
+								 &m_begin_transaction, NULL);
+	if (res == SQLITE_OK) 
+		res = sqlite3_prepare_v2(m_db, "ROLLBACK TRANSACTION", 21,
+								 &m_rollback_transaction, NULL);
+	if (res == SQLITE_OK) 
+		res = sqlite3_prepare_v2(m_db, "COMMIT TRANSACTION", 19,
+								 &m_commit_transaction, NULL);
+	
+	return res;	
+}
+
+int Database::connect(const char* path) {
+	this->m_path = strdup(path);
+	if (!m_path) {
+		fprintf(stderr, "Error: ran out of memory when trying to connect to "
+				        "database.\n");
+		return 1;
+	}
+	return this->connect();
+}
+
+int Database::begin_transaction() {
+	return this->execute(m_begin_transaction);
+}
+
+int Database::rollback_transaction() {
+	return this->execute(m_rollback_transaction);
+}
+
+int Database::commit_transaction() {
+	return this->execute(m_commit_transaction);
+}
+
+int Database::bind_all_columns(sqlite3_stmt* stmt, Table* table, va_list args) {
+	int res = DB_OK;
+	int param = 1;
+	for (uint32_t i=0; i<table->column_count(); i++) {
+		Column* col = table->column(i);
+		if (col->is_pk()) continue;
+		uint8_t* bdata = NULL;
+		uint32_t bsize = 0;
+		switch(col->type()) {
+			case TYPE_INTEGER:
+				res = sqlite3_bind_int64(stmt, param++, va_arg(args, uint64_t));
+				break;
+			case TYPE_TEXT:
+				res = sqlite3_bind_text(stmt, param++, va_arg(args, char*), 
+										-1, SQLITE_STATIC);
+				break;
+			case TYPE_BLOB:
+				bdata = va_arg(args, uint8_t*);
+				bsize = va_arg(args, uint32_t);
+                res = sqlite3_bind_blob(stmt, param++,
+										bdata,
+										bsize,
+										SQLITE_STATIC);
+				break;
+		}
+		if (res != SQLITE_OK) {
+			fprintf(stderr, "Error: failed to bind parameter #%d with column #%d"
+					        "of type %d table %s \n",
+					param, i, col->type(), table->name());
+			return res;
+		}
+	}
+	return res;
+}
+
+int Database::bind_va_columns(sqlite3_stmt* stmt, uint32_t count, va_list args) {
+	return this->bind_columns(stmt, count, 1, args);
+}
+
+int Database::bind_columns(sqlite3_stmt* stmt, uint32_t count, int param, 
+						   va_list args) {	
+	int res = DB_OK;
+    for (uint32_t i=0; i<count; i++) {
+        Column* col = va_arg(args, Column*);
+        va_arg(args, int);
+        uint8_t* bdata = NULL;
+        uint32_t bsize = 0;
+        char* tval;
+        switch(col->type()) {
+            case TYPE_INTEGER:
+                res = sqlite3_bind_int64(stmt, param++, va_arg(args, uint64_t));
+                break;
+            case TYPE_TEXT:
+                tval = va_arg(args, char*);
+                res = sqlite3_bind_text(stmt, param++, tval, -1, SQLITE_STATIC);
+                break;
+            case TYPE_BLOB:
+                bdata = va_arg(args, uint8_t*);
+                bsize = va_arg(args, uint32_t);
+                res = sqlite3_bind_blob(stmt, param++,
+                                        bdata,
+										bsize,
+                                        SQLITE_STATIC);
+                break;
+		}
+        if (res != SQLITE_OK) {
+            fprintf(stderr, "Error: failed to bind parameter #%d with column #%d "
+					        "of type %d res %d: %s  \n",
+					param-1, i, col->type(), res, 
+					sqlite3_errmsg(m_db));
+            return res;
+        }
+    }
+	return res;
+}
+
+#define __get_stmt(expr) \
+	sqlite3_stmt* stmt; \
+    sqlite3_stmt** pps; \
+	char* key = strdup(name); \
+	cache_get_and_retain(m_statement_cache, key, (void**)&pps); \
+	if (!pps) { \
+		va_list args; \
+		va_start(args, count); \
+		pps = expr; \
+		va_end(args); \
+		cache_set_and_retain(m_statement_cache, key, pps, 0); \
+	} \
+    stmt = *pps; \
+	free(key);
+
+int Database::count(const char* name, void** output, Table* table, 
+					uint32_t count, ...) {
+	va_list args;
+	va_start(args, count);
+	__get_stmt(table->count(m_db, count, args));
+	int res = SQLITE_OK;
+	res = this->bind_va_columns(stmt, count, args);
+	*output = malloc(sizeof(uint64_t));
+	assert(*output);
+	res = this->step_once(stmt, *(uint8_t**)output, NULL);
+	sqlite3_reset(stmt);
+	cache_release_value(m_statement_cache, pps);
+	va_end(args);
+	return res;
+}
+
+int Database::get_value(const char* name, void** output, Table* table, 
+						Column* value_column, uint32_t count, ...) {
+	va_list args;
+	va_start(args, count);
+	__get_stmt(table->get_column(m_db, value_column, count, args));
+	int res = SQLITE_OK;
+	this->bind_va_columns(stmt, count, args);
+	uint32_t size = value_column->size();
+	*output = malloc(size);
+	assert(*output);
+	res = this->step_once(stmt, (uint8_t*)*output, NULL);
+	sqlite3_reset(stmt);
+	cache_release_value(m_statement_cache, pps);
+	va_end(args);
+	return res;
+}
+
+int Database::get_column(const char* name, void** output, uint32_t* result_count,
+						 Table* table, Column* column, uint32_t count, ...) {
+	va_list args;
+	va_start(args, count);
+	__get_stmt(table->get_column(m_db, column, count, args));
+	int res = SQLITE_OK;
+	this->bind_va_columns(stmt, count, args);
+	uint32_t size = INITIAL_ROWS * column->size();
+	*output = malloc(size);
+	res = this->step_all(stmt, output, size, result_count);
+	sqlite3_reset(stmt);
+	cache_release_value(m_statement_cache, pps);
+	va_end(args);
+	return res;
+}
+
+int Database::get_row(const char* name, uint8_t** output, Table* table, 
+					  uint32_t count, ...) {
+	va_list args;
+	va_start(args, count);
+	__get_stmt(table->get_row(m_db, count, args));
+	int res = SQLITE_OK;
+	this->bind_va_columns(stmt, count, args);
+	*output = table->alloc_result();
+	res = this->step_once(stmt, *output, NULL);
+	sqlite3_reset(stmt);
+	cache_release_value(m_statement_cache, pps);
+	va_end(args);
+	return res;
+}
+
+int Database::get_row_ordered(const char* name, uint8_t** output, Table* table, 
+							  Column* order_by, int order, uint32_t count, ...) {
+	va_list args;
+	va_start(args, count);
+	__get_stmt(table->get_row_ordered(m_db, order_by, order, count, args));
+	int res = SQLITE_OK;
+	this->bind_va_columns(stmt, count, args);
+	*output = table->alloc_result();
+	res = this->step_once(stmt, *output, NULL);
+	sqlite3_reset(stmt);
+	cache_release_value(m_statement_cache, pps);
+	va_end(args);
+	return res;
+}
+
+int Database::get_all_ordered(const char* name, uint8_t*** output, 
+							  uint32_t* result_count, Table* table, 
+							  Column* order_by, int order, uint32_t count, ...) {
+	va_list args;
+	va_start(args, count);
+	__get_stmt(table->get_row_ordered(m_db, order_by, order, count, args));
+	int res = SQLITE_OK;
+	this->bind_va_columns(stmt, count, args);
+	uint8_t* current = NULL;
+	*result_count = 0;
+	uint32_t output_max = INITIAL_ROWS;
+	*output = (uint8_t**)calloc(output_max, sizeof(uint8_t*));
+	
+	res = SQLITE_ROW;
+	while (res == SQLITE_ROW) {
+		if ((*result_count) >= output_max) {
+			output_max *= REALLOC_FACTOR;
+			*output = (uint8_t**)realloc((*output), output_max * sizeof(uint8_t*));
+			if (!(*output)) {
+				fprintf(stderr, "Error: ran out of memory trying to realloc output"
+						        "in get_all_ordered.\n");
+				return DB_ERROR;
+			}
+		}
+		current = table->alloc_result();
+		res = this->step_once(stmt, current, NULL);
+		if (res == SQLITE_ROW) {
+			(*output)[(*result_count)] = current;
+			(*result_count)++;
+		} else {
+			table->free_result(current);
+		}
+	}
+
+	sqlite3_reset(stmt);
+	cache_release_value(m_statement_cache, pps);
+	va_end(args);
+	return res;
+}
+
+int Database::update_value(const char* name, Table* table, Column* value_column, 
+						   void** value, uint32_t count, ...) {
+	va_list args;
+	va_start(args, count);
+	__get_stmt(table->update_value(m_db, value_column, count, args));
+	int param = 1;
+	int res = SQLITE_OK;
+	switch(value_column->type()) {
+		case TYPE_INTEGER:
+			res = sqlite3_bind_int64(stmt, param++, (uint64_t)*value);
+			break;
+		case TYPE_TEXT:
+			res = sqlite3_bind_text(stmt, param++, (char*)*value, -1, SQLITE_STATIC);
+			break;
+			// XXX: support blob columns here
+		case TYPE_BLOB:
+			fprintf(stderr, "Error: Database::update_value() not implemented for "
+					        "BLOB columns.\n");
+			assert(false);
+	}
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: update_value failed to bind value with value_column "
+				        "type %d in table %s. \n",
+				value_column->type(), table->name());
+		return res;
+	}
+	this->bind_columns(stmt, count, param, args);
+	res = sqlite3_step(stmt);
+	sqlite3_reset(stmt);
+    cache_release_value(m_statement_cache, pps);
+	va_end(args);
+	return (res == SQLITE_DONE ? SQLITE_OK : res);
+}
+
+int Database::del(const char* name, Table* table, uint32_t count, ...) {
+	va_list args;
+	va_start(args, count);
+	__get_stmt(table->del(m_db, count, args));
+	int res = SQLITE_OK;
+	this->bind_va_columns(stmt, count, args);
+	if (res == SQLITE_OK) res = this->execute(stmt);
+	va_end(args);
+	return res;
+	
+}
+
+/**
+ * Given a table and an arg list in the same order as Table::add_column() calls,
+ * binds and executes a sql update. The Table is responsible for preparing the
+ * statement in Table::update()
+ *
+ * All integer args must be cast to uint64_t
+ * All blob columns must provide 2 args in the list. The first arg is a uint8_t* 
+ * of data and then the uint32_t value for size of the data. 
+ *
+ */
+int Database::update(Table* table, uint64_t pkvalue, ...) {
+	va_list args;
+	va_start(args, pkvalue);
+
+	int res = SQLITE_OK;
+	
+	// get the prepared statement
+	sqlite3_stmt* stmt = table->update(m_db);
+	if (!stmt) {
+		fprintf(stderr, "Error: %s table gave a NULL statement when trying to "
+				        "update.\n", table->name());
+		return 1;
+	}
+	
+	this->bind_all_columns(stmt, table, args);
+	
+	// bind the primary key in the WHERE clause
+	//  bind_all_columns already bound the first n'th params, where n in the
+	//  table's column count, so we provide that count as the parameter value
+	if (res==SQLITE_OK) res = sqlite3_bind_int64(stmt, table->column_count(), 
+												 pkvalue);
+	if (res==SQLITE_OK) res = this->execute(stmt);
+	va_end(args);
+	return res;
+}
+
+int Database::insert(Table* table, ...) {
+	va_list args;
+	va_start(args, table);
+
+	int res = SQLITE_OK;
+	// get the prepared statement
+	sqlite3_stmt* stmt = table->insert(m_db);
+	if (!stmt) {
+		fprintf(stderr, "Error: %s table gave a NULL statement when trying to "
+				        "insert.\n", table->name());
+		return 1;
+	}
+	this->bind_all_columns(stmt, table, args);
+	if (res == SQLITE_OK) res = this->execute(stmt);
+	va_end(args);
+	return res;
+}
+
+#undef __get_stmt
+
+int Database::del(Table* table, uint64_t serial) {
+	int res = SQLITE_OK;
+	sqlite3_stmt* stmt = table->del(m_db);
+	if (!stmt) {
+		fprintf(stderr, "Error: %s table gave a NULL statement when trying to "
+				        "delete.\n", table->name());
+		return res;
+	}
+	if (res == SQLITE_OK) res = sqlite3_bind_int64(stmt, 1, serial);
+	if (res == SQLITE_OK) res = this->execute(stmt);
+	return res;
+}
+
+uint64_t Database::last_insert_id() {
+	return (uint64_t)sqlite3_last_insert_rowid(m_db);
+}
+
+
+
+int Database::sql_once(const char* fmt, ...) {
+	int res = 0;
+    va_list args;
+    va_start(args, fmt);
+    char* error;
+    if (this->m_db) {
+        char *query = sqlite3_vmprintf(fmt, args);
+        res = sqlite3_exec(this->m_db, query, NULL, NULL, &error);
+        sqlite3_free(query);
+    } else {
+        fprintf(stderr, "Error: database not open.\n");
+        res = SQLITE_ERROR;
+    }
+    va_end(args);
+	if (error) {
+		strlcpy(m_error, error, m_error_size);
+		fprintf(stderr, "Error: sql(): %s \n", m_error);
+		fprintf(stderr, "Error: fmt: %s \n", fmt);
+		sqlite3_free(error);
+	}
+	return res;
+}
+
+int Database::sql(const char* name, const char* fmt, ...) {
+	sqlite3_stmt* stmt;
+	char* key = strdup(name);
+	cache_get_and_retain(m_statement_cache, key, (void**)&stmt);
+	if (!stmt) {
+		va_list args;
+		va_start(args, fmt);
+		char* query = sqlite3_vmprintf(fmt, args);
+		int res = sqlite3_prepare_v2(m_db, query, strlen(query), &stmt, NULL);
+		va_end(args);
+		if (res != SQLITE_OK) {
+			fprintf(stderr, "Error: unable to prepare statement for query: %s\n"
+					        "Error: %s\n",
+					query, sqlite3_errmsg(m_db));
+			free(key);
+			return res;
+		}
+		cache_set_and_retain(m_statement_cache, key, stmt, 0); \
+		free(key);
+	}
+	return this->execute(stmt);
+}
+
+int Database::execute(sqlite3_stmt* stmt) {
+	int res = sqlite3_step(stmt);
+	if (res == SQLITE_DONE) {
+		res = SQLITE_OK;
+	} else {
+		strlcpy(m_error, sqlite3_errmsg(m_db), m_error_size);
+		fprintf(stderr, "Error: execute() error: %s \n", m_error);
+	}
+	res = sqlite3_reset(stmt);
+	return res;
+}
+
+int Database::add_table(Table* t) {
+	if (m_table_count >= m_table_max) {
+		m_tables = (Table**)realloc(m_tables, 
+									m_table_max*sizeof(Table*)*REALLOC_FACTOR);
+		if (!m_tables) {
+			fprintf(stderr, "Error: unable to reallocate memory to add a "
+					"table\n");
+			return 1;
+		}
+		m_table_max *= REALLOC_FACTOR;
+	}
+	m_tables[m_table_count++] = t;
+	
+	return 0;
+}
+
+/**
+ * get a row count of the first table to detect if the schema
+ * needs to be initialized
+ */
+bool Database::is_empty() {
+	if (!m_tables[0]) {
+		fprintf(stderr, "Warning: Database has not had a schema initialized.\n");
+		return false;
+	}
+	int res = SQLITE_OK;
+	char* query;
+	asprintf(&query, "SELECT count(*) FROM %s;", m_tables[0]->name());
+	res = sqlite3_exec(this->m_db, query, NULL, NULL, NULL);
+	free(query);
+	return res != SQLITE_OK;
+}
+
+int Database::create_tables() {
+	int res = SQLITE_OK;
+	for (uint32_t i=0; i<m_table_count; i++) {
+		this->sql_once(m_tables[i]->create());
+		if (res!=SQLITE_OK) {
+			fprintf(stderr, "Error: sql error trying to create table: %s: %s\n",
+					m_tables[i]->name(), m_error);
+			return res;
+		}
+	}
+	return res;
+}
+
+size_t Database::store_column(sqlite3_stmt* stmt, int column, uint8_t* output) {
+	size_t used;
+	int type = sqlite3_column_type(stmt, column);
+	const void* blob;
+	int blobsize;
+	switch(type) {
+		case SQLITE_INTEGER:
+			*(uint64_t*)output = (uint64_t)sqlite3_column_int64(stmt, column);
+			used = sizeof(uint64_t);
+			break;
+		case SQLITE_TEXT:
+			*(const char**)output = strdup((const char*)sqlite3_column_text(stmt, 
+																			column));
+			used = sizeof(char*);
+			break;
+		case SQLITE_BLOB:
+			blob = sqlite3_column_blob(stmt, column);
+			blobsize = sqlite3_column_bytes(stmt, column);
+			*(void**)output = malloc(blobsize);
+			if (*(void**)output && blobsize) {
+				memcpy(*(void**)output, blob, blobsize);
+			} else {
+				fprintf(stderr, "Error: unable to get blob from database stmt.\n");
+			}
+			used = sizeof(void*);
+			break;
+		case SQLITE_NULL:
+			// result row has a NULL value which is okay
+			*(const char**)output = NULL;
+			used = sizeof(char*);
+			break;
+		default:
+			fprintf(stderr, "Error: unhandled column type in "
+							"Database::store_column(): %d \n", 
+					type);
+			return 0;
+	}
+	
+	return used;
+}
+
+/**
+ *   will not realloc memory for output since caller should know how
+ *   much to alloc in the first place. Sets used to be how many bytes
+ *   were written to output
+ */
+int Database::step_once(sqlite3_stmt* stmt, uint8_t* output, uint32_t* used) {
+	int res = sqlite3_step(stmt);
+	uint8_t* current = output;
+	if (used) *used = 0;
+	if (res == SQLITE_ROW) {
+		int count = sqlite3_column_count(stmt);
+		for (int i = 0; i < count; i++) {
+			current += this->store_column(stmt, i, current);
+		}
+		if (used) {
+			*used = current - output;
+		}
+	}
+	
+	return res;
+}
+
+int Database::step_all(sqlite3_stmt* stmt, void** output, uint32_t size, 
+					   uint32_t* count) {
+	uint32_t used = 0;
+	uint32_t total_used = used;
+	uint32_t rowsize = size / INITIAL_ROWS;
+	uint8_t* current = *(uint8_t**)output;
+	*count = 0;
+	int res = SQLITE_ROW;
+	while (res == SQLITE_ROW) {
+		current = *(uint8_t**)output + total_used;
+		res = this->step_once(stmt, current, &used);
+		if (res == SQLITE_ROW) (*count)++;
+		total_used += used;
+		if (total_used >= (size - rowsize)) {
+			size *= REALLOC_FACTOR;
+			*output = realloc(*output, size);
+			if (!*output) {
+				fprintf(stderr, "Error: ran out of memory in Database::step_all \n");
+				return SQLITE_ERROR;
+			}
+		}		
+	}
+    sqlite3_reset(stmt);
+	return res;
+}
+
+
+/**
+ *
+ *  libcache
+ *
+ */
+void Database::init_cache() {
+	cache_attributes_t attrs;
+	attrs.version = CACHE_ATTRIBUTES_VERSION_2;
+	attrs.key_hash_cb = cache_key_hash_cb_cstring;
+	attrs.key_is_equal_cb = cache_key_is_equal_cb_cstring;
+	attrs.key_retain_cb = cache_key_retain;
+	attrs.key_release_cb = cache_release_cb_free;
+	attrs.value_release_cb = cache_statement_release;
+	attrs.value_retain_cb = NULL;
+	attrs.value_make_purgeable_cb = NULL;
+	attrs.value_make_nonpurgeable_cb = NULL;
+	attrs.user_data = NULL;
+	cache_create("org.macosforge.darwinbuild.darwinup.statements", 
+				 &attrs, &m_statement_cache);
+}
+
+void Database::destroy_cache() {
+	cache_destroy(m_statement_cache);
+}
+
+void cache_key_retain(void* key_in, void** key_out, void* user_data) {
+	*key_out = strdup((char*)key_in);
+}
+
+void cache_statement_release(void* value, void* user_data) {
+	sqlite3_finalize(*(sqlite3_stmt**)value);
+}

Copied: trunk/darwinup/Database.h (from rev 731, branches/PR-7489777/darwinup/Database.h)
===================================================================
--- trunk/darwinup/Database.h	                        (rev 0)
+++ trunk/darwinup/Database.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_START@
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_END@
+ */
+
+#ifndef _DATABASE_H
+#define _DATABASE_H
+
+#include <assert.h>
+#include <cache.h>
+#include <cache_callbacks.h>
+#include <sqlite3.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "Table.h"
+#include "Digest.h"
+#include "Archive.h"
+
+// flag for generating queries with ORDER BY clauses
+#define ORDER_BY_DESC 0
+#define ORDER_BY_ASC  1
+
+// initial number of rows we allocate for when querying
+#define INITIAL_ROWS   8
+
+// how much we grow by when we need more space
+#define REALLOC_FACTOR 4
+#define ERROR_BUF_SIZE 1024
+
+// return code bits
+#define DB_OK        0x0000
+#define DB_ERROR     0x0001
+#define DB_FOUND     0x0010
+
+// test return code to see if actual results were found
+#define FOUND(x)  ((x & DB_FOUND) && !(x & DB_ERROR))
+
+// Schema creation macros
+#define ADD_COLUMN(table, name, type, index, pk, unique) \
+    assert(table->add_column(new Column(name, type, index, pk, unique))==0);
+#define ADD_INDEX(table, name, type, unique) \
+	assert(table->add_column(new Column(name, type, true, false, unique))==0);
+#define ADD_PK(table, name) \
+	assert(table->add_column(new Column(name, TYPE_INTEGER, \
+                                        false, true, false))==0);
+#define ADD_TEXT(table, name) \
+	assert(table->add_column(new Column(name, TYPE_TEXT))==0);
+#define ADD_INTEGER(table, name) \
+	assert(table->add_column(new Column(name, TYPE_INTEGER))==0);
+#define ADD_BLOB(table, name) \
+	assert(table->add_column(new Column(name, TYPE_BLOB))==0);
+
+
+/**
+ * 
+ * Generic sqlite abstraction
+ *
+ */
+struct Database {
+	Database();
+	Database(const char* path);
+	virtual ~Database();
+
+	/**
+	 * init_schema is called during db connection.
+	 * Projects implementing a Database derived class
+	 * should use Table::add_column() or the ADD_*
+	 * macros in their init_schema() to define their schema
+	 */
+	virtual void init_schema();
+	
+	const char*  path();
+	const char*  error();
+	int          connect();
+	int          connect(const char* path);
+	
+	int          begin_transaction();
+	int          rollback_transaction();
+	int          commit_transaction();
+	
+	/**
+	 * statement caching and execution
+	 *
+	 * - name is a string key that labels the query for caching purposes 
+	 * - output is where we will store the value requested
+	 * - count is the number of sets of parameters
+	 * - va_list should have sets of 3 (integer and text) or 4 (blob) 
+	 *     parameters for WHERE clause like Column*, char, value(s)
+	 *      - Column* is the column to match against
+	 *      - char is how to compare, one of '=', '!', '>', or '<'
+	 *      - value(s) is the value to match
+	 *          - text columns require a char* arg
+	 *          - integer columns require a uint64_t arg
+	 *          - blob columns require 2 args in the list:
+	 *              - first is a uint8_t* of data
+	 *              - second is a uint32_t value for size of the data
+	 *
+	 */
+	int  count(const char* name, void** output, Table* table, uint32_t count, ...);
+	int  get_value(const char* name, void** output, Table* table, Column* value_column, 
+				   uint32_t count, ...);
+	int  get_column(const char* name, void** output, uint32_t* result_count, 
+					Table* table, Column* column, uint32_t count, ...);
+	int  get_row(const char* name, uint8_t** output, Table* table, uint32_t count, ...);
+	int  get_row_ordered(const char* name, uint8_t** output, Table* table, Column* order_by, 
+						 int order, uint32_t count, ...);
+	int  get_all_ordered(const char* name, uint8_t*** output, uint32_t* result_count,
+						 Table* table, Column* order_by, int order, uint32_t count, ...);
+	int  update_value(const char* name, Table* table, Column* value_column, void** value, 
+					  uint32_t count, ...);
+	int  del(const char* name, Table* table, uint32_t count, ...);
+	
+	/**
+	 * update/insert whole rows
+	 *
+	 * Given a table and a va_list in the same order as Table::add_column() 
+	 * calls, minus any primary key columns, bind and executes a sql query 
+	 * for insert or update. 
+	 *
+	 * The Table is responsible for preparing the statement
+	 *
+	 * text columns require char* args
+	 * integer columns require uint64_t args
+	 * blob columns require 2 args in the list:
+	 *    - first is a uint8_t* of data
+	 *    - second is a uint32_t value for size of the data 
+	 *
+	 */	
+	int  update(Table* table, uint64_t pkvalue, ...);
+	int  insert(Table* table, ...);
+	
+	// delete row with primary key equal to serial
+	int  del(Table* table, uint64_t serial);
+	
+	uint64_t last_insert_id();
+	
+	
+protected:
+
+	// execute query with printf-style format, does not cache statement
+	int sql_once(const char* fmt, ...);
+	// cache statement with name, execute query with printf-style format
+	int sql(const char* name, const char* fmt, ...);
+	int execute(sqlite3_stmt* stmt);
+	
+	int  add_table(Table*);
+	
+	// test if database has had its tables created
+	bool  is_empty();
+	int   create_tables();
+
+	// bind all table columns from va_list
+	int   bind_all_columns(sqlite3_stmt* stmt, Table* table, va_list args);
+	// bind each set of parameters from va_list 
+	int   bind_va_columns(sqlite3_stmt* stmt, uint32_t count, va_list args);
+	// bind parameters from va_list, starting with the param'th parameter in stmt
+	int   bind_columns(sqlite3_stmt* stmt, uint32_t count, int param, 
+					   va_list args);
+	
+	/**
+	 * step and store functions
+	 */
+	size_t store_column(sqlite3_stmt* stmt, int column, uint8_t* output);
+	int step_once(sqlite3_stmt* stmt, uint8_t* output, uint32_t* used);
+	int step_all(sqlite3_stmt* stmt, void** output, uint32_t size, uint32_t* count);
+	
+	// libcache
+	void init_cache();
+	void destroy_cache();
+	
+	char*            m_path;
+	sqlite3*         m_db;
+
+	Table**          m_tables;
+	uint32_t         m_table_count;
+	uint32_t         m_table_max;
+
+	cache_t*         m_statement_cache;
+	
+	sqlite3_stmt*    m_begin_transaction;
+	sqlite3_stmt*    m_rollback_transaction;
+	sqlite3_stmt*    m_commit_transaction;
+	
+	char*            m_error;
+	size_t           m_error_size;
+	
+	static const int TYPE_INTEGER = SQLITE_INTEGER;
+	static const int TYPE_TEXT    = SQLITE3_TEXT;
+	static const int TYPE_BLOB    = SQLITE_BLOB;
+
+};
+
+// libcache callbacks
+void cache_key_retain(void* key_in, void** key_out, void* user_data);
+void cache_statement_retain(void* value, void* user_data);
+void cache_statement_release(void* value, void* user_data);
+
+#endif

Modified: trunk/darwinup/Depot.cpp
===================================================================
--- trunk/darwinup/Depot.cpp	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/Depot.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -35,7 +35,6 @@
 #include "File.h"
 #include "SerialSet.h"
 #include "Utils.h"
-
 #include <assert.h>
 #include <copyfile.h>
 #include <errno.h>
@@ -48,7 +47,6 @@
 #include <unistd.h>
 #include <sys/stat.h>
 
-#include <sqlite3.h>
 
 Depot::Depot() {
 	m_prefix = NULL;
@@ -75,8 +73,12 @@
 }
 
 Depot::~Depot() {
+	
+	// XXX: this is expensive, but is it necessary?
+	//this->check_consistency();
+
 	if (m_lock_fd != -1)	this->unlock();
-	if (m_db)		sqlite3_close(m_db);
+	delete m_db;
 	if (m_prefix)           free(m_prefix);
 	if (m_depot_path)	free(m_depot_path);
 	if (m_database_path)	free(m_database_path);
@@ -88,16 +90,17 @@
 const char*	Depot::downloads_path()		{ return m_downloads_path; }
 const char*     Depot::prefix()                 { return m_prefix; }
 
-// Initialize the depot storage on disk
-int Depot::initialize() {
-	int res = 0;
-	
-	// initialization requires all these paths to be set
-	if (!(m_prefix && m_depot_path && m_database_path && m_archives_path && m_downloads_path)) {
-		return -1;
+int Depot::connect() {
+	m_db = new DarwinupDatabase(m_database_path);
+	if (!m_db) {
+		fprintf(stderr, "Error: unable to connect to database in Depot::connect().\n");
+		return 1;
 	}
-	
-	res = mkdir(m_depot_path, m_depot_mode);
+	return 0;
+}
+
+int Depot::create_storage() {
+	int res = mkdir(m_depot_path, m_depot_mode);
 	if (res && errno != EEXIST) {
 		perror(m_depot_path);
 		return res;
@@ -113,26 +116,39 @@
 		perror(m_downloads_path);
 		return res;
 	}
+	return 0;
+}
 
-	res = this->lock(LOCK_SH);
-	if (res) return res;
-	m_is_locked = 1;
+// Initialize the depot
+int Depot::initialize(bool writable) {
+	int res = 0;
 	
-	int exists = is_regular_file(m_database_path);
-	
-	res = sqlite3_open(m_database_path, &m_db);
-	if (res) {
-		sqlite3_close(m_db);
-		m_db = NULL;
+	// initialization requires all these paths to be set
+	if (!(m_prefix && m_depot_path && m_database_path && m_archives_path && m_downloads_path)) {
+		return -1;
 	}
 	
-	if (m_db && !exists) {
-		this->SQL("CREATE TABLE archives (serial INTEGER PRIMARY KEY AUTOINCREMENT, uuid BLOB UNIQUE, name TEXT, date_added INTEGER, active INTEGER, info INTEGER)");
-		this->SQL("CREATE TABLE files (serial INTEGER PRIMARY KEY AUTOINCREMENT, archive INTEGER, info INTEGER, mode INTEGER, uid INTEGER, gid INTEGER, size INTEGER, digest BLOB, path TEXT)");
-		this->SQL("CREATE INDEX archives_uuid ON archives (uuid)");
-		this->SQL("CREATE INDEX files_path ON files (path)");
+	if (writable) {
+		uid_t uid = getuid();
+		if (uid) {
+			fprintf(stderr, "You must be root to perform that operation.\n");
+			exit(3);
+		}			
+		res = this->create_storage();
+		if (res) return res;
+		res = this->lock(LOCK_SH);
+		if (res) return res;
+		m_is_locked = 1;		
 	}
-	
+
+	int exists = is_regular_file(m_database_path);
+	if (!exists && !writable) {
+		// read-only mode requested but we have no database
+		return -2;
+	}
+
+	res = this->connect();
+
 	return res;
 }
 
@@ -142,63 +158,26 @@
 
 // Unserialize an archive from the database.
 // Find the archive by UUID.
-// XXX: should be memoized
 Archive* Depot::archive(uuid_t uuid) {
 	int res = 0;
 	Archive* archive = NULL;
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "SELECT serial, name, info, date_added FROM archives WHERE uuid=?";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		res = sqlite3_bind_blob(stmt, 1, uuid, sizeof(uuid_t), SQLITE_STATIC);
-		if (res == 0) res = sqlite3_step(stmt);
-		if (res == SQLITE_ROW) {
-			uint64_t serial = sqlite3_column_int64(stmt, 0);
-			const unsigned char* name = sqlite3_column_text(stmt, 1);
-			uint64_t info = sqlite3_column_int64(stmt, 2);
-			time_t date_added = sqlite3_column_int(stmt, 3);
-			archive = new Archive(serial, uuid, (const char*)name, NULL, info, date_added);
-		}
-		sqlite3_reset(stmt);
-	}
+	uint8_t* data;
+	
+	res = this->m_db->get_archive(&data, uuid);
+	if (FOUND(res)) archive = this->m_db->make_archive(data);
 	return archive;
 }
 
 // Unserialize an archive from the database.
 // Find the archive by serial.
-// XXX: should be memoized
 Archive* Depot::archive(uint64_t serial) {
 	int res = 0;
 	Archive* archive = NULL;
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "SELECT uuid, name, info, date_added FROM archives WHERE serial=?";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		res = sqlite3_bind_int64(stmt, 1, serial);
-		if (res == 0) res = sqlite3_step(stmt);
-		if (res == SQLITE_ROW) {
-			uuid_t uuid;
-			const void* blob = sqlite3_column_blob(stmt, 0);
-			int blobsize = sqlite3_column_bytes(stmt, 0);
-			if (blobsize > 0) {
-				assert(blobsize == sizeof(uuid_t));
-				memcpy(uuid, blob, sizeof(uuid_t));
-			} else {
-				uuid_clear(uuid);
-			}
-			const unsigned char* name = sqlite3_column_text(stmt, 1);
-			uint64_t info = sqlite3_column_int64(stmt, 2);
-			time_t date_added = sqlite3_column_int(stmt, 3);
-			archive = new Archive(serial, uuid, (const char*)name, NULL, info, date_added);
-		}
-		sqlite3_reset(stmt);
-	}
+	uint8_t* data;
+	
+	res = this->m_db->get_archive(&data, serial);
+	if (FOUND(res)) archive = this->m_db->make_archive(data);
+
 	return archive;
 }
 
@@ -207,82 +186,23 @@
 Archive* Depot::archive(archive_name_t name) {
 	int res = 0;
 	Archive* archive = NULL;
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "SELECT serial, uuid, info, date_added FROM archives WHERE name=? ORDER BY date_added DESC LIMIT 1";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		res = sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);
-		if (res == 0) res = sqlite3_step(stmt);
-		if (res == SQLITE_ROW) {
-			uuid_t uuid;
-			const void* blob = sqlite3_column_blob(stmt, 1);
-			int blobsize = sqlite3_column_bytes(stmt, 1);
-			if (blobsize > 0) {
-				assert(blobsize == sizeof(uuid_t));
-				memcpy(uuid, blob, sizeof(uuid_t));
-			} else {
-				uuid_clear(uuid);
-			}
-			uint64_t serial = sqlite3_column_int64(stmt, 0);
-			uint64_t info = sqlite3_column_int64(stmt, 2);
-			time_t date_added = sqlite3_column_int(stmt, 3);
-			archive = new Archive(serial, uuid, (const char*)name, NULL, info, date_added);
-		}
-		sqlite3_reset(stmt);
-	}
+	uint8_t* data;
+	
+	res = this->m_db->get_archive(&data, name);
+	if (FOUND(res)) archive = this->m_db->make_archive(data);
 	return archive;
 }
 
 Archive* Depot::archive(archive_keyword_t keyword) {
 	int res = 0;
 	Archive* archive = NULL;
-	static sqlite3_stmt* stmt = NULL;
-	const char* query = NULL;
-	if (stmt == NULL && m_db) {
-		if (keyword == DEPOT_ARCHIVE_NEWEST) {
-			query = "SELECT serial FROM archives WHERE name != '<Rollback>' ORDER BY date_added DESC LIMIT 1";
-		} else if (keyword == DEPOT_ARCHIVE_OLDEST) {
-			query = "SELECT serial FROM archives WHERE name != '<Rollback>' ORDER BY date_added ASC LIMIT 1";
-		} else {
-			fprintf(stderr, "Error: unknown archive keyword.\n");
-			res = -1;
-		}
-		if (res == 0) res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		res = sqlite3_step(stmt);
-		if (res == SQLITE_ROW) {
-			uint64_t serial = sqlite3_column_int64(stmt, 0);
-			archive = Depot::archive(serial);
-		}
-		sqlite3_reset(stmt);
-	}
+	uint8_t* data;
+	
+	res = this->m_db->get_archive(&data, keyword);
+	if (FOUND(res)) archive = this->m_db->make_archive(data);	
 	return archive;	
 }
 
-// create a new Archive from a database row
-Archive* Depot::archive(sqlite3_stmt* stmt) {
-	uuid_t uuid;
-	uint64_t serial = sqlite3_column_int64(stmt, 0);
-	const void* blob = sqlite3_column_blob(stmt, 1);
-	int blobsize = sqlite3_column_bytes(stmt, 1);
-	const unsigned char* name = sqlite3_column_text(stmt, 2);
-	uint64_t info = sqlite3_column_int64(stmt, 3);
-	time_t date_added = sqlite3_column_int(stmt, 4);
-	if (blobsize > 0) {
-		assert(blobsize == sizeof(uuid_t));
-		memcpy(uuid, blob, sizeof(uuid_t));
-	} else {
-		uuid_clear(uuid);
-	}
-	return new Archive(serial, uuid, (const char*)name, NULL, info, date_added);	
-}
-
-
 // Return Archive from database matching arg, which is one of:
 //
 //   uuid (ex: 22969F32-9C4F-4370-82C8-DD3609736D8D)
@@ -292,81 +212,71 @@
 //            or     "oldest" for the oldest installed root)
 //   
 Archive* Depot::get_archive(const char* arg) {
+
+	// test for arg being a uuid
 	uuid_t uuid;
-	uint64_t serial; 
 	if (uuid_parse(arg, uuid) == 0) {
 		return Depot::archive(uuid);
 	}
-	serial = strtoull(arg, NULL, 0);
-	if (serial) {
+
+	// test for arg being a serial number
+	uint64_t serial; 
+	char* endptr = NULL;
+	serial = strtoull(arg, &endptr, 0);
+	if (serial && (*arg != '\0') && (*endptr == '\0')) {
 		return Depot::archive(serial);
 	}
+	
+	// test for keywords
 	if (strncasecmp("oldest", arg, 6) == 0) {
-		IF_DEBUG("looking for oldest\n");
 		return Depot::archive(DEPOT_ARCHIVE_OLDEST);
 	}
 	if (strncasecmp("newest", arg, 6) == 0) {
-		IF_DEBUG("looking for newest\n");
 		return Depot::archive(DEPOT_ARCHIVE_NEWEST);
 	}
+	
+	// if nothing else, must be an archive name
 	return Depot::archive((archive_name_t)arg);
 }
 
-Archive** Depot::get_all_archives(size_t* count) {
+Archive** Depot::get_all_archives(uint32_t* count) {
 	extern uint32_t verbosity;
-	int res = 0;
-	*count = this->count_archives();
-	Archive** list = (Archive**)malloc(*count * sizeof(Archive*));
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "SELECT serial, uuid, name, info, date_added FROM archives WHERE name != '<Rollback>' ORDER BY serial DESC";
-		if (verbosity & VERBOSE_DEBUG) {
-			query = "SELECT serial, uuid, name, info, date_added FROM archives ORDER BY serial DESC";
-		}
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+	int res = DB_OK;
+	uint8_t** archlist;
+	res = this->m_db->get_archives(&archlist, count, verbosity & VERBOSE_DEBUG);
+	
+	Archive** list = (Archive**)malloc(sizeof(Archive*) * (*count));
+	if (!list) {
+		fprintf(stderr, "Error: ran out of memory in Depot::get_all_archives\n");
+		return NULL;
 	}
-	if (stmt && res == 0) {
-		size_t i = 0;
-		while (res != SQLITE_DONE) {
-			res = sqlite3_step(stmt);
-			if (res == SQLITE_ROW) {
-				list[i++] = this->archive(stmt);
-			} 
+	if (FOUND(res)) {
+		for (uint32_t i=0; i < *count; i++) {
+			Archive* archive = this->m_db->make_archive(archlist[i]);
+			if (archive) {
+				list[i] = archive;
+			} else {
+				fprintf(stderr, "%s:%d: DB::make_archive returned NULL\n", __FILE__, __LINE__);
+				res = -1;
+				break;
+			}
 		}
-		sqlite3_reset(stmt);
 	}
+
 	return list;	
 }
 
-size_t Depot::count_archives() {
+uint64_t Depot::count_archives() {
 	extern uint32_t verbosity;
-	int res = 0;
-	size_t count = 0;
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "SELECT count(*) FROM archives WHERE name != '<Rollback>'";
-		if (verbosity & VERBOSE_DEBUG) {
-			query = "SELECT count(*) FROM archives";
-		}
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		res = sqlite3_step(stmt);
-		if (res == SQLITE_ROW) {
-			count = sqlite3_column_int64(stmt, 0);
-		}
-		sqlite3_reset(stmt);
-	}
-	return count;
+	uint64_t c = this->m_db->count_archives((bool)(verbosity & VERBOSE_DEBUG));
+	return c;
 }
 
 int Depot::iterate_archives(ArchiveIteratorFunc func, void* context) {
 	int res = 0;
-	size_t count = 0;
+	uint32_t count = 0;
 	Archive** list = this->get_all_archives(&count);
-	for (size_t i = 0; i < count; i++) {
+	for (uint32_t i = 0; i < count; i++) {
 		if (list[i]) {
 			res = func(list[i], context);
 			delete list[i];
@@ -376,53 +286,24 @@
 }
 
 int Depot::iterate_files(Archive* archive, FileIteratorFunc func, void* context) {
-	int res = 0;
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "SELECT serial, info, path, mode, uid, gid, size, digest FROM files WHERE archive=? ORDER BY path";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		res = sqlite3_bind_int64(stmt, 1, archive->serial());
-		while (res == 0) {
-			res = sqlite3_step(stmt);
-			if (res == SQLITE_ROW) {
-				res = 0;
-				int i = 0;
-				uint64_t serial = sqlite3_column_int64(stmt, i++);
-				uint32_t info = sqlite3_column_int(stmt, i++);
-				const unsigned char* path = sqlite3_column_text(stmt, i++);
-				mode_t mode = sqlite3_column_int(stmt, i++);
-				uid_t uid = sqlite3_column_int(stmt, i++);
-				gid_t gid = sqlite3_column_int(stmt, i++);
-				off_t size = sqlite3_column_int64(stmt, i++);
-				const void* blob = sqlite3_column_blob(stmt, i);
-				int blobsize = sqlite3_column_bytes(stmt, i++);
-
-				Digest* digest = NULL;
-				if (blobsize > 0) {
-					digest = new Digest();
-					digest->m_size = blobsize;
-					memcpy(digest->m_data, blob, ((size_t)blobsize < sizeof(digest->m_data)) ? blobsize : sizeof(digest->m_data));
-				}
-
-				File* file = FileFactory(serial, archive, info, (const char*)path, mode, uid, gid, size, digest);
-				if (file) {
-					res = func(file, context);
-					delete file;
-				} else {
-					fprintf(stderr, "%s:%d: FileFactory returned NULL\n", __FILE__, __LINE__);
-					res = -1;
-					break;
-				}
-			} else if (res == SQLITE_DONE) {
-				res = 0;
+	int res = DB_OK;
+	uint8_t** filelist;
+	uint32_t count;
+	res = this->m_db->get_files(&filelist, &count, archive);
+	if (FOUND(res)) {
+		for (uint32_t i=0; i < count; i++) {
+			File* file = this->m_db->make_file(filelist[i]);
+			if (file) {
+				res = func(file, context);
+				delete file;
+			} else {
+				fprintf(stderr, "%s:%d: DB::make_file returned NULL\n", __FILE__, __LINE__);
+				res = -1;
 				break;
 			}
 		}
-		sqlite3_reset(stmt);
 	}
+
 	return res;
 }
 
@@ -480,7 +361,7 @@
 		
 			uint32_t actual_flags = File::compare(file, actual);
 			uint32_t preceding_flags = File::compare(actual, preceding);
-		
+			
 			// If file == actual && actual == preceding then nothing needs to be done.
 			if (actual_flags == FILE_INFO_IDENTICAL && preceding_flags == FILE_INFO_IDENTICAL) {
 				state = ' ';
@@ -550,20 +431,19 @@
 			if ((state != ' ' && preceding_flags != FILE_INFO_IDENTICAL) ||
 				INFO_TEST(actual->info(), FILE_INFO_BASE_SYSTEM | FILE_INFO_ROLLBACK_DATA)) {
 				*rollback_files += 1;
-				IF_DEBUG("[analyze]    insert rollback\n");
-				res = this->insert(rollback, actual);
+				if (!this->has_file(rollback, actual)) {
+					IF_DEBUG("[analyze]    insert rollback\n");
+					res = this->insert(rollback, actual);
+				}
 				assert(res == 0);
+
 				// need to save parent directories as well
-				char *ppath;
-				char *pathbuf;
-				pathbuf = strdup(actual->path());
-				ppath = dirname(pathbuf);
-				free(pathbuf);
+				FTSENT *pent = ent->fts_parent;
+				
 				// while we have a valid path that is below the prefix
-				while (ppath 
-					   && strncmp(ppath, this->prefix(), strlen(this->prefix())) == 0
-					   && strncmp(ppath, this->prefix(), strlen(ppath)) > 0) {
-					File* parent = FileFactory(ppath);
+				while (pent && pent->fts_level >= 0) {
+					File* parent = FileFactory(rollback, pent);
+					
 					// if parent dir does not exist, we are
 					//  generating a rollback of base system
 					//  which does not have matching directories,
@@ -572,10 +452,13 @@
 						IF_DEBUG("[analyze]      parent path not found, skipping parents\n");
 						break;
 					}
-					IF_DEBUG("[analyze]      adding parent to rollback: %s \n", parent->path());
-					res = this->insert(rollback, parent);
+
+					if (!this->has_file(rollback, parent)) {
+						IF_DEBUG("[analyze]      adding parent to rollback: %s \n", parent->path());
+						res = this->insert(rollback, parent);
+					}
 					assert(res == 0);
-					ppath = dirname(ppath);
+					pent = pent->fts_parent;
 				}
 			}
 
@@ -718,11 +601,6 @@
 	assert(rollback != NULL);
 	assert(archive != NULL);
 
-	// Check the consistency of the database before proceeding with the installation
-	// If this fails, abort the installation.
-	// res = this->check_consistency();
-	// if (res != 0) return res;
-
 	res = this->lock(LOCK_EX);
 	if (res != 0) return res;
 
@@ -791,14 +669,19 @@
 
 	// Installation is complete.  Activate the archive in the database.
 	if (res == 0) res = this->begin_transaction();
-	if (res == 0) res = SQL("UPDATE archives SET active=1 WHERE serial=%lld;", rollback->serial());
-	if (res == 0) res = SQL("UPDATE archives SET active=1 WHERE serial=%lld;", archive->serial());
+	if (res == 0) {
+		res = this->m_db->activate_archive(rollback->serial());
+		if (res) this->rollback_transaction();
+	}
+	if (res == 0) {
+		res = this->m_db->activate_archive(archive->serial());
+		if (res) this->rollback_transaction();
+	}
 	if (res == 0) res = this->commit_transaction();
 
 	// Remove the stage and rollback directories (save disk space)
 	remove_directory(archive_path);
 	remove_directory(rollback_path);
-
 	free(rollback_path);
 	free(archive_path);
 	
@@ -820,7 +703,6 @@
 		if (ent->fts_info == FTS_D) {
 			char path[PATH_MAX];
 			snprintf(path, PATH_MAX, "%s/%s", m_archives_path, ent->fts_name);
-			IF_DEBUG("pruning: %s\n", path);
 			res = remove_directory(path);
 		}
 		ent = ent->fts_link;
@@ -831,21 +713,7 @@
 
 int Depot::prune_archives() {
 	int res = 0;
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "DELETE FROM archives WHERE serial IN (SELECT serial FROM archives WHERE serial NOT IN (SELECT DISTINCT archive FROM files));";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		if (res == 0) res = sqlite3_step(stmt);
-		if (res == SQLITE_DONE) {
-			res = 0;
-		} else {
-			fprintf(stderr, "%s:%d: Could not prune archives in database: %s (%d)\n", __FILE__, __LINE__, sqlite3_errmsg(m_db), res);
-		}
-		sqlite3_reset(stmt);
-	}
+	res = this->m_db->delete_empty_archives();
 	return res;
 }
 
@@ -934,9 +802,6 @@
 		return -1;
 	}
 
-//	res = this->check_consistency();
-//	if (res != 0) return res;
-
 	res = this->lock(LOCK_EX);
 	if (res != 0) return res;
 
@@ -946,7 +811,7 @@
 
 	// We do this here to get an exclusive lock on the database.
 	if (res == 0) res = this->begin_transaction();
-	if (res == 0) res = SQL("UPDATE archives SET active=0 WHERE serial=%lld;", serial);
+	if (res == 0) res = m_db->deactivate_archive(serial);
 	if (res == 0) res = this->commit_transaction();
 
 	InstallContext context(this, archive);
@@ -956,8 +821,7 @@
 	uint32_t i;
 	for (i = 0; i < context.files_to_remove->count; ++i) {
 		uint64_t serial = context.files_to_remove->values[i];
-		IF_DEBUG("deleting file %lld\n", serial);
-		if (res == 0) res = SQL("DELETE FROM files WHERE serial=%lld;", serial);
+		if (res == 0) res = m_db->delete_file(serial);
 	}
 	if (res == 0) res = this->commit_transaction();
 
@@ -1067,79 +931,18 @@
 }
 
 
-File* Depot::file_star_eded_by(File* file, sqlite3_stmt* stmt) {
-	assert(file != NULL);
-	assert(file->archive() != NULL);
-	
-	File* result = NULL;
-	uint64_t serial = 0;
-	int res = 0;
-	if (stmt && res == 0) {
-		if (res == 0) res = sqlite3_bind_int64(stmt, 1, file->archive()->serial());
-		if (res == 0) res = sqlite3_bind_text(stmt, 2, file->path(), -1, SQLITE_STATIC);
-		if (res == 0) res = sqlite3_step(stmt);
-		switch (res) {
-			case SQLITE_DONE:
-				serial = 0;
-				break;
-			case SQLITE_ROW:
-				{
-				int i = 0;
-				uint64_t serial = sqlite3_column_int64(stmt, i++);
-				uint64_t archive_serial = sqlite3_column_int64(stmt, i++);
-				uint32_t info = sqlite3_column_int(stmt, i++);
-				const unsigned char* path = sqlite3_column_text(stmt, i++);
-				mode_t mode = sqlite3_column_int(stmt, i++);
-				uid_t uid = sqlite3_column_int(stmt, i++);
-				gid_t gid = sqlite3_column_int(stmt, i++);
-				off_t size = sqlite3_column_int64(stmt, i++);
-				const void* blob = sqlite3_column_blob(stmt, i);
-				int blobsize = sqlite3_column_bytes(stmt, i++);
-
-				Digest* digest = NULL;
-				if (blobsize > 0) {
-					digest = new Digest();
-					digest->m_size = blobsize;
-					memcpy(digest->m_data, blob, ((size_t)blobsize < sizeof(digest->m_data)) ? blobsize : sizeof(digest->m_data));
-				}
-
-				Archive* archive = this->archive(archive_serial);
-
-				result = FileFactory(serial, archive, info, (const char*)path, mode, uid, gid, size, digest);
-				}
-				break;
-			default:
-				fprintf(stderr, "%s:%d: unexpected SQL error: %d\n", __FILE__, __LINE__, res);
-				break;
-		}
-		sqlite3_reset(stmt);
-	} else {
-		fprintf(stderr, "%s:%d: unexpected SQL error: %d\n", __FILE__, __LINE__, res);
-	}
-	
-	return result;
-}
-
 File* Depot::file_superseded_by(File* file) {
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		// archive which installed this file immediately after
-		const char* query = "SELECT serial, archive, info, path, mode, uid, gid, size, digest FROM files WHERE archive>? AND path=? ORDER BY archive ASC LIMIT 1";
-		int res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	return this->file_star_eded_by(file, stmt);
+	uint8_t* data;
+	int res = this->m_db->get_next_file(&data, file, FILE_SUPERSEDED);
+	if (FOUND(res)) return this->m_db->make_file(data);
+	return NULL;
 }
 
 File* Depot::file_preceded_by(File* file) {
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		// archive which installed this file immediately before
-		const char* query = "SELECT serial, archive, info, path, mode, uid, gid, size, digest FROM files WHERE archive<? AND path=? ORDER BY archive DESC LIMIT 1";
-		int res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	return this->file_star_eded_by(file, stmt);
+	uint8_t* data;
+	int res = this->m_db->get_next_file(&data, file, FILE_PRECEDED);
+	if (FOUND(res)) return this->m_db->make_file(data);
+	return NULL;
 }
 
 int Depot::check_consistency() {
@@ -1148,34 +951,22 @@
 	SerialSet* inactive = new SerialSet();
 	assert(inactive != NULL);
 	
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "SELECT serial FROM archives WHERE active=0 ORDER BY serial DESC";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+	// get inactive archives serials from the database
+	uint64_t* serials;
+	uint32_t  count;
+	this->m_db->get_inactive_archive_serials(&serials, &count);
+	for (uint32_t i=0; i < count; i++) {
+		inactive->add(serials[i]);
 	}
-	if (stmt && res == 0) {
-		while (res == 0) {
-			res = sqlite3_step(stmt);
-			if (res == SQLITE_ROW) {
-				res = 0;
-				uint64_t serial = sqlite3_column_int64(stmt, 0);
-				inactive->add(serial);
-			} else if (res == SQLITE_DONE) {
-				res = 0;
-				break;
-			} else {
-				fprintf(stderr, "%s:%d: unexpected SQL error: %d\n", __FILE__, __LINE__, res);
-			}
-		}
-		sqlite3_reset(stmt);
-	}
+	free(serials);
 	
+	// print a list of inactive archives
 	if (res == 0 && inactive && inactive->count > 0) {
-		fprintf(stderr, "The following archive%s in an inconsistent state and must be uninstalled before proceeding:\n\n", inactive->count > 1 ? "s are" : " is");
+		fprintf(stderr, "The following archive%s in an inconsistent state and must be uninstalled "
+						"before proceeding:\n\n", inactive->count > 1 ? "s are" : " is");
 		uint32_t i;
-		fprintf(stderr, "%-36s %-23s %s\n", "UUID", "Date Installed", "Name");
-		fprintf(stderr, "====================================  =======================  =================\n");
+		fprintf(stderr, "%-6s %-36s  %-23s  %s\n", "Serial", "UUID", "Date Installed", "Name");
+		fprintf(stderr, "====== ====================================  =======================  =================\n");
 		for (i = 0; i < inactive->count; ++i) {
 			Archive* archive = this->archive(inactive->values[i]);
 			if (archive) {
@@ -1203,15 +994,15 @@
 
 
 int Depot::begin_transaction() {
-	return this->SQL("BEGIN TRANSACTION");
+	return this->m_db->begin_transaction();
 }
 
 int Depot::rollback_transaction() {
-	return this->SQL("ROLLBACK TRANSACTION");
+	return this->m_db->rollback_transaction();
 }
 
 int Depot::commit_transaction() {
-	return this->SQL("COMMIT TRANSACTION");
+	return this->m_db->commit_transaction();
 }
 
 int Depot::is_locked() { return m_is_locked; }
@@ -1247,35 +1038,14 @@
 int Depot::insert(Archive* archive) {
 	// Don't insert an archive that is already in the database
 	assert(archive->serial() == 0);
-	
-	int res = 0;
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "INSERT INTO archives (uuid, info, name, date_added) VALUES (?, ?, ?, ?)";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		int i = 1;
-		if (res == 0) res = sqlite3_bind_blob(stmt, i++, archive->uuid(), sizeof(uuid_t), SQLITE_STATIC);
-		if (res == 0) res = sqlite3_bind_int(stmt, i++, archive->info());
-		if (res == 0) res = sqlite3_bind_text(stmt, i++, archive->name(), -1, SQLITE_STATIC);
-		if (res == 0) res = sqlite3_bind_int(stmt, i++, archive->date_installed());
-		if (res == 0) res = sqlite3_step(stmt);
-		if (res == SQLITE_DONE) {
-			archive->m_serial = (uint64_t)sqlite3_last_insert_rowid(m_db);
-			res = 0;
-		} else {
-			fprintf(stderr, "%s:%d: Could not add archive to database: %s (%d)\n", __FILE__, __LINE__, sqlite3_errmsg(m_db), res);
-		}
-		sqlite3_reset(stmt);
-	}
-	return res;
+	archive->m_serial = m_db->insert_archive(archive->uuid(),
+											  archive->info(),
+											  archive->name(),
+											  archive->date_installed());
+	return archive->m_serial == 0;
 }
 
 int Depot::insert(Archive* archive, File* file) {
-	int res = 0;
-	int do_update = 0;
 	// check for the destination prefix in file's path, remove if found
 	char *path, *relpath;
 	size_t prefixlen = strlen(this->prefix());
@@ -1285,49 +1055,19 @@
 	        relpath += prefixlen - 1;
 	}
 
-	const char* query = NULL;
-	if (this->has_file(archive, file)) {
-		do_update = 1;
-		IF_DEBUG("archive(%llu) already has %s, updating instead \n", archive->serial(), relpath);
-		query = "UPDATE files SET info=?, mode=?, uid=?, gid=?, digest=? WHERE archive=? and path=?";
-	} else {
-		query = "INSERT INTO files (info, mode, uid, gid, digest, archive, path) VALUES (?, ?, ?, ?, ?, ?, ?)";	
+	file->m_serial = m_db->insert_file(file->info(), file->mode(), file->uid(), file->gid(), 
+									   file->digest(), archive, relpath);
+	if (!file->m_serial) {
+		fprintf(stderr, "Error: unable to insert file at path %s for archive %s \n", 
+				relpath, archive->name());
+		return DB_ERROR;
 	}
-	sqlite3_stmt* stmt = NULL;
-	if (m_db) {
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		int i = 1;
-		if (res == 0) res = sqlite3_bind_int(stmt, i++, file->info());
-		if (res == 0) res = sqlite3_bind_int(stmt, i++, file->mode());
-		if (res == 0) res = sqlite3_bind_int(stmt, i++, file->uid());
-		if (res == 0) res = sqlite3_bind_int(stmt, i++, file->gid());
-		Digest* dig = file->digest();
-		if (res == 0 && dig) res = sqlite3_bind_blob(stmt, i++, dig->data(), dig->size(), SQLITE_STATIC);
-		else if (res == 0) res = sqlite3_bind_blob(stmt, i++, NULL, 0, SQLITE_STATIC);
-		if (res == 0) res = sqlite3_bind_int64(stmt, i++, archive->serial());
-		if (res == 0) res = sqlite3_bind_text(stmt, i++, relpath, -1, SQLITE_STATIC);
-		if (res == 0) res = sqlite3_step(stmt);
-		if (res == SQLITE_DONE) {
-			// if we did an insert, update the file serial
-			if (!do_update) {
-				file->m_serial = (uint64_t)sqlite3_last_insert_rowid(m_db);
-			}
-			res = 0;
-		} else {
-			fprintf(stderr, "%s:%d: Could not add file to database: %s (%d)\n", __FILE__, __LINE__, sqlite3_errmsg(m_db), res);
-		}
-		sqlite3_reset(stmt);
-	}
+
 	free(path);
-	return res;
+	return 0;
 }
 
 int Depot::has_file(Archive* archive, File* file) {
-	int res = 0;
-	size_t count = 0;
 	// check for the destination prefix in file's path, remove if found
 	char *path, *relpath;
 	size_t prefixlen = strlen(this->prefix());
@@ -1336,44 +1076,31 @@
 	if (strncmp(file->path(), this->prefix(), prefixlen) == 0) {
 		relpath += prefixlen - 1;
 	}
-	
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "SELECT count(*) FROM files WHERE archive=? and path=?";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		int i = 1;
-		if (res == 0) res = sqlite3_bind_int64(stmt, i++, archive->serial());
-		if (res == 0) res = sqlite3_bind_text(stmt, i++, relpath, -1, SQLITE_STATIC);
-		if (res == 0) res = sqlite3_step(stmt);
-		if (res == SQLITE_ROW) {
-			count = sqlite3_column_int64(stmt, 0);
-			IF_DEBUG("has_file(%llu, %s) got count = %u \n", archive->serial(), relpath, (unsigned int)count);
-		} else {
-			res = -1;
-			fprintf(stderr, "%s:%d: Could not query for file in archive: %s (%d)\n", __FILE__, __LINE__, sqlite3_errmsg(m_db), res);
-		}
-		sqlite3_reset(stmt);
-	}
+
+	uint64_t count = m_db->count_files(archive, relpath);
+
 	free(path);
 	return count > 0;
 }
 
+
 int Depot::remove(Archive* archive) {
 	int res = 0;
-	uint64_t serial = archive->serial();
-	if (res == 0) res = SQL("DELETE FROM files WHERE archive=%lld", serial);
-	if (res == 0) res = SQL("DELETE FROM archives WHERE serial=%lld", serial);
+	res = m_db->delete_files(archive);
+	if (res) {
+		fprintf(stderr, "Error: unable to delete files for archive %llu \n", archive->serial());
+		return res;
+	}
+	res = m_db->delete_archive(archive);
+	if (res) {
+		fprintf(stderr, "Error: unable to delete archive %llu \n", archive->serial());
+		return res;
+	}
 	return res;
 }
 
 int Depot::remove(File* file) {
-	int res = 0;
-	uint64_t serial = file->serial();
-	if (res == 0) res = SQL("DELETE FROM files WHERE serial=%lld", serial);
-	return res;
+	return m_db->delete_file(file);
 }
 
 // helper to dispatch the actual command for process_archive()
@@ -1399,7 +1126,7 @@
 int Depot::process_archive(const char* command, const char* arg) {
 	extern uint32_t verbosity;
 	int res = 0;
-	size_t count = 0;
+	uint32_t count = 0;
 	Archive** list = NULL;
 	
 	if (strncasecmp(arg, "all", 3) == 0) {
@@ -1427,29 +1154,3 @@
 	free(list);
 	return res;
 }
-
-
-#define __SQL(callback, context, fmt) \
-	va_list args; \
-	char* errmsg; \
-	va_start(args, fmt); \
-	if (this->m_db) { \
-		char *query = sqlite3_vmprintf(fmt, args); \
-		res = sqlite3_exec(this->m_db, query, callback, context, &errmsg); \
-		if (res != SQLITE_OK) { \
-			fprintf(stderr, "Error: %s (%d)\n  SQL: %s\n", errmsg, res, query); \
-		} \
-		sqlite3_free(query); \
-	} else { \
-		fprintf(stderr, "Error: database not open.\n"); \
-		res = SQLITE_ERROR; \
-	} \
-	va_end(args);
-
-int Depot::SQL(const char* fmt, ...) {
-	int res;
-	__SQL(NULL, NULL, fmt);
-	return res;
-}
-
-#undef __SQL

Modified: trunk/darwinup/Depot.h
===================================================================
--- trunk/darwinup/Depot.h	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/Depot.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -30,30 +30,36 @@
  * @APPLE_BSD_LICENSE_HEADER_END@
  */
 
+#ifndef _DEPOT_H
+#define _DEPOT_H
+
 #include <sys/types.h>
 #include <uuid/uuid.h>
-#include <sqlite3.h>
+#include "DB.h"
+#include "Archive.h"
 
 struct Archive;
 struct File;
+struct DarwinupDatabase;
 
 typedef int (*ArchiveIteratorFunc)(Archive* archive, void* context);
 typedef int (*FileIteratorFunc)(File* file, void* context);
 
-typedef char* archive_name_t;
-
-enum archive_keyword_t {
-		DEPOT_ARCHIVE_NEWEST,
-		DEPOT_ARCHIVE_OLDEST
-};
-
 struct Depot {
 	Depot();
 	Depot(const char* prefix);
 	
 	virtual ~Depot();
 
-	int initialize();
+	// establish database connection
+	int connect();
+
+	// create directories we need for storage
+	int create_storage();
+	
+	// use initialize() to connect to database 
+	//  and (optionally) create the storage directories
+	int initialize(bool writable);
 	int is_initialized();
 	
 	const char* prefix();
@@ -69,12 +75,11 @@
 	Archive* archive(uuid_t uuid);
 	Archive* archive(archive_name_t name);
 	Archive* archive(archive_keyword_t keyword);
-	Archive* archive(sqlite3_stmt* stmt);
 	Archive* get_archive(const char* arg);
 
 	// returns a list of Archive*. Caller must free the list. 
-	Archive** get_all_archives(size_t *count);
-	size_t count_archives();
+	Archive** get_all_archives(uint32_t *count);
+	uint64_t count_archives();
 	
 	int dump();
 	static int dump_archive(Archive* archive, void* context);
@@ -107,30 +112,31 @@
 	// test if the depot is currently locked 
 	int is_locked();
 
-	protected:
 
+protected:
+
 	// Serialize access to the Depot via flock(2).
-	int lock(int operation);
-	int unlock(void);
+	int     lock(int operation);
+	int     unlock(void);
 
 	// Inserts an Archive into the database.
 	// This modifies the Archive's serial number.
 	// If the Archive already has a serial number, it cannot be inserted.
-	int insert(Archive* archive);
+	int     insert(Archive* archive);
 	
 	// Inserts a File into the database, as part of the specified Archive.
 	// This modifies the File's serial number.
 	// This modifies the File's Archive pointer.
 	// If the File already has a serial number, it cannot be inserted.
-	int insert(Archive* archive, File* file);
-
-	int has_file(Archive* archive, File* file);
+	int     insert(Archive* archive, File* file);
 	
+	int     has_file(Archive* archive, File* file);
+	
 	// Removes an Archive from the database.
-	int remove(Archive* archive);
+	int     remove(Archive* archive);
 	
 	// Removes a File from the database.
-	int remove(File* file);
+	int     remove(File* file);
 
 	int		analyze_stage(const char* path, Archive* archive, Archive* rollback, int* rollback_files);
 	int		prune_directories();
@@ -138,22 +144,21 @@
 	// Removes all archive entries which have no corresponding files entries.
 	int		prune_archives();
 	
-	File*		file_superseded_by(File* file);
-	File*		file_preceded_by(File* file);
-	File*		file_star_eded_by(File* file, sqlite3_stmt* stmt);
+	File*	file_superseded_by(File* file);
+	File*	file_preceded_by(File* file);
 
 	int		check_consistency();
 
-
-	virtual int	SQL(const char* fmt, ...);
-
-	sqlite3*	m_db;
+	DarwinupDatabase* m_db;
+	
 	mode_t		m_depot_mode;
-        char*           m_prefix;
+	char*       m_prefix;
 	char*		m_depot_path;
 	char*		m_database_path;
 	char*		m_archives_path;
 	char*		m_downloads_path;
-	int		m_lock_fd;
-        int             m_is_locked;
+	int		    m_lock_fd;
+	int         m_is_locked;
 };
+
+#endif

Modified: trunk/darwinup/Digest.cpp
===================================================================
--- trunk/darwinup/Digest.cpp	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/Digest.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *

Modified: trunk/darwinup/Digest.h
===================================================================
--- trunk/darwinup/Digest.h	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/Digest.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -30,10 +30,15 @@
  * @APPLE_BSD_LICENSE_HEADER_END@
  */
 
+#ifndef _DIGEST_H
+#define _DIGEST_H
+
 #include <sys/types.h>
 #include <stdint.h>
 #include <openssl/evp.h>
 
+#include "Utils.h"
+
 ////
 //  Digest
 //
@@ -90,6 +95,7 @@
 	uint32_t	m_size;
 	
 	friend struct Depot;
+	friend struct DarwinupDatabase;
 };
 
 ////
@@ -132,3 +138,6 @@
 	// The target is obtained via readlink(2).
 	SHA1DigestSymlink(const char* filename);
 };
+
+#endif
+

Modified: trunk/darwinup/File.cpp
===================================================================
--- trunk/darwinup/File.cpp	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/File.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *

Modified: trunk/darwinup/File.h
===================================================================
--- trunk/darwinup/File.h	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/File.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -30,12 +30,20 @@
  * @APPLE_BSD_LICENSE_HEADER_END@
  */
 
+#ifndef _FILE_H
+#define _FILE_H
+
 #include "Digest.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fts.h>
 
+enum file_starseded_t {
+	FILE_SUPERSEDED,
+	FILE_PRECEDED
+};
+
 //
 // FILE_INFO flags stored in the database
 //
@@ -214,3 +222,5 @@
 	virtual int install(const char* prefix, const char* dest);
 	virtual int remove();
 };
+
+#endif

Modified: trunk/darwinup/SerialSet.cpp
===================================================================
--- trunk/darwinup/SerialSet.cpp	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/SerialSet.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *

Modified: trunk/darwinup/SerialSet.h
===================================================================
--- trunk/darwinup/SerialSet.h	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/SerialSet.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -30,6 +30,9 @@
  * @APPLE_BSD_LICENSE_HEADER_END@
  */
 
+#ifndef _SERIALSET_H
+#define _SERIALSET_H
+
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -44,3 +47,5 @@
 	uint32_t count;
 	uint64_t* values;
 };
+
+#endif

Copied: trunk/darwinup/Table.cpp (from rev 731, branches/PR-7489777/darwinup/Table.cpp)
===================================================================
--- trunk/darwinup/Table.cpp	                        (rev 0)
+++ trunk/darwinup/Table.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_START@
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "Table.h"
+#include "Database.h"
+
+
+Table::Table(const char* name) {
+	m_column_max    = 2;
+	m_column_count  = 0;
+	m_columns       = (Column**)malloc(sizeof(Column*) * m_column_max);
+	m_columns_size  = 0; 
+	m_result_max    = 1;
+	m_result_count  = 0;
+	m_results       = (uint8_t**)malloc(sizeof(uint8_t*) * m_result_max);
+	m_name          = strdup(name);
+	m_create_sql    = NULL;
+	m_custom_create_sql    = NULL;
+	m_insert_sql    = NULL;
+	m_update_sql    = NULL;
+	m_delete_sql    = NULL;
+	m_prepared_insert = NULL;
+	m_prepared_update = NULL;
+	m_prepared_delete = NULL;	
+}
+
+Table::~Table() {
+	for (uint32_t i = 0; i < m_column_count; i++) {
+		delete m_columns[i];
+	}
+	free(m_columns);
+	
+
+	for (uint32_t i=0; i < m_result_count; i++) {
+		if (m_results[i]) {
+			this->free_result(m_results[i]);
+		}
+	}
+	free(m_results);
+	
+	free(m_name);
+
+	free(m_create_sql);
+	free(m_custom_create_sql);
+	free(m_insert_sql);
+	free(m_update_sql);
+	free(m_delete_sql);
+	
+	sqlite3_finalize(m_prepared_insert);
+	sqlite3_finalize(m_prepared_update);
+	sqlite3_finalize(m_prepared_delete);
+	
+}
+
+const char* Table::name() {
+	return m_name;
+}
+
+int Table::set_custom_create(const char* sql) {
+	this->m_custom_create_sql = strdup(sql);
+	return this->m_custom_create_sql == 0;
+}
+
+int Table::add_column(Column* c) {
+	// accumulate offsets for columns in m_columns_size
+	c->m_offset = this->m_columns_size;
+	this->m_columns_size += c->size();
+	
+	// reallocate if needed
+	if (m_column_count >= m_column_max) {
+		m_columns = (Column**)realloc(m_columns, m_column_max * sizeof(Column*) * REALLOC_FACTOR);
+		if (!m_columns) {
+			fprintf(stderr, "Error: unable to reallocate memory to add a column\n");
+			return 1;
+		}
+		m_column_max *= REALLOC_FACTOR;
+	}
+	m_columns[m_column_count++] = c;
+	
+	return 0;
+}
+
+Column* Table::column(uint32_t index) {
+	if (index < m_column_count) {
+		return this->m_columns[index];
+	} else {
+		return NULL;
+	}
+}
+
+int Table::offset(uint32_t index) {
+	return this->m_columns[index]->offset();
+}
+
+uint32_t Table::row_size() {
+	return m_columns_size;
+}
+
+uint8_t* Table::alloc_result() {
+	if (m_result_count >= m_result_max) {
+		m_results = (uint8_t**)realloc(m_results, m_result_max * sizeof(uint8_t*) * REALLOC_FACTOR);
+		if (!m_results) {
+			fprintf(stderr, "Error: unable to reallocate memory to add a result row\n");
+			return NULL;
+		}
+		m_result_max *= REALLOC_FACTOR;
+	}
+	m_result_count++;
+	m_results[m_result_count-1] = (uint8_t*)calloc(1, this->row_size());
+	return m_results[m_result_count-1];
+}
+
+int Table::free_result(uint8_t* result) {
+	for (uint32_t i=0; i < m_result_count; i++) {
+		// look for matching result
+		if (result == m_results[i]) {
+			this->free_row((uint8_t*)m_results[i]);
+			free(m_results[i]);
+			m_results[i] = NULL;
+			// if we did not free the last result,
+			// move last result to the empty slot
+			if (i != (m_result_count - 1)) {
+				m_results[i] = m_results[m_result_count-1];
+				m_results[m_result_count-1] = NULL;
+			}
+			m_result_count--;
+		}
+	}
+	return 0;
+}
+
+sqlite3_stmt* Table::count(sqlite3* db) {
+	sqlite3_stmt* stmt = (sqlite3_stmt*)malloc(sizeof(sqlite3_stmt*));
+	char* query;
+	int size = asprintf(&query, "SELECT count(*) FROM %s ;", m_name) + 1;
+    int res = sqlite3_prepare_v2(db, query, size, &stmt, NULL); \
+    free(query); \
+    if (res != SQLITE_OK) { \
+        fprintf(stderr, "Error: unable to prepare statement: %s\n", \
+				sqlite3_errmsg(db)); \
+        return NULL; \
+    }	
+	return stmt;
+}
+
+/**
+ * Prepare and cache the update statement. 
+ * Assumes table only has 1 primary key
+ */
+sqlite3_stmt* Table::update(sqlite3* db) {
+	// we only need to prepare once, return if we already have it
+	if (m_prepared_update) {
+		return m_prepared_update;
+	}
+	uint32_t i = 0;
+	bool comma = false;  // flag we set to start adding commas
+	
+	// calculate the length of the sql statement
+	size_t size = 27 + 5*m_column_count;
+	for (i=0; i<m_column_count; i++) {
+		size += strlen(m_columns[i]->name());
+	}
+	
+	// generate the sql query
+	m_update_sql = (char*)malloc(size);
+	strlcpy(m_update_sql, "UPDATE ", size);
+	strlcat(m_update_sql, m_name, size);
+	strlcat(m_update_sql, " SET ", size);
+	for (i=0; i<m_column_count; i++) {
+		// comma separate after 0th column
+		if (comma) strlcat(m_update_sql, ", ", size);
+		// primary keys do not get inserted
+		if (!m_columns[i]->is_pk()) {
+			strlcat(m_update_sql, m_columns[i]->name(), size);
+			strlcat(m_update_sql, "=?", size);
+			comma = true;
+		}
+	}
+	
+	// WHERE statement using primary keys
+	strlcat(m_update_sql, " WHERE ", size);
+	for (i=0; i<m_column_count; i++) {
+		if (m_columns[i]->is_pk()) {
+			strlcat(m_update_sql, m_columns[i]->name(), size);
+			strlcat(m_update_sql, "=?", size);
+			break;
+		}
+	}
+	strlcat(m_update_sql, ";", size);
+	
+	// prepare
+	int res = sqlite3_prepare_v2(db, m_update_sql, strlen(m_update_sql), &m_prepared_update, NULL);
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: unable to prepare update statement for table: %s \n", m_name);
+		return NULL;
+	}
+	return m_prepared_update;
+}
+
+
+sqlite3_stmt* Table::insert(sqlite3* db) {
+	// we only need to prepare once, return if we already have it
+	if (m_prepared_insert) {
+		return m_prepared_insert;
+	}
+	
+	uint32_t i = 0;
+	bool comma = false;  // flag we set to start adding commas
+	
+	// calculate the length of the sql statement
+	size_t size = 27 + 5*m_column_count;
+	for (i=0; i<m_column_count; i++) {
+		size += strlen(m_columns[i]->name());
+	}
+	
+	// generate the sql query
+	m_insert_sql = (char*)malloc(size);
+	strlcpy(m_insert_sql, "INSERT INTO ", size);
+	strlcat(m_insert_sql, m_name, size);
+	strlcat(m_insert_sql, " (", size);
+	for (i=0; i<m_column_count; i++) {
+		// comma separate after 0th column
+		if (comma) strlcat(m_insert_sql, ", ", size);
+		// primary keys do not get inserted
+		if (!m_columns[i]->is_pk()) {
+			strlcat(m_insert_sql, m_columns[i]->name(), size);
+			comma = true;
+		}
+	}
+	comma = false;
+	strlcat(m_insert_sql, ") VALUES (", size);
+	for (i=0; i<m_column_count; i++) {
+		// comma separate after 0th column
+		if (comma) strlcat(m_insert_sql, ", ", size); 
+		// primary keys do not get inserted
+		if (!m_columns[i]->is_pk()) {
+			strlcat(m_insert_sql, "?", size);
+			comma = true;
+		}
+	}
+	strlcat(m_insert_sql, ");", size);
+	
+	// prepare
+	int res = sqlite3_prepare_v2(db, m_insert_sql, strlen(m_insert_sql), &m_prepared_insert, NULL);
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: unable to prepare insert statement for table: %s \n", m_name);
+		return NULL;
+	}
+	return m_prepared_insert;
+}
+
+sqlite3_stmt* Table::del(sqlite3* db) {
+	// we only need to prepare once, return if we already have it
+	if (m_prepared_delete) return m_prepared_delete;
+	
+	uint32_t i = 0;
+	
+	// generate the sql query
+	size_t size = 22 + strlen(m_name);
+	for (i=0; i<m_column_count; i++) {
+		if (m_columns[i]->is_pk()) {
+			size += strlen(m_columns[i]->name()) + 2;
+			break;
+		}
+	}
+	m_delete_sql = (char*)malloc(size);
+	strlcpy(m_delete_sql, "DELETE FROM ", size);
+	strlcat(m_delete_sql, m_name, size);
+	
+	// WHERE statement using primary keys
+	strlcat(m_delete_sql, " WHERE ", size);
+	for (i=0; i<m_column_count; i++) {
+		if (m_columns[i]->is_pk()) {
+			strlcat(m_delete_sql, m_columns[i]->name(), size);
+			strlcat(m_delete_sql, "=?", size);
+			break;
+		}
+	}
+	strlcat(m_delete_sql, ";", size);
+	
+	// prepare
+	int res = sqlite3_prepare_v2(db, m_delete_sql, strlen(m_delete_sql), &m_prepared_delete, NULL);
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: unable to prepare delete statement for table: %s \n", m_name);
+		return NULL;
+	}
+	return m_prepared_delete;
+	
+}
+
+#define __alloc_stmt_query \
+	size_t size = 256; \
+	size_t used = 0; \
+	char* query = (char*)malloc(size); \
+	sqlite3_stmt** pps = (sqlite3_stmt**)malloc(sizeof(sqlite3_stmt*));
+
+#define __check_and_cat(text) \
+	used = strlcat(query, text, size); \
+    if (used >= size-1) { \
+        size *= 4; \
+        query = (char*)realloc(query, size); \
+        if (!query) { \
+			fprintf(stderr, "Error: ran out of memory!\n"); \
+            return NULL; \
+        } \
+        used = strlcat(query, text, size); \
+    }
+
+#define __prepare_stmt \
+    int res = sqlite3_prepare_v2(db, query, size, pps, NULL); \
+    free(query); \
+    if (res != SQLITE_OK) { \
+        fprintf(stderr, "Error: unable to prepare statement: %s\n", \
+				sqlite3_errmsg(db)); \
+        return NULL; \
+    }
+
+sqlite3_stmt** Table::count(sqlite3* db, uint32_t count, va_list args) {
+	__alloc_stmt_query;
+	strlcpy(query, "SELECT count(*) FROM ", size);
+	__check_and_cat(m_name);
+	__check_and_cat(" WHERE 1");
+	this->where_va_columns(count, query, size, &used, args);
+	strlcat(query, ";", size);
+	__prepare_stmt;
+
+	return pps;	
+}
+
+sqlite3_stmt** Table::get_column(sqlite3* db, Column* value_column, uint32_t count, va_list args) {
+	__alloc_stmt_query;
+	strlcpy(query, "SELECT ", size);
+	__check_and_cat(value_column->name());
+	__check_and_cat(" FROM ");
+	__check_and_cat(m_name);
+	__check_and_cat(" WHERE 1");
+	this->where_va_columns(count, query, size, &used, args);
+	strlcat(query, ";", size);
+	__prepare_stmt;
+	
+	return pps;
+}
+
+sqlite3_stmt** Table::get_row(sqlite3* db, uint32_t count, va_list args) {
+	__alloc_stmt_query;
+	strlcpy(query, "SELECT * FROM ", size);
+	__check_and_cat(m_name);
+	__check_and_cat(" WHERE 1");
+	this->where_va_columns(count, query, size, &used, args);
+	strlcat(query, ";", size);
+	__prepare_stmt;
+	
+	return pps;	
+}
+
+sqlite3_stmt** Table::get_row_ordered(sqlite3* db, Column* order_by, int order, 
+									 uint32_t count, va_list args) {
+	__alloc_stmt_query;
+	strlcpy(query, "SELECT * FROM ", size);
+	__check_and_cat(m_name);
+	__check_and_cat(" WHERE 1");
+	this->where_va_columns(count, query, size, &used, args);
+	__check_and_cat(" ORDER BY ");
+	__check_and_cat(order_by->name());
+	__check_and_cat((order == ORDER_BY_DESC ? " DESC" : " ASC"));
+	strlcat(query, ";", size);
+	__prepare_stmt;
+	
+	return pps;	
+}
+
+sqlite3_stmt** Table::update_value(sqlite3* db, Column* value_column, uint32_t count, va_list args) {
+	__alloc_stmt_query;
+	strlcpy(query, "UPDATE ", size);
+	__check_and_cat(m_name);
+	__check_and_cat(" SET ");
+	__check_and_cat(value_column->name());
+	__check_and_cat("=? WHERE 1");
+	this->where_va_columns(count, query, size, &used, args);
+	strlcat(query, ";", size);
+	__prepare_stmt;
+	
+	return pps;
+}
+
+sqlite3_stmt** Table::del(sqlite3* db, uint32_t count, va_list args) {
+	__alloc_stmt_query;
+	strlcpy(query, "DELETE FROM ", size);
+	__check_and_cat(m_name);
+	__check_and_cat(" WHERE 1");
+	this->where_va_columns(count, query, size, &used, args);
+	strlcat(query, ";", size);
+	__prepare_stmt;
+	
+	return pps;
+}
+
+const char* Table::create() {
+	size_t size = 0;
+	if (!m_create_sql) {
+		uint32_t i = 0;
+		
+		// size of "create table ( );" plus table name, plus 1 for each column to separate
+		size = strlen(m_name) + 22 + m_column_count;
+		for (i=0; i<m_column_count; i++) {		
+			// size for column spec
+			size += strlen(m_columns[i]->create());
+			// size for create index query
+			size += 26 + 2*strlen(m_columns[i]->name()) + 2*strlen(m_name);
+			// custom sql
+			if (m_custom_create_sql) size += strlen(m_custom_create_sql);
+		}
+		
+		// create creation sql
+		m_create_sql = (char*)malloc(size);
+		strlcpy(m_create_sql, "CREATE TABLE ", size);
+		strlcat(m_create_sql, m_name, size);
+		strlcat(m_create_sql, " (", size);
+		// get creation sql for each column
+		for (i=0; i<m_column_count; i++) {
+			if (i) strlcat(m_create_sql, ", ", size); // comma separate after 0th column
+			strlcat(m_create_sql, m_columns[i]->create(), size);
+		}
+		strlcat(m_create_sql, "); ", size);
+		
+		for (i=0; i<m_column_count; i++) {
+			if (m_columns[i]->is_index()) {
+				char* buf;
+				asprintf(&buf, "CREATE INDEX %s_%s ON %s (%s);", 
+						 m_name, m_columns[i]->name(), m_name, m_columns[i]->name());
+				strlcat(m_create_sql, buf, size);
+				free(buf);
+			}
+		}
+		if (m_custom_create_sql) strlcat(m_create_sql, m_custom_create_sql, size);
+	}
+
+	return (const char*)m_create_sql;
+}
+
+int Table::where_va_columns(uint32_t count, char* query, size_t size, 
+							size_t* used, va_list args) {
+	char tmpstr[256];
+    char tmp_op = '=';
+    char op = '=';
+    char not_op = ' ';
+	int len;
+	for (uint32_t i=0; i < count; i++) {
+		Column* col = va_arg(args, Column*);
+        tmp_op = va_arg(args, int);
+        if (tmp_op == '!') {
+            not_op = tmp_op;
+        } else {
+            op = tmp_op;
+        }
+        va_arg(args, void*);
+        if (col->type() == SQLITE_BLOB) va_arg(args, uint32_t);
+		len = snprintf(tmpstr, 256, " AND %s%c%c?", col->name(), not_op, op);
+		if (len >= 255) {
+			fprintf(stderr, "Error: column name is too big (limit: 248): %s\n", 
+					col->name());
+			return NULL;
+		}
+		*used = strlcat(query, tmpstr, size);
+		if (*used >= size-1) {
+			size *= 4;
+			query = (char*)realloc(query, size);
+			if (!query) {
+				fprintf(stderr, "Error: ran out of memory!\n");
+				return -1;
+			}
+			*used = strlcat(query, tmpstr, size);
+		}
+	}
+	
+	return 0;
+}
+
+const Column** Table::columns() {
+	return (const Column**)m_columns;
+}
+
+uint32_t Table::column_count() {
+	return this->m_column_count;
+}
+
+int Table::free_row(uint8_t* row) {
+	uint8_t* current = row;
+	void* ptr;
+	for (uint32_t i=0; i < m_column_count; i++) {
+		switch (m_columns[i]->type()) {
+			case SQLITE_INTEGER:
+				current += sizeof(uint64_t);
+				// nothing to free
+				break;
+			default:
+				memcpy(&ptr, current, sizeof(void*));
+				free(ptr);
+				current += sizeof(void*);
+		}
+	}
+	return 0;
+}
+
+void Table::dump_results(FILE* f) {
+	fprintf(f, "====================================================================\n");
+	for (uint32_t i=0; i < m_result_count; i++) {
+		fprintf(f, "%p %u:\n", m_results[i], i);
+		__data_hex(f, m_results[i], 48);
+	}
+	fprintf(f, "====================================================================\n");
+}

Copied: trunk/darwinup/Table.h (from rev 731, branches/PR-7489777/darwinup/Table.h)
===================================================================
--- trunk/darwinup/Table.h	                        (rev 0)
+++ trunk/darwinup/Table.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_START@
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @APPLE_BSD_LICENSE_HEADER_END@
+ */
+
+#ifndef _TABLE_H
+#define _TABLE_H
+
+#include <stdint.h>
+#include <sqlite3.h>
+
+#include "Column.h"
+
+
+struct Table {	
+	Table(const char* name);
+	virtual ~Table();
+	
+	const char*    name();
+
+	// Add custom SQL to table initialization
+	int            set_custom_create(const char* sql);
+	
+	// Column handling
+	int            add_column(Column*);
+	Column*        column(uint32_t index);
+	// get the result record offset for column at index
+	int            offset(uint32_t index);
+	// get total size of result record
+	uint32_t       row_size();	
+
+	// Result record handling
+	uint8_t*       alloc_result();
+	int            free_result(uint8_t* result);
+
+	/**
+	 * sql statement generators (cached on Table)
+	 */
+	sqlite3_stmt*    count(sqlite3* db);
+	sqlite3_stmt*    update(sqlite3* db);
+	sqlite3_stmt*    insert(sqlite3* db);
+	sqlite3_stmt*    del(sqlite3* db);
+	
+	/**
+	 * sql statement generators (cached by Database & libcache)
+	 *
+	 * - order is either ORDER_BY_ASC or ORDER_BY_DESC
+	 * - count parameters should be the number of items in the va_list
+	 * - args should have sets of 3 (integer and text) or 4 (blob) 
+	 *     parameters for WHERE clause like Column*, char, value
+	 *      - Column* is the column to match against
+	 *      - char is how to compare, one of '=', '!', '>', or '<'
+	 *      - value is the value to match (1 for integer and text, 2 for blobs)
+	 *          which is ignored by these API since they leave placeholders 
+	 *          instead
+	 *
+	 */
+	sqlite3_stmt**    count(sqlite3* db, uint32_t count, va_list args);
+	sqlite3_stmt**    get_column(sqlite3* db, Column* value_column, 
+								uint32_t count, va_list args);
+	sqlite3_stmt**    get_row(sqlite3* db, uint32_t count, va_list args);
+	sqlite3_stmt**    get_row_ordered(sqlite3* db, Column* order_by, int order, 
+							 uint32_t count, va_list args);
+	sqlite3_stmt**    update_value(sqlite3* db, Column* value_column, 
+								  uint32_t count, va_list args);
+	sqlite3_stmt**    del(sqlite3* db, uint32_t count, va_list args);
+	
+protected:
+
+	const char*    create();  
+	
+	int            where_va_columns(uint32_t count, char* query, size_t size, 
+									size_t* used, va_list args);
+	const Column** columns();
+	uint32_t       column_count();
+
+	// free the out-of-band columns (text, blob) from a result record
+	int            free_row(uint8_t* row);
+	void           dump_results(FILE* f);	
+	
+	char*          m_name;
+
+	char*          m_create_sql;
+	char*          m_custom_create_sql;
+	char*          m_insert_sql;
+	char*          m_update_sql;
+	char*          m_delete_sql;
+	
+	Column**       m_columns;
+	uint32_t       m_column_count;
+	uint32_t       m_column_max;
+	int            m_columns_size;
+	
+	sqlite3_stmt*  m_prepared_insert;
+	sqlite3_stmt*  m_prepared_update;
+	sqlite3_stmt*  m_prepared_delete;
+	
+	uint8_t**      m_results;
+	uint32_t       m_result_count;
+	uint32_t       m_result_max;
+
+	friend struct Database;
+};
+
+#endif

Modified: trunk/darwinup/Utils.cpp
===================================================================
--- trunk/darwinup/Utils.cpp	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/Utils.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -245,3 +245,21 @@
 	if (res == 0) return localfile;
 	return NULL;	
 }
+
+void __data_hex(FILE* f, uint8_t* data, uint32_t size) {
+	if (!size) return;
+	for (uint32_t i=0; i < size; i++) {
+		if (!(i%8)) {
+			if (i<10) fprintf(f, " ");
+			fprintf(f, "%d", i);
+		} else {
+			fprintf(f, "  ");
+		}
+	}
+	fprintf(f, "\n");
+	for (uint32_t i=0; i < size; i++) {
+		fprintf(f, "%02x", data[i]);
+	}
+	fprintf(f, "\n");
+}
+

Modified: trunk/darwinup/Utils.h
===================================================================
--- trunk/darwinup/Utils.h	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/Utils.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -30,6 +30,9 @@
  * @APPLE_BSD_LICENSE_HEADER_END@
  */
 
+#ifndef _UTILS_H
+#define _UTILS_H
+
 #include <stdint.h>
 #include <sys/types.h>
 #include <fts.h>
@@ -59,7 +62,10 @@
 char* fetch_url(const char* srcpath, const char* dstpath);
 char* fetch_userhost(const char* srcpath, const char* dstpath);
 
+void __data_hex(FILE* f, uint8_t* data, uint32_t size);
+
 inline int INFO_TEST(uint32_t word, uint32_t flag) { return ((word & flag) != 0); }
 inline int INFO_SET(uint32_t word, uint32_t flag) { return (word | flag); }
 inline int INFO_CLR(uint32_t word, uint32_t flag) { return (word & (~flag)); }
 
+#endif

Modified: trunk/darwinup/main.cpp
===================================================================
--- trunk/darwinup/main.cpp	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/main.cpp	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *
@@ -30,10 +30,6 @@
  * @APPLE_BSD_LICENSE_HEADER_END@
  */
 
-#include "Archive.h"
-#include "Depot.h"
-#include "Utils.h"
-
 #include <libgen.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -41,8 +37,15 @@
 #include <unistd.h>
 #include <limits.h>
 
+#include "Archive.h"
+#include "Depot.h"
+#include "Utils.h"
+#include "DB.h"
+
+
 void usage(char* progname) {
 	fprintf(stderr, "usage:    %s [-v] [-p DIR] [command] [args]          \n", progname);
+	fprintf(stderr, "version: 15                                                    \n");
 	fprintf(stderr, "                                                               \n");
 	fprintf(stderr, "options:                                                       \n");
 	fprintf(stderr, "          -f         force operation to succeed at all costs   \n");
@@ -83,6 +86,7 @@
 uint32_t verbosity;
 uint32_t force;
 
+
 int main(int argc, char* argv[]) {
 	char* progname = strdup(basename(argv[0]));      
 
@@ -118,13 +122,7 @@
 	}
 	argc -= optind;
 	argv += optind;
-
-	// you must be root
-	uid_t uid = getuid();
-	if (uid) {
-		fprintf(stderr, "You must be root to run this tool.\n");
-		exit(3);
-	}
+	if (argc == 0) usage(progname);
 	
 	int res = 0;
 
@@ -133,39 +131,55 @@
 	}
 		
 	Depot* depot = new Depot(path);
-	res = depot->initialize();
-	if (res) {
-		fprintf(stderr, "Error: unable to initialize storage.\n");
-		exit(2);
+		
+	// commands with no arguments
+	if (argc == 1) {
+		if (strcmp(argv[0], "list") == 0) {
+			res = depot->initialize(false);
+			if (res == -2) {
+				fprintf(stdout, "Nothing has been installed yet.\n");
+				exit(0);
+			}
+			if (res == 0) depot->list();
+		} else if (strcmp(argv[0], "dump") == 0) {
+			if (depot->initialize(false)) exit(11);
+			depot->dump();
+		} else {
+			usage(progname);
+		}
 	}
-	
-	if (argc == 2 && strcmp(argv[0], "install") == 0) {
-		res = depot->install(argv[1]);
-	} else if (argc == 2 && strcmp(argv[0], "upgrade") == 0) {
-		// find most recent matching archive by name
-		Archive* old = depot->get_archive(basename(argv[1]));
-		if (!old) {
-			fprintf(stderr, "Error: unable to find a matching root to upgrade.\n");
-			res = 5;
+
+	// loop over arguments
+	for (int i = 1; i < argc; i++) {
+		if (strcmp(argv[0], "install") == 0) {
+			if (i==1 && depot->initialize(true)) exit(13);
+			res = depot->install(argv[i]);
+		} else if (strcmp(argv[0], "upgrade") == 0) {
+			if (i==1 && depot->initialize(true)) exit(14);
+			// find most recent matching archive by name
+			Archive* old = depot->get_archive(basename(argv[i]));
+			if (!old) {
+				fprintf(stderr, "Error: unable to find a matching root to upgrade.\n");
+				res = 5;
+			}
+			// install new archive
+			if (res == 0) res = depot->install(argv[i]);
+			// uninstall old archive
+			if (res == 0) res = depot->uninstall(old);
+		} else if (strcmp(argv[0], "files") == 0) {
+			if (i==1 && depot->initialize(false)) exit(12);
+			res = depot->process_archive(argv[0], argv[i]);
+		} else if (strcmp(argv[0], "uninstall") == 0) {
+			if (i==1 && depot->initialize(true)) exit(15);
+			res = depot->process_archive(argv[0], argv[i]);
+		} else if (strcmp(argv[0], "verify") == 0) {
+			if (i==1 && depot->initialize(true)) exit(16);
+			res = depot->process_archive(argv[0], argv[i]);
+		} else {
+			usage(progname);
 		}
-		// install new archive
-		if (res == 0) res = depot->install(argv[1]);
-		// uninstall old archive
-		if (res == 0) res = depot->uninstall(old);
-	} else if (argc == 1 && strcmp(argv[0], "list") == 0) {
-		depot->list();
-	} else if (argc == 1 && strcmp(argv[0], "dump") == 0) {
-		depot->dump();
-	} else if (argc == 2 && strcmp(argv[0], "files") == 0) {
-		res = depot->process_archive(argv[0], argv[1]);
-	} else if (argc == 2 && strcmp(argv[0], "uninstall") == 0) {
-		res = depot->process_archive(argv[0], argv[1]);
-	} else if (argc == 2 && strcmp(argv[0], "verify") == 0) {
-		res = depot->process_archive(argv[0], argv[1]);
-	} else {
-		usage(progname);
 	}
-
+	
 	free(path);
 	exit(res);
 	return res;

Modified: trunk/darwinup/redo_prebinding.h
===================================================================
--- trunk/darwinup/redo_prebinding.h	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/darwinup/redo_prebinding.h	2010-03-03 21:37:30 UTC (rev 732)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_BSD_LICENSE_HEADER_START@
  *

Copied: trunk/testing/darwinup/300dirs.tbz2 (from rev 731, branches/PR-7489777/testing/darwinup/300dirs.tbz2)
===================================================================
(Binary files differ)

Copied: trunk/testing/darwinup/300files.tbz2 (from rev 731, branches/PR-7489777/testing/darwinup/300files.tbz2)
===================================================================
(Binary files differ)

Modified: trunk/testing/darwinup/run-tests.sh
===================================================================
--- trunk/testing/darwinup/run-tests.sh	2010-03-02 21:08:27 UTC (rev 731)
+++ trunk/testing/darwinup/run-tests.sh	2010-03-03 21:37:30 UTC (rev 732)
@@ -32,6 +32,11 @@
 	tar zxvf $R.tar.gz -C $PREFIX
 done;
 
+for R in 300dirs 300files;
+do
+	cp $R.tbz2 $PREFIX/
+done;
+
 mkdir -p $ORIG
 cp -R $DEST/* $ORIG/
 
@@ -47,6 +52,18 @@
 	$DIFF $ORIG $DEST 2>&1
 done
 
+echo "========== TEST: Multiple argument test ==========";
+darwinup -vv -p $DEST install $PREFIX/root{,2,3}
+LINES=$(darwinup -p $DEST list | wc -l)
+if [ $LINES -lt 5 ]; then
+	echo "Failed multiple argument test."
+	exit 1;
+fi
+darwinup -vv -p $DEST uninstall all
+echo "DIFF: diffing original test files to dest (should be no diffs) ..."
+$DIFF $ORIG $DEST 2>&1
+
+
 echo "========== TEST: Trying all roots at once, uninstall in reverse ==========";
 for R in $ROOTS;
 do
@@ -107,6 +124,25 @@
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 
+echo "========== TEST: trying large roots ==========";
+echo "INFO: installing 300files";
+darwinup -vv -p $DEST install $PREFIX/300files.tbz2
+darwinup -vv -p $DEST uninstall 300files.tbz2
+echo "DIFF: diffing original test files to dest (should be no diffs) ..."
+$DIFF $ORIG $DEST 2>&1
+echo "INFO: installing 300dir";
+darwinup -vv -p $DEST install $PREFIX/300dirs.tbz2
+darwinup -vv -p $DEST uninstall 300dirs.tbz2
+echo "DIFF: diffing original test files to dest (should be no diffs) ..."
+$DIFF $ORIG $DEST 2>&1
+echo "INFO: installing both 300files and 300dirs";
+darwinup -vv -p $DEST install $PREFIX/300dirs.tbz2
+darwinup -vv -p $DEST install $PREFIX/300files.tbz2
+darwinup -vv -p $DEST uninstall 300dirs.tbz2
+darwinup -vv -p $DEST uninstall 300files.tbz2
+echo "DIFF: diffing original test files to dest (should be no diffs) ..."
+$DIFF $ORIG $DEST 2>&1
+
 echo "========== TEST: Try uninstalling with user data in rollback =========="
 echo "INFO: Installing root5 ...";
 darwinup -vv -p $DEST install $PREFIX/root5
@@ -131,8 +167,10 @@
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 
- 
-# expected failures
+
+#
+# The following are expected failures
+#
 set +e
 echo "========== TEST: Trying a root that will fail due to object change =========="
 darwinup -vv -p $DEST install $PREFIX/root4
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/darwinbuild-changes/attachments/20100303/21b4acec/attachment-0001.html>


More information about the darwinbuild-changes mailing list