<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[117407] trunk/base</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="https://trac.macports.org/changeset/117407">117407</a></dd>
<dt>Author</dt> <dd>jmr@macports.org</dd>
<dt>Date</dt> <dd>2014-02-25 10:28:14 -0800 (Tue, 25 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>store portgroups in the registry, so they can always be used by the portfiles, especially when a portgroup only exists in a non-default source.
move portfiles out of the db and into the filesystem, and deduplicate them (for when the same version is installed with different variants)</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbaseMakefilein">trunk/base/Makefile.in</a></li>
<li><a href="#trunkbaseportmgrdmgpostflight">trunk/base/portmgr/dmg/postflight</a></li>
<li><a href="#trunkbasesrccregistryMakefile">trunk/base/src/cregistry/Makefile</a></li>
<li><a href="#trunkbasesrccregistryentryc">trunk/base/src/cregistry/entry.c</a></li>
<li><a href="#trunkbasesrccregistryentryh">trunk/base/src/cregistry/entry.h</a></li>
<li><a href="#trunkbasesrccregistryregistryc">trunk/base/src/cregistry/registry.c</a></li>
<li><a href="#trunkbasesrccregistryregistryh">trunk/base/src/cregistry/registry.h</a></li>
<li><a href="#trunkbasesrccregistrysqlc">trunk/base/src/cregistry/sql.c</a></li>
<li><a href="#trunkbasesrcmacports10macportstcl">trunk/base/src/macports1.0/macports.tcl</a></li>
<li><a href="#trunkbasesrcmacports10testsmacportstest">trunk/base/src/macports1.0/tests/macports.test</a></li>
<li><a href="#trunkbasesrcpextlib10tracelibc">trunk/base/src/pextlib1.0/tracelib.c</a></li>
<li><a href="#trunkbasesrcport10portinstalltcl">trunk/base/src/port1.0/portinstall.tcl</a></li>
<li><a href="#trunkbasesrcport10portutiltcl">trunk/base/src/port1.0/portutil.tcl</a></li>
<li><a href="#trunkbasesrcregistry20Makefile">trunk/base/src/registry2.0/Makefile</a></li>
<li><a href="#trunkbasesrcregistry20entryobjc">trunk/base/src/registry2.0/entryobj.c</a></li>
<li><a href="#trunkbasesrcregistry20portuninstalltcl">trunk/base/src/registry2.0/portuninstall.tcl</a></li>
<li><a href="#trunkbasesrcregistry20registryc">trunk/base/src/registry2.0/registry.c</a></li>
<li><a href="#trunkbasesrcregistry20registryh">trunk/base/src/registry2.0/registry.h</a></li>
<li><a href="#trunkbasesrcregistry20registry_utiltcl">trunk/base/src/registry2.0/registry_util.tcl</a></li>
<li><a href="#trunkbasesrcregistry20utilc">trunk/base/src/registry2.0/util.c</a></li>
<li><a href="#trunkbasesrcregistry20utilh">trunk/base/src/registry2.0/util.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkbasesrccregistryportgroupc">trunk/base/src/cregistry/portgroup.c</a></li>
<li><a href="#trunkbasesrccregistryportgrouph">trunk/base/src/cregistry/portgroup.h</a></li>
<li><a href="#trunkbasesrcdedup_portfilestcl">trunk/base/src/dedup_portfiles.tcl</a></li>
<li><a href="#trunkbasesrcregistry20portgroupc">trunk/base/src/registry2.0/portgroup.c</a></li>
<li><a href="#trunkbasesrcregistry20portgrouph">trunk/base/src/registry2.0/portgroup.h</a></li>
<li><a href="#trunkbasesrcregistry20portgroupobjc">trunk/base/src/registry2.0/portgroupobj.c</a></li>
<li><a href="#trunkbasesrcregistry20portgroupobjh">trunk/base/src/registry2.0/portgroupobj.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbaseMakefilein"></a>
<div class="modfile"><h4>Modified: trunk/base/Makefile.in (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/Makefile.in        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/Makefile.in        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -94,6 +94,7 @@
</span><span class="cx">         $(TCLSH) src/upgrade_sources_conf_default.tcl &quot;${prefix}&quot;
</span><span class="cx"> # Convert image directories (and direct mode installs) to image archives
</span><span class="cx">         $(TCLSH) src/images_to_archives.tcl &quot;${macports_tcl_dir}&quot;
</span><ins>+        $(TCLSH) src/dedup_portfiles.tcl &quot;${macports_tcl_dir}&quot;
</ins><span class="cx"> endif
</span><span class="cx"> ifndef SELFUPDATING
</span><span class="cx">         @echo &quot;&quot;; echo &quot;Congratulations, you have successfully installed the MacPorts system. To get the Portfiles and update the system, add ${prefix}/bin to your PATH and run:&quot;; echo &quot;&quot;
</span></span></pre></div>
<a id="trunkbaseportmgrdmgpostflight"></a>
<div class="modfile"><h4>Modified: trunk/base/portmgr/dmg/postflight (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/portmgr/dmg/postflight        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/portmgr/dmg/postflight        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -74,6 +74,7 @@
</span><span class="cx">     # Convert image directories (and direct mode installs) to image archives
</span><span class="cx">     echo &quot;Updating port image format...&quot;
</span><span class="cx">     ${TCLSH} ${SCRIPT_DIR}/images_to_archives.tcl ${MACPORTS_TCL_DIR}
</span><ins>+    ${TCLSH} ${SCRIPT_DIR}/dedup_portfiles.tcl ${MACPORTS_TCL_DIR}
</ins><span class="cx"> 
</span><span class="cx">     echo &quot;Synchronizing the MacPorts installation with the project's rsync server...&quot;
</span><span class="cx">     if ! ${BINPATH}/port -v selfupdate; then 
</span></span></pre></div>
<a id="trunkbasesrccregistryMakefile"></a>
<div class="modfile"><h4>Modified: trunk/base/src/cregistry/Makefile (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/cregistry/Makefile        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/Makefile        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -1,6 +1,6 @@
</span><span class="cx"> # $Id$
</span><span class="cx"> 
</span><del>-OBJS = registry.o entry.o sql.o vercomp.o util.o file.o
</del><ins>+OBJS = registry.o entry.o sql.o vercomp.o util.o file.o portgroup.o
</ins><span class="cx"> STLIB_NAME = cregistry.a
</span><span class="cx"> RANLIB = ranlib
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbasesrccregistryentryc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/cregistry/entry.c (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/cregistry/entry.c        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/entry.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -2,7 +2,7 @@
</span><span class="cx">  * entry.c
</span><span class="cx">  * $Id$
</span><span class="cx">  *
</span><del>- * Copyright (c) 2010-2011 The MacPorts Project
</del><ins>+ * Copyright (c) 2010-2011, 2014 The MacPorts Project
</ins><span class="cx">  * Copyright (c) 2007 Chris Pickel &lt;sfiera@macports.org&gt;
</span><span class="cx">  * All rights reserved.
</span><span class="cx">  *
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> #include &lt;config.h&gt;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#include &quot;portgroup.h&quot;
</ins><span class="cx"> #include &quot;entry.h&quot;
</span><span class="cx"> #include &quot;registry.h&quot;
</span><span class="cx"> #include &quot;sql.h&quot;
</span><span class="lines">@@ -246,9 +247,11 @@
</span><span class="cx">     sqlite3_stmt* ports = NULL;
</span><span class="cx">     sqlite3_stmt* files = NULL;
</span><span class="cx">     sqlite3_stmt* dependencies = NULL;
</span><ins>+    sqlite3_stmt* portgroups = NULL;
</ins><span class="cx">     char* ports_query = &quot;DELETE FROM registry.ports WHERE id=?&quot;;
</span><span class="cx">     char* files_query = &quot;DELETE FROM registry.files WHERE id=?&quot;;
</span><span class="cx">     char* dependencies_query = &quot;DELETE FROM registry.dependencies WHERE id=?&quot;;
</span><ins>+    char* portgroups_query = &quot;DELETE FROM registry.portgroups WHERE id=?&quot;;
</ins><span class="cx">     if ((sqlite3_prepare_v2(reg-&gt;db, ports_query, -1, &amp;ports, NULL) == SQLITE_OK)
</span><span class="cx">             &amp;&amp; (sqlite3_bind_int64(ports, 1, entry-&gt;id) == SQLITE_OK)
</span><span class="cx">             &amp;&amp; (sqlite3_prepare_v2(reg-&gt;db, files_query, -1, &amp;files, NULL)
</span><span class="lines">@@ -256,7 +259,10 @@
</span><span class="cx">             &amp;&amp; (sqlite3_bind_int64(files, 1, entry-&gt;id) == SQLITE_OK)
</span><span class="cx">             &amp;&amp; (sqlite3_prepare_v2(reg-&gt;db, dependencies_query, -1, &amp;dependencies,
</span><span class="cx">                     NULL) == SQLITE_OK)
</span><del>-            &amp;&amp; (sqlite3_bind_int64(dependencies, 1, entry-&gt;id) == SQLITE_OK)) {
</del><ins>+            &amp;&amp; (sqlite3_bind_int64(dependencies, 1, entry-&gt;id) == SQLITE_OK)
+            &amp;&amp; (sqlite3_prepare_v2(reg-&gt;db, portgroups_query, -1, &amp;portgroups,
+                    NULL) == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_int64(portgroups, 1, entry-&gt;id) == SQLITE_OK)) {
</ins><span class="cx">         int r;
</span><span class="cx">         do {
</span><span class="cx">             r = sqlite3_step(ports);
</span><span class="lines">@@ -271,7 +277,20 @@
</span><span class="cx">                                         r = sqlite3_step(dependencies);
</span><span class="cx">                                         switch (r) {
</span><span class="cx">                                             case SQLITE_DONE:
</span><del>-                                                result = 1;
</del><ins>+                                                do {
+                                                    r = sqlite3_step(portgroups);
+                                                    switch (r) {
+                                                        case SQLITE_DONE:
+                                                            result = 1;
+                                                            break;
+                                                        case SQLITE_BUSY:
+                                                            break;
+                                                        case SQLITE_ERROR:
+                                                            reg_sqlite_error(reg-&gt;db,
+                                                                    errPtr, NULL);
+                                                            break;
+                                                    }
+                                                } while (r == SQLITE_BUSY);
</ins><span class="cx">                                                 break;
</span><span class="cx">                                             case SQLITE_BUSY:
</span><span class="cx">                                                 break;
</span><span class="lines">@@ -315,6 +334,9 @@
</span><span class="cx">     if (dependencies) {
</span><span class="cx">         sqlite3_finalize(dependencies);
</span><span class="cx">     }
</span><ins>+    if (portgroups) {
+        sqlite3_finalize(portgroups);
+    }
</ins><span class="cx">     return result;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -670,6 +692,73 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * Associates a portgroup with given port.
+ *
+ * @param [in] entry      the entry to map the portgroup to
+ * @param [in] name       the portgroup name (e.g. &quot;muniversal&quot;)
+ * @param [in] version    the portgroup version (e.g. &quot;1.0&quot;)
+ * @param [in] sha256     the sha256 hash of the portgroup file
+ * @param [in] size       the size of the portgroup file in bytes
+ * @param [out] errPtr    on error, a description of the error that occurred
+ * @return                true if success; false if failure
+ */
+int reg_entry_addgroup(reg_entry* entry, char* name, char *version,
+        char *sha256, sqlite_int64 size, reg_error* errPtr) {
+    reg_registry* reg = entry-&gt;reg;
+    int result = 1;
+    sqlite3_stmt* stmt = NULL;
+    char* insert = &quot;INSERT INTO registry.portgroups (id, name, version, size, sha256) &quot;
+        &quot;VALUES (?, ?, ?, ?, ?)&quot;;
+    if ((sqlite3_prepare_v2(reg-&gt;db, insert, -1, &amp;stmt, NULL) == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_int64(stmt, 1, entry-&gt;id) == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC) == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_text(stmt, 3, version, -1, SQLITE_STATIC) == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_int64(stmt, 4, size) == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_text(stmt, 5, sha256, -1, SQLITE_STATIC) == SQLITE_OK)) {
+        int r;
+        do {
+            r = sqlite3_step(stmt);
+            switch (r) {
+                case SQLITE_DONE:
+                    sqlite3_reset(stmt);
+                    break;
+                case SQLITE_BUSY:
+                    break;
+                default:
+                    reg_sqlite_error(reg-&gt;db, errPtr, insert);
+                    result = 0;
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg-&gt;db, errPtr, insert);
+        result = 0;
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    return result;
+}
+
+/**
+ * Gets a list of portgroups that are used by this port.
+ *
+ * @param [in] entry       a port
+ * @param [out] portgroups a list of portgroups used by the given port
+ * @param [out] errPtr     on error, a description of the error that occurred
+ * @return                 true if success; false if failure
+ */
+int reg_entry_getgroups(reg_entry* entry, reg_portgroup*** portgroups, reg_error* errPtr) {
+    reg_registry* reg = entry-&gt;reg;
+    char* query = sqlite3_mprintf(&quot;SELECT ROWID FROM portgroups &quot;
+            &quot;WHERE id=%lld&quot;,
+            entry-&gt;id);
+    int result = reg_all_portgroups(reg, query, -1, portgroups, errPtr);
+    sqlite3_free(query);
+    return result;
+}
+
+/**
</ins><span class="cx">  * Maps files to the given port in the filemap. The list of files must not
</span><span class="cx">  * contain files that are already mapped to the given port.
</span><span class="cx">  *
</span></span></pre></div>
<a id="trunkbasesrccregistryentryh"></a>
<div class="modfile"><h4>Modified: trunk/base/src/cregistry/entry.h (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/cregistry/entry.h        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/entry.h        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -71,6 +71,11 @@
</span><span class="cx"> int reg_entry_propset(reg_entry* entry, char* key, char* value,
</span><span class="cx">         reg_error* errPtr);
</span><span class="cx"> 
</span><ins>+int reg_entry_addgroup(reg_entry* entry, char* name, char *version,
+        char *sha256, sqlite_int64 size, reg_error* errPtr);
+int reg_entry_getgroups(reg_entry* entry, reg_portgroup*** portgroups,
+        reg_error* errPtr);
+
</ins><span class="cx"> int reg_entry_map(reg_entry* entry, char** files, int file_count,
</span><span class="cx">         reg_error* errPtr);
</span><span class="cx"> int reg_entry_unmap(reg_entry* entry, char** files, int file_count,
</span></span></pre></div>
<a id="trunkbasesrccregistryportgroupc"></a>
<div class="addfile"><h4>Added: trunk/base/src/cregistry/portgroup.c (0 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/cregistry/portgroup.c                                (rev 0)
+++ trunk/base/src/cregistry/portgroup.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -0,0 +1,318 @@
</span><ins>+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#if HAVE_CONFIG_H
+#include &lt;config.h&gt;
+#endif
+
+#include &quot;portgroup.h&quot;
+#include &quot;util.h&quot;
+#include &quot;registry.h&quot;
+#include &quot;sql.h&quot;
+
+#include &lt;sqlite3.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+
+/**
+ * Converts a `sqlite3_stmt` into a `reg_portgroup`. The first column of the stmt's
+ * row must be the id of a portgroup; the second either `SQLITE_NULL` or the
+ * address of the entry in memory.
+ *
+ * @param [in] userdata sqlite3 database
+ * @param [out] portgroup   portgroup described by `stmt`
+ * @param [in] stmt     `sqlite3_stmt` with appropriate columns
+ * @param [out] errPtr  unused
+ * @return              true if success; false if failure
+ */
+static int reg_stmt_to_portgroup(void* userdata, void** portgroup, void* stmt,
+        void* calldata UNUSED, reg_error* errPtr UNUSED) {
+    int is_new;
+    reg_registry* reg = (reg_registry*)userdata;
+    sqlite_int64 id = sqlite3_column_int64(stmt, 0);
+    Tcl_HashEntry* hash = Tcl_CreateHashEntry(&amp;reg-&gt;open_portgroups,
+            (const char*)&amp;id, &amp;is_new);
+    if (is_new) {
+        reg_portgroup* p = malloc(sizeof(reg_portgroup));
+        if (!p) {
+            return 0;
+        }
+        p-&gt;reg = reg;
+        p-&gt;id = id;
+        p-&gt;proc = NULL;
+        *portgroup = p;
+        Tcl_SetHashValue(hash, p);
+    } else {
+        *portgroup = Tcl_GetHashValue(hash);
+    }
+    return 1;
+}
+
+/**
+ * Type-safe version of `reg_all_objects` for `reg_portgroup`.
+ *
+ * @param [in] reg       registry to select entries from
+ * @param [in] query     the select query to execute
+ * @param [in] query_len length of the query (or -1 for automatic)
+ * @param [out] objects  the portgroups selected
+ * @param [out] errPtr   on error, a description of the error that occurred
+ * @return               the number of entries if success; negative if failure
+ */
+int reg_all_portgroups(reg_registry* reg, char* query, int query_len,
+        reg_portgroup*** objects, reg_error* errPtr) {
+    int lower_bound = 0;
+    return reg_all_objects(reg, query, query_len, (void***)objects,
+            reg_stmt_to_portgroup, &amp;lower_bound, NULL, errPtr);
+}
+
+/**
+ * Searches the registry for portgroups for which each key's value is equal to the
+ * given value. To find all portgroups, pass a key_count of 0.
+ *
+ * Bad keys should cause sqlite3 errors but not permit SQL injection attacks.
+ * Pass it good keys anyway.
+ *
+ * @param [in] reg       registry to search in
+ * @param [in] keys      a list of keys to search by
+ * @param [in] vals      a list of values to search by, matching keys
+ * @param [in] strats    a list of strategies to use when searching
+ * @param [in] key_count the number of key/value pairs passed
+ * @param [out] portgroups    a list of matching portgroups
+ * @param [out] errPtr   on error, a description of the error that occurred
+ * @return               the number of entries if success; false if failure
+ */
+int reg_portgroup_search(reg_registry* reg, char** keys, char** vals, int* strats,
+        int key_count, reg_portgroup*** portgroups, reg_error* errPtr) {
+    int i;
+    char* kwd = &quot; WHERE &quot;;
+    char* query;
+    size_t query_len, query_space;
+    int result;
+
+    /* build the query */
+    query = strdup(&quot;SELECT ROWID FROM registry.portgroups&quot;);
+    if (!query) {
+        return -1;
+    }
+    query_len = query_space = strlen(query);
+
+    for (i = 0; i &lt; key_count; i++) {
+        char* op;
+        char* cond;
+
+        /* get the strategy */
+        if ((op = reg_strategy_op(strats[i], errPtr)) == NULL) {
+            free(query);
+            return -1;
+        }
+
+        cond = sqlite3_mprintf(op, keys[i], vals[i]);
+        if (!cond || !reg_strcat(&amp;query, &amp;query_len, &amp;query_space, kwd)
+            || !reg_strcat(&amp;query, &amp;query_len, &amp;query_space, cond)) {
+            free(query);
+            return -1;
+        }
+        sqlite3_free(cond);
+        kwd = &quot; AND &quot;;
+    }
+
+    /* do the query */
+    result = reg_all_portgroups(reg, query, -1, portgroups, errPtr);
+    free(query);
+    return result;
+}
+
+/**
+ * Gets a named property of a portgroup. That property can be set using
+ * `reg_portgroup_propset`. The property named must be one that exists in the table
+ * and must not be one with internal meaning such as `id` or `state`.
+ *
+ * @param [in] portgroup   portgroup to get property from
+ * @param [in] key     property to get
+ * @param [out] value  the value of the property
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
+ */
+int reg_portgroup_propget(reg_portgroup* portgroup, char* key, char** value,
+        reg_error* errPtr) {
+    reg_registry* reg = portgroup-&gt;reg;
+    int result = 0;
+    sqlite3_stmt* stmt = NULL;
+    char* query;
+    const char *text;
+    query = sqlite3_mprintf(&quot;SELECT %q FROM registry.portgroups WHERE ROWID=%lld&quot;, key,
+            portgroup-&gt;id);
+    if (sqlite3_prepare_v2(reg-&gt;db, query, -1, &amp;stmt, NULL) == SQLITE_OK) {
+        int r;
+        do {
+            r = sqlite3_step(stmt);
+            switch (r) {
+                case SQLITE_ROW:
+                    text = (const char*)sqlite3_column_text(stmt, 0);
+                    if (text) {
+                        *value = strdup(text);
+                        result = 1;
+                    } else {
+                        reg_sqlite_error(reg-&gt;db, errPtr, query);
+                    }
+                    break;
+                case SQLITE_DONE:
+                    errPtr-&gt;code = REG_INVALID;
+                    errPtr-&gt;description = &quot;an invalid portgroup was passed&quot;;
+                    errPtr-&gt;free = NULL;
+                    break;
+                case SQLITE_BUSY:
+                    continue;
+                default:
+                    reg_sqlite_error(reg-&gt;db, errPtr, query);
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg-&gt;db, errPtr, query);
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    sqlite3_free(query);
+    return result;
+}
+
+/**
+ * Sets a named property of an portgroup. That property can be later retrieved using
+ * `reg_portgroup_propget`. The property named must be one that exists in the table
+ * and must not be one with internal meaning such as `id` or `state`. If `name`,
+ * `epoch`, `version`, `revision`, or `variants` is set, it could trigger a
+ * conflict if another port with the same combination of values for those
+ * columns exists.
+ *
+ * @param [in] portgroup   portgroup to set property for
+ * @param [in] key     property to set
+ * @param [in] value   the desired value of the property
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
+ */
+int reg_portgroup_propset(reg_portgroup* portgroup, char* key, char* value,
+        reg_error* errPtr) {
+    reg_registry* reg = portgroup-&gt;reg;
+    int result = 0;
+    sqlite3_stmt* stmt = NULL;
+    char* query;
+    query = sqlite3_mprintf(&quot;UPDATE registry.ports SET %q = '%q' WHERE ROWID=%lld&quot;,
+            key, value, portgroup-&gt;id);
+    if (sqlite3_prepare_v2(reg-&gt;db, query, -1, &amp;stmt, NULL) == SQLITE_OK) {
+        int r;
+        do {
+            r = sqlite3_step(stmt);
+            switch (r) {
+                case SQLITE_DONE:
+                    result = 1;
+                    break;
+                case SQLITE_BUSY:
+                    break;
+                default:
+                    if (sqlite3_reset(stmt) == SQLITE_CONSTRAINT) {
+                        errPtr-&gt;code = REG_CONSTRAINT;
+                        errPtr-&gt;description = &quot;a constraint was disobeyed&quot;;
+                        errPtr-&gt;free = NULL;
+                    } else {
+                        reg_sqlite_error(reg-&gt;db, errPtr, query);
+                    }
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg-&gt;db, errPtr, query);
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    sqlite3_free(query);
+    return result;
+}
+
+/**
+ * Opens an existing portgroup in the registry.
+ *
+ * @param [in] reg      registry to open portgroup in
+ * @param [in] id       id of entry referencing portgroup
+ * @param [in] name     name of portgroup
+ * @param [in] version  version of portgroup
+ * @param [in] size     size of portgroup
+ * @param [in] sha256   sha256 of portgroup
+ * @param [out] errPtr  on error, a description of the error that occurred
+ * @return              the portgroup if success; NULL if failure
+ */
+reg_portgroup* reg_portgroup_open(reg_registry* reg, char *id, char* name, char* version,
+        char* size, char* sha256, reg_error* errPtr) {
+    sqlite3_stmt* stmt = NULL;
+    reg_portgroup* portgroup = NULL;
+    int lower_bound = 0;
+    char* query;
+    query = &quot;SELECT ROWID FROM registry.portgroups WHERE id=? AND name=? AND version=? &quot;
+        &quot;AND size=? AND sha256=?&quot;;
+    if ((sqlite3_prepare_v2(reg-&gt;db, query, -1, &amp;stmt, NULL) == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_text(stmt, 1, id, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_text(stmt, 3, version, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_text(stmt, 4, size, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            &amp;&amp; (sqlite3_bind_text(stmt, 5, sha256, -1, SQLITE_STATIC)
+                == SQLITE_OK)) {
+        int r;
+        do {
+            r = sqlite3_step(stmt);
+            switch (r) {
+                case SQLITE_ROW:
+                    reg_stmt_to_portgroup(reg, (void**)&amp;portgroup, stmt, &amp;lower_bound, errPtr);
+                    break;
+                case SQLITE_DONE:
+                    errPtr-&gt;code = REG_NOT_FOUND;
+                    errPtr-&gt;description = sqlite3_mprintf(&quot;no matching portgroup found for: &quot; \
+                            &quot;id=%s, name=%s, version=%s, size=%s, sha256=%s&quot;, \
+                            id, name, version, size, sha256);
+                    errPtr-&gt;free = (reg_error_destructor*) sqlite3_free;
+                    break;
+                case SQLITE_BUSY:
+                    continue;
+                default:
+                    reg_sqlite_error(reg-&gt;db, errPtr, query);
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg-&gt;db, errPtr, query);
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    return portgroup;
+}
</ins></span></pre></div>
<a id="trunkbasesrccregistryportgrouph"></a>
<div class="addfile"><h4>Added: trunk/base/src/cregistry/portgroup.h (0 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/cregistry/portgroup.h                                (rev 0)
+++ trunk/base/src/cregistry/portgroup.h        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -0,0 +1,56 @@
</span><ins>+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+#ifndef _CPORTGROUP_H
+#define _CPORTGROUP_H
+
+#if HAVE_CONFIG_H
+#include &lt;config.h&gt;
+#endif
+
+#include &quot;registry.h&quot;
+
+#include &lt;sqlite3.h&gt;
+
+typedef struct {
+    sqlite_int64 id; /* rowid in the database */
+    reg_registry* reg; /* associated registry */
+    char* proc; /* name of Tcl proc, if using Tcl */
+} reg_portgroup;
+
+int reg_portgroup_search(reg_registry* reg, char** keys, char** vals, int* strats,
+        int key_count, reg_portgroup*** portgroups, reg_error* errPtr);
+int reg_all_portgroups(reg_registry* reg, char* query, int query_len,
+        reg_portgroup*** objects, reg_error* errPtr);
+reg_portgroup* reg_portgroup_open(reg_registry* reg, char *id, char* name, char* version,
+        char* size, char* sha256, reg_error* errPtr);
+int reg_portgroup_propget(reg_portgroup* portgroup, char* key, char** value,
+        reg_error* errPtr);
+int reg_portgroup_propset(reg_portgroup* portgroup, char* key, char* value,
+        reg_error* errPtr);
+
+#endif /* _CPORTGROUP_H */
</ins></span></pre></div>
<a id="trunkbasesrccregistryregistryc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/cregistry/registry.c (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/cregistry/registry.c        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/registry.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -4,7 +4,7 @@
</span><span class="cx">  * vim:expandtab:tw=80
</span><span class="cx">  *
</span><span class="cx">  * Copyright (c) 2007 Chris Pickel &lt;sfiera@macports.org&gt;
</span><del>- * Copyright (c) 2012 The MacPorts Project
</del><ins>+ * Copyright (c) 2012, 2014 The MacPorts Project
</ins><span class="cx">  * All rights reserved.
</span><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="lines">@@ -32,6 +32,7 @@
</span><span class="cx"> #include &lt;config.h&gt;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#include &quot;portgroup.h&quot;
</ins><span class="cx"> #include &quot;entry.h&quot;
</span><span class="cx"> #include &quot;file.h&quot;
</span><span class="cx"> #include &quot;sql.h&quot;
</span><span class="lines">@@ -208,6 +209,8 @@
</span><span class="cx">                                     sizeof(sqlite_int64)/sizeof(int));
</span><span class="cx">                             Tcl_InitHashTable(&amp;reg-&gt;open_files,
</span><span class="cx">                                     TCL_STRING_KEYS);
</span><ins>+                            Tcl_InitHashTable(&amp;reg-&gt;open_portgroups,
+                                    sizeof(sqlite_int64)/sizeof(int));
</ins><span class="cx">                             reg-&gt;status |= reg_attached;
</span><span class="cx">                             result = 1;
</span><span class="cx">                         }
</span></span></pre></div>
<a id="trunkbasesrccregistryregistryh"></a>
<div class="modfile"><h4>Modified: trunk/base/src/cregistry/registry.h (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/cregistry/registry.h        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/registry.h        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -4,7 +4,7 @@
</span><span class="cx">  * $Id$
</span><span class="cx">  *
</span><span class="cx">  * Copyright (c) 2007 Chris Pickel &lt;sfiera@macports.org&gt;
</span><del>- * Copyright (c) 2012 The MacPorts Project
</del><ins>+ * Copyright (c) 2012, 2014 The MacPorts Project
</ins><span class="cx">  * All rights reserved.
</span><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="lines">@@ -73,6 +73,7 @@
</span><span class="cx">     int status;
</span><span class="cx">     Tcl_HashTable open_entries;
</span><span class="cx">     Tcl_HashTable open_files;
</span><ins>+    Tcl_HashTable open_portgroups;
</ins><span class="cx"> } reg_registry;
</span><span class="cx"> 
</span><span class="cx"> int reg_open(reg_registry** regPtr, reg_error* errPtr);
</span></span></pre></div>
<a id="trunkbasesrccregistrysqlc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/cregistry/sql.c (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/cregistry/sql.c        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/sql.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -3,7 +3,7 @@
</span><span class="cx">  * $Id$
</span><span class="cx">  *
</span><span class="cx">  * Copyright (c) 2007 Chris Pickel &lt;sfiera@macports.org&gt;
</span><del>- * Copyright (c) 2012 The MacPorts Project
</del><ins>+ * Copyright (c) 2012, 2014 The MacPorts Project
</ins><span class="cx">  * All rights reserved.
</span><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="lines">@@ -129,13 +129,13 @@
</span><span class="cx"> 
</span><span class="cx">         /* metadata table */
</span><span class="cx">         &quot;CREATE TABLE registry.metadata (key UNIQUE, value)&quot;,
</span><del>-        &quot;INSERT INTO registry.metadata (key, value) VALUES ('version', 1.100)&quot;,
</del><ins>+        &quot;INSERT INTO registry.metadata (key, value) VALUES ('version', 1.200)&quot;,
</ins><span class="cx">         &quot;INSERT INTO registry.metadata (key, value) VALUES ('created', strftime('%s', 'now'))&quot;,
</span><span class="cx"> 
</span><span class="cx">         /* ports table */
</span><span class="cx">         &quot;CREATE TABLE registry.ports (&quot;
</span><del>-            &quot;id INTEGER PRIMARY KEY AUTOINCREMENT, &quot;
-            &quot;name TEXT COLLATE NOCASE, portfile CLOB, url TEXT, &quot;
</del><ins>+            &quot;id INTEGER PRIMARY KEY, &quot;
+            &quot;name TEXT COLLATE NOCASE, portfile TEXT, url TEXT, &quot;
</ins><span class="cx">             &quot;location TEXT, epoch INTEGER, version TEXT COLLATE VERSION, &quot;
</span><span class="cx">             &quot;revision INTEGER, variants TEXT, negated_variants TEXT, &quot;
</span><span class="cx">             &quot;state TEXT, date DATETIME, installtype TEXT, archs TEXT, &quot;
</span><span class="lines">@@ -163,6 +163,11 @@
</span><span class="cx">         &quot;FOREIGN KEY(id) REFERENCES ports(id))&quot;,
</span><span class="cx">         &quot;CREATE INDEX registry.dep_name ON dependencies (name)&quot;,
</span><span class="cx"> 
</span><ins>+        /* portgroups table */
+        &quot;CREATE TABLE registry.portgroups (id INTEGER, &quot;
+            &quot;name TEXT, version TEXT COLLATE VERSION, size INTEGER, sha256 TEXT, &quot;
+            &quot;FOREIGN KEY(id) REFERENCES ports(id))&quot;,
+
</ins><span class="cx">         &quot;COMMIT&quot;,
</span><span class="cx">         NULL
</span><span class="cx">     };
</span><span class="lines">@@ -321,6 +326,36 @@
</span><span class="cx">             continue;
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        if (sql_version(NULL, -1, version, -1, &quot;1.2&quot;) &lt; 0) {
+            /* We need to add the portgroup table and move the portfiles out
+               of the db and into the filesystem. The latter is way easier to do
+               from Tcl, so here we'll just flag that it needs to be done. */
+            static char* version_1_2_queries[] = {
+                /* portgroups table */
+                &quot;CREATE TABLE registry.portgroups (id INTEGER, &quot;
+                    &quot;name TEXT, version TEXT COLLATE VERSION, size INTEGER, sha256 TEXT, &quot;
+                    &quot;FOREIGN KEY(id) REFERENCES ports(id))&quot;,
+
+                &quot;UPDATE registry.metadata SET value = '1.200' WHERE key = 'version'&quot;,
+
+                &quot;INSERT INTO registry.metadata (key, value) VALUES ('portfiles_update_needed', 1)&quot;,
+
+                &quot;COMMIT&quot;,
+                NULL
+            };
+
+            sqlite3_finalize(stmt);
+            stmt = NULL;
+
+            if (!do_queries(db, version_1_2_queries, errPtr)) {
+                rollback_db(db);
+                return 0;
+            }
+
+            did_update = 1;
+            continue;
+        }
+
</ins><span class="cx">         /* add new versions here, but remember to:
</span><span class="cx">          *  - finalize the version query statement and set stmt to NULL
</span><span class="cx">          *  - do _not_ use &quot;BEGIN&quot; in your query list, since a transaction has
</span></span></pre></div>
<a id="trunkbasesrcdedup_portfilestcl"></a>
<div class="addfile"><h4>Added: trunk/base/src/dedup_portfiles.tcl (0 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/dedup_portfiles.tcl                                (rev 0)
+++ trunk/base/src/dedup_portfiles.tcl        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -0,0 +1,47 @@
</span><ins>+#!/usr/bin/env tclsh
+# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
+# $Id$
+
+# move portfiles from sqlite db to filesystem, while deduplicating
+# Takes one argument, which should be TCL_PACKAGE_DIR.
+
+source [file join [lindex $argv 0] macports1.0 macports_fastload.tcl]
+package require macports 1.0
+package require registry2 2.0
+package require Pextlib 1.0
+
+umask 022
+
+array set ui_options {ports_verbose yes}
+
+mportinit ui_options
+
+if {[registry::metadata get portfiles_update_needed] == 1} {
+    set portfiles_dir [file join ${macports::registry.path} registry portfiles]
+
+    registry::write {
+        set installed_ports [registry::entry imaged]
+        foreach portref $installed_ports {
+            set portfile_contents [$portref portfile]
+            if {$portfile_contents ne &quot;&quot; &amp;&amp; $portfile_contents ne &quot;0&quot;} {
+                set portfile_partial_dir [file join $portfiles_dir [$portref name]-[$portref version]_[$portref revision]]
+                file mkdir $portfile_partial_dir
+                set portfile_temp_path ${portfile_partial_dir}/Portfile
+                set fd [open $portfile_temp_path w]
+                puts $fd $portfile_contents
+                close $fd
+
+                set hash_size [sha256 file $portfile_temp_path]-[file size $portfile_temp_path]
+                set portfile_dir [file join $portfile_partial_dir $hash_size]
+                file mkdir $portfile_dir
+                file rename -force $portfile_temp_path $portfile_dir
+                file mtime ${portfile_dir}/Portfile [$portref date]
+
+                $portref portfile $hash_size
+            }
+        }
+        registry::metadata del portfiles_update_needed
+    }
+}
+
+exit 0
</ins><span class="cx">Property changes on: trunk/base/src/dedup_portfiles.tcl
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="svnkeywords"></a>
<div class="addfile"><h4>Added: svn:keywords</h4></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4>Added: svn:eol-style</h4></div>
<a id="trunkbasesrcmacports10macportstcl"></a>
<div class="modfile"><h4>Modified: trunk/base/src/macports1.0/macports.tcl (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/macports1.0/macports.tcl        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/macports1.0/macports.tcl        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -1363,12 +1363,12 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     foreach {opt val} $options {
</span><del>-        $workername eval set user_options($opt) $val
-        $workername eval set $opt $val
</del><ins>+        $workername eval [list set user_options($opt) $val]
+        $workername eval [list set $opt $val]
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     foreach {var val} $variations {
</span><del>-        $workername eval set variations($var) $val
</del><ins>+        $workername eval [list set variations($var) $val]
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -1715,12 +1715,7 @@
</span><span class="cx"> proc mportopen_installed {name version revision variants options} {
</span><span class="cx">     global macports::registry.path
</span><span class="cx">     set regref [lindex [registry::entry imaged $name $version $revision $variants] 0]
</span><del>-    set portfile_dir [file join ${registry.path} registry portfiles $name ${version}_${revision}$variants]
-    file mkdir $portfile_dir
-    set fd [open ${portfile_dir}/Portfile w]
-    puts $fd [$regref portfile]
-    close $fd
-    file mtime ${portfile_dir}/Portfile [$regref date]
</del><ins>+    set portfile_dir [file join ${registry.path} registry portfiles ${name}-${version}_${revision} [$regref portfile]]
</ins><span class="cx"> 
</span><span class="cx">     set variations {}
</span><span class="cx">     set minusvariant [lrange [split [$regref negated_variants] -] 1 end]
</span><span class="lines">@@ -1732,23 +1727,17 @@
</span><span class="cx">         lappend variations $v -
</span><span class="cx">     }
</span><span class="cx">     lappend options subport $name
</span><del>-    return [mportopen file://${portfile_dir}/ $options $variations]
-}
</del><span class="cx"> 
</span><del>-# mportclose_installed
-# close mport opened with mportopen_installed and clean up associated files
-proc mportclose_installed {mport} {
-    global macports::registry.path
-    foreach key {subport version revision portvariants} {
-        set $key [_mportkey $mport $key]
</del><ins>+    # find portgroups in registry
+    set pgdirlist [list]
+    foreach pg [$regref groups_used] {
+        lappend pgdirlist [file join ${registry.path} registry portgroups [$pg sha256]-[$pg size]]
</ins><span class="cx">     }
</span><del>-    mportclose $mport
-    set portfiles_dir [file join ${registry.path} registry portfiles $subport]
-    set portfile [file join $portfiles_dir ${version}_${revision}$portvariants Portfile]
-    file delete -force $portfile [file dirname $portfile]
-    if {[llength [glob -nocomplain -directory $portfiles_dir *]] == 0} {
-        file delete -force $portfiles_dir
</del><ins>+    if {$pgdirlist ne {}} {
+        lappend options _portgroup_search_dirs $pgdirlist
</ins><span class="cx">     }
</span><ins>+
+    return [mportopen file://${portfile_dir}/ $options $variations]
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> # Traverse a directory with ports, calling a function on the path of ports
</span></span></pre></div>
<a id="trunkbasesrcmacports10testsmacportstest"></a>
<div class="modfile"><h4>Modified: trunk/base/src/macports1.0/tests/macports.test (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/macports1.0/tests/macports.test        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/macports1.0/tests/macports.test        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -153,10 +153,10 @@
</span><span class="cx">        return &quot;FAIL: installed port not opened&quot;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if {[catch {mportclose_installed $res}] != 0} {
</del><ins>+    if {[catch {mportclose $res}] != 0} {
</ins><span class="cx">        return &quot;FAIL: cannot close port&quot;
</span><span class="cx">     }
</span><del>-    if {[catch {mportclose_installed $res}] != 1} {
</del><ins>+    if {[catch {mportclose $res}] != 1} {
</ins><span class="cx">        return &quot;FAIL: installed port not closed&quot;
</span><span class="cx">     }
</span><span class="cx">     return &quot;Installed port open successful.&quot;
</span><span class="lines">@@ -702,10 +702,6 @@
</span><span class="cx"> # test mportopen
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-# Covered by mportopen_installed
-# test mportclose_installed
-
-
</del><span class="cx"> test mporttraverse {
</span><span class="cx">     Mport traverse unit test. Uses 3rd column of the Portfile.
</span><span class="cx"> } -setup {
</span></span></pre></div>
<a id="trunkbasesrcpextlib10tracelibc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/pextlib1.0/tracelib.c (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/pextlib1.0/tracelib.c        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/pextlib1.0/tracelib.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -56,6 +56,7 @@
</span><span class="cx"> #include &lt;sys/un.h&gt;
</span><span class="cx"> #include &lt;unistd.h&gt;
</span><span class="cx"> 
</span><ins>+#include &lt;cregistry/portgroup.h&gt;
</ins><span class="cx"> #include &lt;cregistry/entry.h&gt;
</span><span class="cx"> #include &lt;registry2.0/registry.h&gt;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbasesrcport10portinstalltcl"></a>
<div class="modfile"><h4>Modified: trunk/base/src/port1.0/portinstall.tcl (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/port1.0/portinstall.tcl        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/port1.0/portinstall.tcl        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -307,7 +307,7 @@
</span><span class="cx">     homepage depends_run package-install workdir workpath \
</span><span class="cx">     worksrcdir UI_PREFIX destroot revision maintainers user_options \
</span><span class="cx">     portvariants negated_variants targets depends_lib PortInfo epoch license \
</span><del>-    os.platform os.major portarchivetype installPlist
</del><ins>+    os.platform os.major portarchivetype installPlist registry.path porturl
</ins><span class="cx"> 
</span><span class="cx">     set oldpwd [pwd]
</span><span class="cx">     if {$oldpwd eq &quot;&quot;} {
</span><span class="lines">@@ -373,11 +373,40 @@
</span><span class="cx">             # register files
</span><span class="cx">             $regref map $installPlist
</span><span class="cx">         }
</span><del>-        
</del><ins>+
</ins><span class="cx">         # store portfile
</span><del>-        set fd [open [file join ${portpath} Portfile]]
-        $regref portfile [read $fd]
-        close $fd
</del><ins>+        set portfile_path [file join $portpath Portfile]
+        set portfile_sha256 [sha256 file $portfile_path]
+        set portfile_size [file size $portfile_path]
+        set portfile_reg_dir [file join ${registry.path} registry portfiles ${subport}-${version}_${revision} ${portfile_sha256}-${portfile_size}]
+        file mkdir $portfile_reg_dir
+        set portfile_reg_path ${portfile_reg_dir}/Portfile
+        if {![file isfile $portfile_reg_path] || [file size $portfile_reg_path] != $portfile_size || [sha256 file $portfile_reg_path] ne $portfile_sha256} {
+            file copy -force $portfile_path $portfile_reg_dir
+        }
+        $regref portfile ${portfile_sha256}-${portfile_size}
+
+        # store portgroups
+        if {[info exists PortInfo(portgroups)]} {
+            foreach pg $PortInfo(portgroups) {
+                set pgname [lindex $pg 0]
+                set pgversion [lindex $pg 1]
+                set groupFile [getportresourcepath $porturl &quot;port1.0/group/${pgname}-${pgversion}.tcl&quot;]
+                if {[file isfile $groupFile]} {
+                    set pgsha256 [sha256 file $groupFile]
+                    set pgsize [file size $groupFile]
+                    set pg_reg_dir [file join ${registry.path} registry portgroups ${pgsha256}-${pgsize}]
+                    set pg_reg_path ${pg_reg_dir}/${pgname}-${pgversion}.tcl
+                    if {![file isfile $pg_reg_path] || [file size $pg_reg_path] != $pgsize || [sha256 file $pg_reg_path] ne $pgsha256} {
+                        file mkdir $pg_reg_dir
+                        file copy -force $groupFile $pg_reg_dir
+                    }
+                    $regref addgroup $pgname $pgversion $pgsha256 $pgsize
+                } else {
+                    ui_debug &quot;install_main: no portgroup ${pgname}-${pgversion}.tcl found&quot;
+                }
+            }
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _cd $oldpwd
</span></span></pre></div>
<a id="trunkbasesrcport10portutiltcl"></a>
<div class="modfile"><h4>Modified: trunk/base/src/port1.0/portutil.tcl (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/port1.0/portutil.tcl        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/port1.0/portutil.tcl        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -2569,12 +2569,12 @@
</span><span class="cx"> 
</span><span class="cx"> # Use a specified group/version.
</span><span class="cx"> proc PortGroup {group version} {
</span><del>-    global porturl PortInfo
</del><ins>+    global porturl PortInfo _portgroup_search_dirs
</ins><span class="cx"> 
</span><span class="cx">     lappend PortInfo(portgroups) [list $group $version]
</span><span class="cx"> 
</span><del>-    if {[info exists portutil::portgroup_search_dirs]} {
-        foreach dir $portutil::portgroup_search_dirs {
</del><ins>+    if {[info exists _portgroup_search_dirs]} {
+        foreach dir $_portgroup_search_dirs {
</ins><span class="cx">             set groupFile ${dir}/${group}-${version}.tcl
</span><span class="cx">             if {[file exists $groupFile]} {
</span><span class="cx">                 uplevel &quot;source $groupFile&quot;
</span></span></pre></div>
<a id="trunkbasesrcregistry20Makefile"></a>
<div class="modfile"><h4>Modified: trunk/base/src/registry2.0/Makefile (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/Makefile        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/Makefile        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -3,7 +3,8 @@
</span><span class="cx"> SRCS = registry.tcl registry_autoconf.tcl registry_util.tcl receipt_flat.tcl receipt_sqlite.tcl portimage.tcl portuninstall.tcl
</span><span class="cx"> OBJS = registry.o util.o \
</span><span class="cx">         entry.o entryobj.o \
</span><del>-        file.o fileobj.o
</del><ins>+        file.o fileobj.o \
+        portgroup.o portgroupobj.o
</ins><span class="cx">         #graph.o graphobj.o
</span><span class="cx"> 
</span><span class="cx"> SHLIB_NAME= registry${SHLIB_SUFFIX}
</span></span></pre></div>
<a id="trunkbasesrcregistry20entryobjc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/registry2.0/entryobj.c (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/entryobj.c        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/entryobj.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -356,6 +356,59 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static int entry_obj_add_portgroup(Tcl_Interp* interp, reg_entry* entry, int objc,
+        Tcl_Obj* CONST objv[]) {
+    reg_registry* reg = registry_for(interp, reg_attached);
+    if (objc != 6) {
+        Tcl_WrongNumArgs(interp, 1, objv, &quot;addgroup name version sha256 size&quot;);
+        return TCL_ERROR;
+    } else if (reg == NULL) {
+        return TCL_ERROR;
+    } else {
+        reg_error error;
+        char* name = Tcl_GetString(objv[2]);
+        char* version = Tcl_GetString(objv[3]);
+        char* sha256 = Tcl_GetString(objv[4]);
+        Tcl_WideInt tclsize;
+        Tcl_GetWideIntFromObj(interp, objv[5], &amp;tclsize);
+        sqlite_int64 size = (sqlite_int64)tclsize;
+        if (reg_entry_addgroup(entry, name, version, sha256, size, &amp;error)) {
+            return TCL_OK;
+        }
+        return registry_failed(interp, &amp;error);
+    }
+}
+
+static int entry_obj_get_portgroups(Tcl_Interp* interp, reg_entry* entry, int objc,
+        Tcl_Obj* CONST objv[]) {
+    reg_registry* reg = registry_for(interp, reg_attached);
+    if (objc != 2) {
+        Tcl_WrongNumArgs(interp, 1, objv, &quot;groups_used&quot;);
+        return TCL_ERROR;
+    } else if (reg == NULL) {
+        return TCL_ERROR;
+    } else {
+        reg_portgroup** portgroups;
+        reg_error error;
+        int portgroup_count = reg_entry_getgroups(entry, &amp;portgroups, &amp;error);
+        if (portgroup_count &gt;= 0) {
+            Tcl_Obj** objs;
+            int retval = TCL_ERROR;
+            if (list_portgroup_to_obj(interp, &amp;objs, portgroups, portgroup_count, &amp;error)){
+                Tcl_Obj* result = Tcl_NewListObj(portgroup_count, objs);
+                Tcl_SetObjResult(interp, result);
+                free(objs);
+                retval = TCL_OK;
+            } else {
+                retval = registry_failed(interp, &amp;error);
+            }
+            free(portgroups);
+            return retval;
+        }
+        return registry_failed(interp, &amp;error);
+    }
+}
+
</ins><span class="cx"> typedef struct {
</span><span class="cx">     char* name;
</span><span class="cx">     int (*function)(Tcl_Interp* interp, reg_entry* entry, int objc,
</span><span class="lines">@@ -391,6 +444,9 @@
</span><span class="cx">     { &quot;dependents&quot;, entry_obj_dependents },
</span><span class="cx">     { &quot;dependencies&quot;, entry_obj_dependencies },
</span><span class="cx">     { &quot;depends&quot;, entry_obj_depends },
</span><ins>+    /* portgroups */
+    { &quot;addgroup&quot;, entry_obj_add_portgroup },
+    { &quot;groups_used&quot;, entry_obj_get_portgroups },
</ins><span class="cx">     { NULL, NULL }
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbasesrcregistry20portgroupc"></a>
<div class="addfile"><h4>Added: trunk/base/src/registry2.0/portgroup.c (0 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/portgroup.c                                (rev 0)
+++ trunk/base/src/registry2.0/portgroup.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -0,0 +1,294 @@
</span><ins>+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#if HAVE_CONFIG_H
+#include &lt;config.h&gt;
+#endif
+
+#include &lt;sqlite3.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+#include &lt;tcl.h&gt;
+
+#include &lt;cregistry/portgroup.h&gt;
+#include &lt;cregistry/util.h&gt;
+
+#include &quot;portgroup.h&quot;
+#include &quot;portgroupobj.h&quot;
+#include &quot;registry.h&quot;
+#include &quot;util.h&quot;
+
+/**
+ * Converts a command name into a `reg_portgroup`.
+ *
+ * @param [in] interp  Tcl interpreter to check within
+ * @param [in] name    name of portgroup to get
+ * @param [out] errPtr description of error if the portgroup can't be found
+ * @return             a portgroup, or NULL if one couldn't be found
+ * @see get_object
+ */
+static reg_portgroup* get_portgroup(Tcl_Interp* interp, char* name, reg_error* errPtr) {
+    return (reg_portgroup*)get_object(interp, name, &quot;portgroup&quot;, portgroup_obj_cmd, errPtr);
+}
+
+/**
+ * Removes the portgroup from the Tcl interpreter. Doesn't actually delete it since
+ * that's the registry's job. This is written to be used as the
+ * `Tcl_CmdDeleteProc` for an portgroup object command.
+ *
+ * @param [in] clientData address of a reg_portgroup to remove
+ */
+void delete_portgroup(ClientData clientData) {
+    reg_portgroup* portgroup = (reg_portgroup*)clientData;
+    free(portgroup-&gt;proc);
+    portgroup-&gt;proc = NULL;
+}
+
+/**
+ * registry::portgroup open id name version size sha256
+ *
+ * Opens a portgroup matching the given parameters.
+ */
+static int portgroup_open(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
+        reg_registry* reg = registry_for(interp, reg_attached);
+        if (objc != 7) {
+                Tcl_WrongNumArgs(interp, 1, objv, &quot;open id name version size sha256&quot;);
+                return TCL_ERROR;
+        } else if (reg == NULL) {
+                return TCL_ERROR;
+        } else {
+                char* id = Tcl_GetString(objv[2]);
+                char* name = Tcl_GetString(objv[3]);
+        char* version = Tcl_GetString(objv[4]);
+        char* size = Tcl_GetString(objv[5]);
+        char* sha256 = Tcl_GetString(objv[6]);
+
+                reg_error error;
+                reg_portgroup* portgroup = reg_portgroup_open(reg, id, name, version, size, sha256, &amp;error);
+                if (portgroup != NULL) {
+                        Tcl_Obj* result;
+                        if (portgroup_to_obj(interp, &amp;result, portgroup, NULL, &amp;error)) {
+                                Tcl_SetObjResult(interp, result);
+                                return TCL_OK;
+                        }
+                }
+                return registry_failed(interp, &amp;error);
+        }
+        return TCL_ERROR;
+}
+
+/**
+ * registry::portgroup close portgroup
+ *
+ * Closes a portgroup. It will remain in the registry.
+ */
+static int portgroup_close(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
+        if (objc != 3) {
+                Tcl_WrongNumArgs(interp, 1, objv, &quot;close portgroup&quot;);
+                return TCL_ERROR;
+        } else {
+                reg_error error;
+                char* proc = Tcl_GetString(objv[2]);
+                reg_portgroup* portgroup = get_portgroup(interp, proc, &amp;error);
+                if (portgroup == NULL) {
+                        return registry_failed(interp, &amp;error);
+                } else {
+                        Tcl_DeleteCommand(interp, proc);
+                        return TCL_OK;
+                }
+        }
+}
+
+typedef struct {
+    char* name;
+    reg_strategy strategy;
+} strategy_type;
+
+static strategy_type strategies[] = {
+    { &quot;-exact&quot;,  reg_strategy_exact },
+    { &quot;-glob&quot;,   reg_strategy_glob },
+    { &quot;-regexp&quot;, reg_strategy_regexp },
+    { &quot;-null&quot;,   reg_strategy_null },
+    { &quot;--&quot;,      reg_strategy_exact },
+    { NULL, 0 }
+};
+
+/*
+ * registry::portgroup search ?key value ...?
+ *
+ * Searches the registry for portgroups for which each key's value is equal to the
+ * given value. To find all portgroups, call `portgroup search` with no key-value pairs.
+ * For each key, can be given an option of -exact, -glob, -regexp or -null to
+ * specify the matching strategy; defaults to exact.
+ */
+static int portgroup_search(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
+    int i, j;
+    reg_registry* reg = registry_for(interp, reg_attached);
+    if (reg == NULL) {
+        return TCL_ERROR;
+    } else {
+        char** keys;
+        char** vals;
+        int* strats;
+        int key_count = 0;
+        reg_portgroup** portgroups;
+        reg_error error;
+        int portgroup_count;
+        for (i = 2; i &lt; objc;) {
+            int index, strat_index, val_length;
+            if (Tcl_GetIndexFromObj(interp, objv[i], portgroup_props, &quot;search key&quot;,
+                        0, &amp;index) != TCL_OK) {
+                return TCL_ERROR;
+            }
+
+            /* we ate the key value */
+            i++;
+
+            /* check whether there's a strategy */
+            if (Tcl_GetString(objv[i])[0] == '-'
+                    &amp;&amp; Tcl_GetIndexFromObjStruct(interp, objv[i], strategies,
+                        sizeof(strategy_type), &quot;option&quot;, 0, &amp;strat_index)
+                    != TCL_ERROR) {
+                /* this key has a strategy specified, eat the strategy parameter */
+                i++;
+
+                if (strategies[strat_index].strategy != reg_strategy_null) {
+                    /* this key must also have a value */
+
+                    if (Tcl_GetStringFromObj(objv[i], &amp;val_length) == NULL
+                            || val_length == 0) {
+                        Tcl_WrongNumArgs(interp, 2, objv,
+                                &quot;search ?key ?options? value ...?&quot;);
+                        return TCL_ERROR;
+                    }
+
+                    i++;
+                }
+            } else {
+                /* this key must also have a value */
+
+                if (Tcl_GetStringFromObj(objv[i], &amp;val_length) == NULL
+                        || val_length == 0) {
+                    Tcl_WrongNumArgs(interp, 2, objv,
+                            &quot;search ?key ?options? value ...?&quot;);
+                    return TCL_ERROR;
+                }
+
+                i++;
+            }
+
+            key_count++;
+        }
+
+        keys = malloc(key_count * sizeof(char*));
+        vals = malloc(key_count * sizeof(char*));
+        strats = malloc(key_count * sizeof(int));
+        if (!keys || !vals || !strats) {
+            return TCL_ERROR;
+        }
+        for (i = 2, j = 0; i &lt; objc &amp;&amp; j &lt; key_count; j++) {
+            int strat_index;
+
+            keys[j] = Tcl_GetString(objv[i++]);
+
+            /* try to get the strategy */
+            if (Tcl_GetString(objv[i])[0] == '-'
+                    &amp;&amp; Tcl_GetIndexFromObjStruct(interp, objv[i], strategies,
+                        sizeof(strategy_type), &quot;option&quot;, 0, &amp;strat_index)
+                    != TCL_ERROR) {
+                /* this key has a strategy specified */
+                i++;
+
+                strats[j] = strategies[strat_index].strategy;
+            } else {
+                /* use default strategy */
+                strats[j] = reg_strategy_exact;
+            }
+
+            if (strats[j] != reg_strategy_null) {
+                vals[j] = Tcl_GetString(objv[i++]);
+            } else {
+                vals[j] = NULL;
+            }
+        }
+        portgroup_count = reg_portgroup_search(reg, keys, vals, strats, key_count,
+                &amp;portgroups, &amp;error);
+        free(keys);
+        free(vals);
+        free(strats);
+        if (portgroup_count &gt;= 0) {
+            int retval;
+            Tcl_Obj* resultObj;
+            Tcl_Obj** objs;
+            if (list_portgroup_to_obj(interp, &amp;objs, portgroups, portgroup_count, &amp;error)){
+                resultObj = Tcl_NewListObj(portgroup_count, objs);
+                Tcl_SetObjResult(interp, resultObj);
+                free(objs);
+                retval = TCL_OK;
+            } else {
+                retval = registry_failed(interp, &amp;error);
+            }
+            free(portgroups);
+            return retval;
+        }
+        return registry_failed(interp, &amp;error);
+    }
+}
+
+typedef struct {
+    char* name;
+    int (*function)(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
+} portgroup_cmd_type;
+
+static portgroup_cmd_type portgroup_cmds[] = {
+    /* Global commands */
+    { &quot;open&quot;, portgroup_open },
+    { &quot;close&quot;, portgroup_close },
+    { &quot;search&quot;, portgroup_search },
+    { NULL, NULL }
+};
+
+/*
+ * registry::portgroup cmd ?arg ...?
+ *
+ * Commands manipulating portgroup entries in the registry. This can be called `registry::portgroup`
+ */
+int portgroup_cmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]) {
+    int cmd_index;
+    if (objc &lt; 2) {
+        Tcl_WrongNumArgs(interp, 1, objv, &quot;cmd ?arg ...?&quot;);
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObjStruct(interp, objv[1], portgroup_cmds,
+                sizeof(portgroup_cmd_type), &quot;cmd&quot;, 0, &amp;cmd_index) == TCL_OK) {
+        portgroup_cmd_type* cmd = &amp;portgroup_cmds[cmd_index];
+        return cmd-&gt;function(interp, objc, objv);
+    }
+    return TCL_ERROR;
+}
</ins></span></pre></div>
<a id="trunkbasesrcregistry20portgrouph"></a>
<div class="addfile"><h4>Added: trunk/base/src/registry2.0/portgroup.h (0 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/portgroup.h                                (rev 0)
+++ trunk/base/src/registry2.0/portgroup.h        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -0,0 +1,42 @@
</span><ins>+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+#ifndef _PORTGROUP_H
+#define _PORTGROUP_H
+
+#if HAVE_CONFIG_H
+#include &lt;config.h&gt;
+#endif
+
+#include &lt;tcl.h&gt;
+
+void delete_portgroup(ClientData clientData);
+
+int portgroup_cmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]);
+
+#endif /* _PORTGROUP_H */
</ins></span></pre></div>
<a id="trunkbasesrcregistry20portgroupobjc"></a>
<div class="addfile"><h4>Added: trunk/base/src/registry2.0/portgroupobj.c (0 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/portgroupobj.c                                (rev 0)
+++ trunk/base/src/registry2.0/portgroupobj.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -0,0 +1,135 @@
</span><ins>+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#if HAVE_CONFIG_H
+#include &lt;config.h&gt;
+#endif
+
+#include &lt;string.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;tcl.h&gt;
+#include &lt;sqlite3.h&gt;
+
+#include &quot;portgroupobj.h&quot;
+#include &quot;registry.h&quot;
+#include &quot;util.h&quot;
+
+const char* portgroup_props[] = {
+    &quot;name&quot;,
+    &quot;version&quot;,
+    &quot;size&quot;,
+    &quot;sha256&quot;,
+    NULL
+};
+
+/* ${portgroup} prop ?value? */
+static int portgroup_obj_prop(Tcl_Interp* interp, reg_portgroup* portgroup, int objc,
+        Tcl_Obj* CONST objv[]) {
+    int index;
+    if (objc &gt; 3) {
+        Tcl_WrongNumArgs(interp, 2, objv, &quot;?value?&quot;);
+        return TCL_ERROR;
+    }
+    if (objc == 2) {
+        /* ${portgroup} prop; return the current value */
+        reg_registry* reg = registry_for(interp, reg_attached);
+        if (reg == NULL) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetIndexFromObj(interp, objv[1], portgroup_props, &quot;prop&quot;, 0, &amp;index)
+                == TCL_OK) {
+            char* key = Tcl_GetString(objv[1]);
+            char* value;
+            reg_error error;
+            if (reg_portgroup_propget(portgroup, key, &amp;value, &amp;error)) {
+                Tcl_Obj* result = Tcl_NewStringObj(value, -1);
+                Tcl_SetObjResult(interp, result);
+                free(value);
+                return TCL_OK;
+            }
+            return registry_failed(interp, &amp;error);
+        }
+        return TCL_ERROR;
+    } else {
+        /* ${portgroup} prop name value; set a new value */
+        reg_registry* reg = registry_for(interp, reg_attached);
+        if (reg == NULL) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetIndexFromObj(interp, objv[1], portgroup_props, &quot;prop&quot;, 0, &amp;index)
+                == TCL_OK) {
+            char* key = Tcl_GetString(objv[1]);
+            char* value = Tcl_GetString(objv[2]);
+            reg_error error;
+            if (reg_portgroup_propset(portgroup, key, value, &amp;error)) {
+                return TCL_OK;
+            }
+            return registry_failed(interp, &amp;error);
+        }
+        return TCL_ERROR;
+    }
+}
+
+typedef struct {
+    char* name;
+    int (*function)(Tcl_Interp* interp, reg_portgroup* portgroup, int objc,
+            Tcl_Obj* CONST objv[]);
+} portgroup_obj_cmd_type;
+
+static portgroup_obj_cmd_type portgroup_cmds[] = {
+    /* keys */
+    { &quot;name&quot;, portgroup_obj_prop },
+    { &quot;version&quot;, portgroup_obj_prop },
+    { &quot;size&quot;, portgroup_obj_prop },
+    { &quot;sha256&quot;, portgroup_obj_prop },
+    { NULL, NULL }
+};
+
+/* ${portgroup} cmd ?arg ...? */
+/* This function implements the command that will be called when a portgroup
+ * created by `registry::portgroup` is used as a procedure. Since all data is kept
+ * in a temporary sqlite3 database that is created for the current interpreter,
+ * none of the sqlite3 functions used have any error checking. That should be a
+ * safe assumption, since nothing outside of registry:: should ever have the
+ * chance to touch it.
+ */
+int portgroup_obj_cmd(ClientData clientData, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]) {
+    int cmd_index;
+    if (objc &lt; 2) {
+        Tcl_WrongNumArgs(interp, 1, objv, &quot;cmd ?arg ...?&quot;);
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObjStruct(interp, objv[1], portgroup_cmds,
+                sizeof(portgroup_obj_cmd_type), &quot;cmd&quot;, 0, &amp;cmd_index) == TCL_OK) {
+        portgroup_obj_cmd_type* cmd = &amp;portgroup_cmds[cmd_index];
+        return cmd-&gt;function(interp, (reg_portgroup*)clientData, objc, objv);
+    }
+    return TCL_ERROR;
+}
+
</ins></span></pre></div>
<a id="trunkbasesrcregistry20portgroupobjh"></a>
<div class="addfile"><h4>Added: trunk/base/src/registry2.0/portgroupobj.h (0 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/portgroupobj.h                                (rev 0)
+++ trunk/base/src/registry2.0/portgroupobj.h        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+#ifndef _PORTGROUP_OBJ_CMD_H
+#define _PORTGROUP_OBJ_CMD_H
+
+#if HAVE_CONFIG_H
+#include &lt;config.h&gt;
+#endif
+
+#include &lt;tcl.h&gt;
+#include &lt;sqlite3.h&gt;
+
+extern const char* portgroup_props[];
+
+int portgroup_obj_cmd(ClientData clientData, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]);
+
+#endif /* _PORTGROUP_OBJ_CMD_H */
</ins></span></pre></div>
<a id="trunkbasesrcregistry20portuninstalltcl"></a>
<div class="modfile"><h4>Modified: trunk/base/src/registry2.0/portuninstall.tcl (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/portuninstall.tcl        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/portuninstall.tcl        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -53,7 +53,7 @@
</span><span class="cx"> 
</span><span class="cx"> proc uninstall {portname {version &quot;&quot;} {revision &quot;&quot;} {variants 0} {optionslist &quot;&quot;}} {
</span><span class="cx">     global uninstall.force uninstall.nochecksum UI_PREFIX \
</span><del>-           macports::portimagefilepath
</del><ins>+           macports::portimagefilepath macports::registry.path
</ins><span class="cx">     array set options $optionslist
</span><span class="cx">     if {[info exists options(subport)]} {
</span><span class="cx">         # don't want this set when calling registry::run_target
</span><span class="lines">@@ -156,7 +156,7 @@
</span><span class="cx">         # look up deps from the saved portfile if possible
</span><span class="cx">         if {![catch {set mport [mportopen_installed [$port name] [$port version] [$port revision] [$port variants] $optionslist]}]} {
</span><span class="cx">             array set depportinfo [mportinfo $mport]
</span><del>-            mportclose_installed $mport
</del><ins>+            mportclose $mport
</ins><span class="cx">             foreach type $deptypes {
</span><span class="cx">                 if {[info exists depportinfo($type)]} {
</span><span class="cx">                     foreach dep $depportinfo($type) {
</span><span class="lines">@@ -217,9 +217,37 @@
</span><span class="cx">         # files so just ignore the failure
</span><span class="cx">         catch {file delete [::file dirname $imagefile]}
</span><span class="cx"> 
</span><ins>+        # We want to delete the portfile if not referenced by any other ports
+        set portfile [$ref portfile]
+
+        # and likewise the portgroups
+        set portgroups [list]
+        foreach pg [$ref groups_used] {
+            lappend portgroups [list [$pg name] [$pg version] [$pg size] [$pg sha256]]
+        }
+
</ins><span class="cx">         registry::write {
</span><span class="cx">             registry::entry delete $port
</span><span class="cx">         }
</span><ins>+
+        set portfile_path [file join ${registry.path} registry portfiles ${portname}-${version}_${revision} $portfile]
+        if {[registry::entry search portfile $portfile] eq {}} {
+            file delete -force $portfile_path
+            catch {file delete [file dirname $portfile_path]}
+        }
+
+        set reg_portgroups_dir [file join ${registry.path} registry portgroups]
+        foreach pg $portgroups {
+            set pgname [lindex $pg 0]
+            set pgversion [lindex $pg 1]
+            set pgsize [lindex $pg 2]
+            set pgsha256 [lindex $pg 3]
+            if {[registry::portgroup search name $pgname version $pgversion size $pgsize sha256 $pgsha256] eq {}} {
+                set pg_reg_dir [file join $reg_portgroups_dir ${pgsha256}-${pgsize}]
+                file delete -force ${pg_reg_dir}/${pgname}-${pgversion}.tcl
+                catch {file delete $pg_reg_dir}
+            }
+        }
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     # uninstall dependencies if requested
</span></span></pre></div>
<a id="trunkbasesrcregistry20registryc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/registry2.0/registry.c (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/registry.c        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/registry.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -3,7 +3,7 @@
</span><span class="cx">  * $Id$
</span><span class="cx">  *
</span><span class="cx">  * Copyright (c) 2007 Chris Pickel &lt;sfiera@macports.org&gt;
</span><del>- * Copyright (c) 2012 The MacPorts Project
</del><ins>+ * Copyright (c) 2012, 2014 The MacPorts Project
</ins><span class="cx">  * All rights reserved.
</span><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="lines">@@ -38,6 +38,7 @@
</span><span class="cx"> #include &lt;tcl.h&gt;
</span><span class="cx"> 
</span><span class="cx"> #include &lt;cregistry/registry.h&gt;
</span><ins>+#include &lt;cregistry/portgroup.h&gt;
</ins><span class="cx"> #include &lt;cregistry/entry.h&gt;
</span><span class="cx"> #include &lt;cregistry/file.h&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -46,6 +47,7 @@
</span><span class="cx"> #include &quot;file.h&quot;
</span><span class="cx"> #include &quot;graph.h&quot;
</span><span class="cx"> #include &quot;item.h&quot;
</span><ins>+#include &quot;portgroup.h&quot;
</ins><span class="cx"> #include &quot;registry.h&quot;
</span><span class="cx"> #include &quot;util.h&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -389,6 +391,7 @@
</span><span class="cx">     /* Tcl_CreateObjCommand(interp, &quot;registry::item&quot;, item_cmd, NULL, NULL); */
</span><span class="cx">     Tcl_CreateObjCommand(interp, &quot;registry::entry&quot;, entry_cmd, NULL, NULL);
</span><span class="cx">     Tcl_CreateObjCommand(interp, &quot;registry::file&quot;, file_cmd, NULL, NULL);
</span><ins>+    Tcl_CreateObjCommand(interp, &quot;registry::portgroup&quot;, portgroup_cmd, NULL, NULL);
</ins><span class="cx">     Tcl_CreateObjCommand(interp, &quot;registry::metadata&quot;, metadata_cmd, NULL, NULL);
</span><span class="cx">     if (Tcl_PkgProvide(interp, &quot;registry2&quot;, &quot;2.0&quot;) != TCL_OK) {
</span><span class="cx">         return TCL_ERROR;
</span></span></pre></div>
<a id="trunkbasesrcregistry20registryh"></a>
<div class="modfile"><h4>Modified: trunk/base/src/registry2.0/registry.h (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/registry.h        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/registry.h        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &lt;tcl.h&gt;
</span><span class="cx"> #include &lt;sqlite3.h&gt;
</span><ins>+#include &lt;cregistry/portgroup.h&gt;
</ins><span class="cx"> #include &lt;cregistry/entry.h&gt;
</span><span class="cx"> 
</span><span class="cx"> typedef struct _entry_list {
</span></span></pre></div>
<a id="trunkbasesrcregistry20registry_utiltcl"></a>
<div class="modfile"><h4>Modified: trunk/base/src/registry2.0/registry_util.tcl (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/registry_util.tcl        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/registry_util.tcl        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -103,7 +103,7 @@
</span><span class="cx">         if {[catch {set result [mportexec $mport $target]} result] || $result != 0} {
</span><span class="cx">             global errorInfo
</span><span class="cx">             ui_debug &quot;$errorInfo&quot;
</span><del>-            catch {mportclose_installed $mport}
</del><ins>+            catch {mportclose $mport}
</ins><span class="cx">             ui_warn &quot;Failed to execute portfile from registry for $portspec&quot;
</span><span class="cx">             switch $target {
</span><span class="cx">                 activate {
</span><span class="lines">@@ -127,7 +127,7 @@
</span><span class="cx">             if {(![info exists keeplogs] || !$keeplogs) &amp;&amp; $target ne &quot;activate&quot;} {
</span><span class="cx">                 catch {mportexec $mport clean}
</span><span class="cx">             }
</span><del>-            mportclose_installed $mport
</del><ins>+            mportclose $mport
</ins><span class="cx">             return 1
</span><span class="cx">         }
</span><span class="cx">     } else {
</span></span></pre></div>
<a id="trunkbasesrcregistry20utilc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/registry2.0/util.c (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/util.c        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/util.c        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -38,6 +38,7 @@
</span><span class="cx"> #include &quot;util.h&quot;
</span><span class="cx"> #include &quot;entryobj.h&quot;
</span><span class="cx"> #include &quot;fileobj.h&quot;
</span><ins>+#include &quot;portgroupobj.h&quot;
</ins><span class="cx"> 
</span><span class="cx"> /**
</span><span class="cx">  * Generates a unique proc name starting with prefix.
</span><span class="lines">@@ -214,6 +215,29 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * Sets a given name to be a portgroup object.
+ *
+ * @param [in] interp  Tcl interpreter to create the portgroup within
+ * @param [in] name    name to associate the given portgroup with
+ * @param [in] portgroup    portgroup to associate with the given name
+ * @param [out] errPtr description of error if it couldn't be set
+ * @return             true if success; false if failure
+ * @see set_object
+ */
+int set_portgroup(Tcl_Interp* interp, char* name, reg_portgroup* portgroup,
+        reg_error* errPtr) {
+    if (set_object(interp, name, portgroup, &quot;portgroup&quot;, portgroup_obj_cmd, NULL,
+                errPtr)) {
+        portgroup-&gt;proc = strdup(name);
+        if (!portgroup-&gt;proc) {
+            return 0;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+/**
</ins><span class="cx">  * Reports a sqlite3 error to Tcl.
</span><span class="cx">  *
</span><span class="cx">  * Queries the database for the most recent error message and sets it as the
</span><span class="lines">@@ -297,6 +321,23 @@
</span><span class="cx">     return 1;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+int portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_portgroup* portgroup,
+        int* lower_bound, reg_error* errPtr) {
+    if (portgroup-&gt;proc == NULL) {
+        char* name = unique_name(interp, &quot;::registry::portgroup&quot;, lower_bound);
+        if (!name) {
+            return 0;
+        }
+        if (!set_portgroup(interp, name, portgroup, errPtr)) {
+            free(name);
+            return 0;
+        }
+        free(name);
+    }
+    *obj = Tcl_NewStringObj(portgroup-&gt;proc, -1);
+    return 1;
+}
+
</ins><span class="cx"> int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
</span><span class="cx">         reg_entry** entries, int entry_count, reg_error* errPtr) {
</span><span class="cx">     int lower_bound = 0;
</span><span class="lines">@@ -311,6 +352,13 @@
</span><span class="cx">             (void***)objs, (void**)files, file_count, errPtr);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+int list_portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
+        reg_portgroup** portgroups, int portgroup_count, reg_error* errPtr) {
+    int lower_bound = 0;
+    return recast(interp, (cast_function*)portgroup_to_obj, &amp;lower_bound, NULL,
+            (void***)objs, (void**)portgroups, portgroup_count, errPtr);
+}
+
</ins><span class="cx"> static int obj_to_string(void* userdata UNUSED, char** string, Tcl_Obj* obj,
</span><span class="cx">         void* param UNUSED, reg_error* errPtr UNUSED) {
</span><span class="cx">     *string = Tcl_GetString(obj);
</span></span></pre></div>
<a id="trunkbasesrcregistry20utilh"></a>
<div class="modfile"><h4>Modified: trunk/base/src/registry2.0/util.h (117406 => 117407)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/registry2.0/util.h        2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/util.h        2014-02-25 18:28:14 UTC (rev 117407)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> #include &lt;sqlite3.h&gt;
</span><span class="cx"> 
</span><span class="cx"> #include &lt;cregistry/registry.h&gt;
</span><ins>+#include &lt;cregistry/portgroup.h&gt;
</ins><span class="cx"> #include &lt;cregistry/entry.h&gt;
</span><span class="cx"> #include &lt;cregistry/file.h&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -60,6 +61,8 @@
</span><span class="cx">         reg_error* errPtr);
</span><span class="cx"> int set_file(Tcl_Interp* interp, char* name, reg_file* file,
</span><span class="cx">         reg_error* errPtr);
</span><ins>+int set_portgroup(Tcl_Interp* interp, char* name, reg_portgroup* portgroup,
+        reg_error* errPtr);
</ins><span class="cx"> 
</span><span class="cx"> void set_sqlite_result(Tcl_Interp* interp, sqlite3* db, const char* query);
</span><span class="cx"> 
</span><span class="lines">@@ -77,6 +80,10 @@
</span><span class="cx">         int* lower_bound, reg_error* errPtr);
</span><span class="cx"> int list_file_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
</span><span class="cx">         reg_file** files, int file_count, reg_error* errPtr);
</span><ins>+int portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj** ibj, reg_portgroup* portgroup,
+        int* lower_bound, reg_error* errPtr);
+int list_portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
+        reg_portgroup** portgroups, int portgroup_count, reg_error* errPtr);
</ins><span class="cx"> 
</span><span class="cx"> void free_strings(void* userdata UNUSED, char** strings, int count);
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>