Revision
117407
Author
jmr@macports.org
Date
2014-02-25 10:28:14 -0800 (Tue, 25 Feb 2014)

Log Message

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)

Modified Paths

Added Paths

Diff

Modified: trunk/base/Makefile.in (117406 => 117407)


--- 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)
@@ -94,6 +94,7 @@
 	$(TCLSH) src/upgrade_sources_conf_default.tcl "${prefix}"
 # Convert image directories (and direct mode installs) to image archives
 	$(TCLSH) src/images_to_archives.tcl "${macports_tcl_dir}"
+	$(TCLSH) src/dedup_portfiles.tcl "${macports_tcl_dir}"
 endif
 ifndef SELFUPDATING
 	@echo ""; echo "Congratulations, you have successfully installed the MacPorts system. To get the Portfiles and update the system, add ${prefix}/bin to your PATH and run:"; echo ""

Modified: trunk/base/portmgr/dmg/postflight (117406 => 117407)


--- 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)
@@ -74,6 +74,7 @@
     # Convert image directories (and direct mode installs) to image archives
     echo "Updating port image format..."
     ${TCLSH} ${SCRIPT_DIR}/images_to_archives.tcl ${MACPORTS_TCL_DIR}
+    ${TCLSH} ${SCRIPT_DIR}/dedup_portfiles.tcl ${MACPORTS_TCL_DIR}
 
     echo "Synchronizing the MacPorts installation with the project's rsync server..."
     if ! ${BINPATH}/port -v selfupdate; then 

Modified: trunk/base/src/cregistry/Makefile (117406 => 117407)


--- 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)
@@ -1,6 +1,6 @@
 # $Id$
 
-OBJS = registry.o entry.o sql.o vercomp.o util.o file.o
+OBJS = registry.o entry.o sql.o vercomp.o util.o file.o portgroup.o
 STLIB_NAME = cregistry.a
 RANLIB = ranlib
 

Modified: trunk/base/src/cregistry/entry.c (117406 => 117407)


--- 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)
@@ -2,7 +2,7 @@
  * entry.c
  * $Id$
  *
- * Copyright (c) 2010-2011 The MacPorts Project
+ * Copyright (c) 2010-2011, 2014 The MacPorts Project
  * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
  * All rights reserved.
  *
@@ -31,6 +31,7 @@
 #include <config.h>
 #endif
 
+#include "portgroup.h"
 #include "entry.h"
 #include "registry.h"
 #include "sql.h"
@@ -246,9 +247,11 @@
     sqlite3_stmt* ports = NULL;
     sqlite3_stmt* files = NULL;
     sqlite3_stmt* dependencies = NULL;
+    sqlite3_stmt* portgroups = NULL;
     char* ports_query = "DELETE FROM registry.ports WHERE id=?";
     char* files_query = "DELETE FROM registry.files WHERE id=?";
     char* dependencies_query = "DELETE FROM registry.dependencies WHERE id=?";
+    char* portgroups_query = "DELETE FROM registry.portgroups WHERE id=?";
     if ((sqlite3_prepare_v2(reg->db, ports_query, -1, &ports, NULL) == SQLITE_OK)
             && (sqlite3_bind_int64(ports, 1, entry->id) == SQLITE_OK)
             && (sqlite3_prepare_v2(reg->db, files_query, -1, &files, NULL)
@@ -256,7 +259,10 @@
             && (sqlite3_bind_int64(files, 1, entry->id) == SQLITE_OK)
             && (sqlite3_prepare_v2(reg->db, dependencies_query, -1, &dependencies,
                     NULL) == SQLITE_OK)
-            && (sqlite3_bind_int64(dependencies, 1, entry->id) == SQLITE_OK)) {
+            && (sqlite3_bind_int64(dependencies, 1, entry->id) == SQLITE_OK)
+            && (sqlite3_prepare_v2(reg->db, portgroups_query, -1, &portgroups,
+                    NULL) == SQLITE_OK)
+            && (sqlite3_bind_int64(portgroups, 1, entry->id) == SQLITE_OK)) {
         int r;
         do {
             r = sqlite3_step(ports);
@@ -271,7 +277,20 @@
                                         r = sqlite3_step(dependencies);
                                         switch (r) {
                                             case SQLITE_DONE:
-                                                result = 1;
+                                                do {
+                                                    r = sqlite3_step(portgroups);
+                                                    switch (r) {
+                                                        case SQLITE_DONE:
+                                                            result = 1;
+                                                            break;
+                                                        case SQLITE_BUSY:
+                                                            break;
+                                                        case SQLITE_ERROR:
+                                                            reg_sqlite_error(reg->db,
+                                                                    errPtr, NULL);
+                                                            break;
+                                                    }
+                                                } while (r == SQLITE_BUSY);
                                                 break;
                                             case SQLITE_BUSY:
                                                 break;
@@ -315,6 +334,9 @@
     if (dependencies) {
         sqlite3_finalize(dependencies);
     }
+    if (portgroups) {
+        sqlite3_finalize(portgroups);
+    }
     return result;
 }
 
@@ -670,6 +692,73 @@
 }
 
 /**
+ * Associates a portgroup with given port.
+ *
+ * @param [in] entry      the entry to map the portgroup to
+ * @param [in] name       the portgroup name (e.g. "muniversal")
+ * @param [in] version    the portgroup version (e.g. "1.0")
+ * @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->reg;
+    int result = 1;
+    sqlite3_stmt* stmt = NULL;
+    char* insert = "INSERT INTO registry.portgroups (id, name, version, size, sha256) "
+        "VALUES (?, ?, ?, ?, ?)";
+    if ((sqlite3_prepare_v2(reg->db, insert, -1, &stmt, NULL) == SQLITE_OK)
+            && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC) == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 3, version, -1, SQLITE_STATIC) == SQLITE_OK)
+            && (sqlite3_bind_int64(stmt, 4, size) == SQLITE_OK)
+            && (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->db, errPtr, insert);
+                    result = 0;
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg->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->reg;
+    char* query = sqlite3_mprintf("SELECT ROWID FROM portgroups "
+            "WHERE id=%lld",
+            entry->id);
+    int result = reg_all_portgroups(reg, query, -1, portgroups, errPtr);
+    sqlite3_free(query);
+    return result;
+}
+
+/**
  * Maps files to the given port in the filemap. The list of files must not
  * contain files that are already mapped to the given port.
  *

Modified: trunk/base/src/cregistry/entry.h (117406 => 117407)


--- 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)
@@ -71,6 +71,11 @@
 int reg_entry_propset(reg_entry* entry, char* key, char* value,
         reg_error* errPtr);
 
+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);
+
 int reg_entry_map(reg_entry* entry, char** files, int file_count,
         reg_error* errPtr);
 int reg_entry_unmap(reg_entry* entry, char** files, int file_count,

Added: trunk/base/src/cregistry/portgroup.c (0 => 117407)


--- trunk/base/src/cregistry/portgroup.c	                        (rev 0)
+++ trunk/base/src/cregistry/portgroup.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,318 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include "portgroup.h"
+#include "util.h"
+#include "registry.h"
+#include "sql.h"
+
+#include <sqlite3.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * 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(&reg->open_portgroups,
+            (const char*)&id, &is_new);
+    if (is_new) {
+        reg_portgroup* p = malloc(sizeof(reg_portgroup));
+        if (!p) {
+            return 0;
+        }
+        p->reg = reg;
+        p->id = id;
+        p->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, &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 = " WHERE ";
+    char* query;
+    size_t query_len, query_space;
+    int result;
+
+    /* build the query */
+    query = strdup("SELECT ROWID FROM registry.portgroups");
+    if (!query) {
+        return -1;
+    }
+    query_len = query_space = strlen(query);
+
+    for (i = 0; i < 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(&query, &query_len, &query_space, kwd)
+            || !reg_strcat(&query, &query_len, &query_space, cond)) {
+            free(query);
+            return -1;
+        }
+        sqlite3_free(cond);
+        kwd = " AND ";
+    }
+
+    /* 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->reg;
+    int result = 0;
+    sqlite3_stmt* stmt = NULL;
+    char* query;
+    const char *text;
+    query = sqlite3_mprintf("SELECT %q FROM registry.portgroups WHERE ROWID=%lld", key,
+            portgroup->id);
+    if (sqlite3_prepare_v2(reg->db, query, -1, &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->db, errPtr, query);
+                    }
+                    break;
+                case SQLITE_DONE:
+                    errPtr->code = REG_INVALID;
+                    errPtr->description = "an invalid portgroup was passed";
+                    errPtr->free = NULL;
+                    break;
+                case SQLITE_BUSY:
+                    continue;
+                default:
+                    reg_sqlite_error(reg->db, errPtr, query);
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg->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->reg;
+    int result = 0;
+    sqlite3_stmt* stmt = NULL;
+    char* query;
+    query = sqlite3_mprintf("UPDATE registry.ports SET %q = '%q' WHERE ROWID=%lld",
+            key, value, portgroup->id);
+    if (sqlite3_prepare_v2(reg->db, query, -1, &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->code = REG_CONSTRAINT;
+                        errPtr->description = "a constraint was disobeyed";
+                        errPtr->free = NULL;
+                    } else {
+                        reg_sqlite_error(reg->db, errPtr, query);
+                    }
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg->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 = "SELECT ROWID FROM registry.portgroups WHERE id=? AND name=? AND version=? "
+        "AND size=? AND sha256=?";
+    if ((sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 1, id, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 3, version, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 4, size, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            && (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**)&portgroup, stmt, &lower_bound, errPtr);
+                    break;
+                case SQLITE_DONE:
+                    errPtr->code = REG_NOT_FOUND;
+                    errPtr->description = sqlite3_mprintf("no matching portgroup found for: " \
+                            "id=%s, name=%s, version=%s, size=%s, sha256=%s", \
+                            id, name, version, size, sha256);
+                    errPtr->free = (reg_error_destructor*) sqlite3_free;
+                    break;
+                case SQLITE_BUSY:
+                    continue;
+                default:
+                    reg_sqlite_error(reg->db, errPtr, query);
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg->db, errPtr, query);
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    return portgroup;
+}

Added: trunk/base/src/cregistry/portgroup.h (0 => 117407)


--- trunk/base/src/cregistry/portgroup.h	                        (rev 0)
+++ trunk/base/src/cregistry/portgroup.h	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,56 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include "registry.h"
+
+#include <sqlite3.h>
+
+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 */

Modified: trunk/base/src/cregistry/registry.c (117406 => 117407)


--- 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)
@@ -4,7 +4,7 @@
  * vim:expandtab:tw=80
  *
  * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
- * Copyright (c) 2012 The MacPorts Project
+ * Copyright (c) 2012, 2014 The MacPorts Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,6 +32,7 @@
 #include <config.h>
 #endif
 
+#include "portgroup.h"
 #include "entry.h"
 #include "file.h"
 #include "sql.h"
@@ -208,6 +209,8 @@
                                     sizeof(sqlite_int64)/sizeof(int));
                             Tcl_InitHashTable(&reg->open_files,
                                     TCL_STRING_KEYS);
+                            Tcl_InitHashTable(&reg->open_portgroups,
+                                    sizeof(sqlite_int64)/sizeof(int));
                             reg->status |= reg_attached;
                             result = 1;
                         }

Modified: trunk/base/src/cregistry/registry.h (117406 => 117407)


--- 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)
@@ -4,7 +4,7 @@
  * $Id$
  *
  * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
- * Copyright (c) 2012 The MacPorts Project
+ * Copyright (c) 2012, 2014 The MacPorts Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -73,6 +73,7 @@
     int status;
     Tcl_HashTable open_entries;
     Tcl_HashTable open_files;
+    Tcl_HashTable open_portgroups;
 } reg_registry;
 
 int reg_open(reg_registry** regPtr, reg_error* errPtr);

Modified: trunk/base/src/cregistry/sql.c (117406 => 117407)


--- 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)
@@ -3,7 +3,7 @@
  * $Id$
  *
  * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
- * Copyright (c) 2012 The MacPorts Project
+ * Copyright (c) 2012, 2014 The MacPorts Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -129,13 +129,13 @@
 
         /* metadata table */
         "CREATE TABLE registry.metadata (key UNIQUE, value)",
-        "INSERT INTO registry.metadata (key, value) VALUES ('version', 1.100)",
+        "INSERT INTO registry.metadata (key, value) VALUES ('version', 1.200)",
         "INSERT INTO registry.metadata (key, value) VALUES ('created', strftime('%s', 'now'))",
 
         /* ports table */
         "CREATE TABLE registry.ports ("
-            "id INTEGER PRIMARY KEY AUTOINCREMENT, "
-            "name TEXT COLLATE NOCASE, portfile CLOB, url TEXT, "
+            "id INTEGER PRIMARY KEY, "
+            "name TEXT COLLATE NOCASE, portfile TEXT, url TEXT, "
             "location TEXT, epoch INTEGER, version TEXT COLLATE VERSION, "
             "revision INTEGER, variants TEXT, negated_variants TEXT, "
             "state TEXT, date DATETIME, installtype TEXT, archs TEXT, "
@@ -163,6 +163,11 @@
         "FOREIGN KEY(id) REFERENCES ports(id))",
         "CREATE INDEX registry.dep_name ON dependencies (name)",
 
+        /* portgroups table */
+        "CREATE TABLE registry.portgroups (id INTEGER, "
+            "name TEXT, version TEXT COLLATE VERSION, size INTEGER, sha256 TEXT, "
+            "FOREIGN KEY(id) REFERENCES ports(id))",
+
         "COMMIT",
         NULL
     };
@@ -321,6 +326,36 @@
             continue;
         }
 
+        if (sql_version(NULL, -1, version, -1, "1.2") < 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 */
+                "CREATE TABLE registry.portgroups (id INTEGER, "
+                    "name TEXT, version TEXT COLLATE VERSION, size INTEGER, sha256 TEXT, "
+                    "FOREIGN KEY(id) REFERENCES ports(id))",
+
+                "UPDATE registry.metadata SET value = '1.200' WHERE key = 'version'",
+
+                "INSERT INTO registry.metadata (key, value) VALUES ('portfiles_update_needed', 1)",
+
+                "COMMIT",
+                NULL
+            };
+
+            sqlite3_finalize(stmt);
+            stmt = NULL;
+
+            if (!do_queries(db, version_1_2_queries, errPtr)) {
+                rollback_db(db);
+                return 0;
+            }
+
+            did_update = 1;
+            continue;
+        }
+
         /* add new versions here, but remember to:
          *  - finalize the version query statement and set stmt to NULL
          *  - do _not_ use "BEGIN" in your query list, since a transaction has

Added: trunk/base/src/dedup_portfiles.tcl (0 => 117407)


--- trunk/base/src/dedup_portfiles.tcl	                        (rev 0)
+++ trunk/base/src/dedup_portfiles.tcl	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,47 @@
+#!/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 "" && $portfile_contents ne "0"} {
+                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
Property changes on: trunk/base/src/dedup_portfiles.tcl
___________________________________________________________________

Added: svn:executable

Added: svn:keywords

Added: svn:eol-style

Modified: trunk/base/src/macports1.0/macports.tcl (117406 => 117407)


--- 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)
@@ -1363,12 +1363,12 @@
     }
 
     foreach {opt val} $options {
-        $workername eval set user_options($opt) $val
-        $workername eval set $opt $val
+        $workername eval [list set user_options($opt) $val]
+        $workername eval [list set $opt $val]
     }
 
     foreach {var val} $variations {
-        $workername eval set variations($var) $val
+        $workername eval [list set variations($var) $val]
     }
 }
 
@@ -1715,12 +1715,7 @@
 proc mportopen_installed {name version revision variants options} {
     global macports::registry.path
     set regref [lindex [registry::entry imaged $name $version $revision $variants] 0]
-    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]
+    set portfile_dir [file join ${registry.path} registry portfiles ${name}-${version}_${revision} [$regref portfile]]
 
     set variations {}
     set minusvariant [lrange [split [$regref negated_variants] -] 1 end]
@@ -1732,23 +1727,17 @@
         lappend variations $v -
     }
     lappend options subport $name
-    return [mportopen file://${portfile_dir}/ $options $variations]
-}
 
-# 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]
+    # find portgroups in registry
+    set pgdirlist [list]
+    foreach pg [$regref groups_used] {
+        lappend pgdirlist [file join ${registry.path} registry portgroups [$pg sha256]-[$pg size]]
     }
-    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
+    if {$pgdirlist ne {}} {
+        lappend options _portgroup_search_dirs $pgdirlist
     }
+
+    return [mportopen file://${portfile_dir}/ $options $variations]
 }
 
 # Traverse a directory with ports, calling a function on the path of ports

Modified: trunk/base/src/macports1.0/tests/macports.test (117406 => 117407)


--- 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)
@@ -153,10 +153,10 @@
        return "FAIL: installed port not opened"
     }
 
-    if {[catch {mportclose_installed $res}] != 0} {
+    if {[catch {mportclose $res}] != 0} {
        return "FAIL: cannot close port"
     }
-    if {[catch {mportclose_installed $res}] != 1} {
+    if {[catch {mportclose $res}] != 1} {
        return "FAIL: installed port not closed"
     }
     return "Installed port open successful."
@@ -702,10 +702,6 @@
 # test mportopen
 
 
-# Covered by mportopen_installed
-# test mportclose_installed
-
-
 test mporttraverse {
     Mport traverse unit test. Uses 3rd column of the Portfile.
 } -setup {

Modified: trunk/base/src/pextlib1.0/tracelib.c (117406 => 117407)


--- 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)
@@ -56,6 +56,7 @@
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 #include <registry2.0/registry.h>
 

Modified: trunk/base/src/port1.0/portinstall.tcl (117406 => 117407)


--- 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)
@@ -307,7 +307,7 @@
     homepage depends_run package-install workdir workpath \
     worksrcdir UI_PREFIX destroot revision maintainers user_options \
     portvariants negated_variants targets depends_lib PortInfo epoch license \
-    os.platform os.major portarchivetype installPlist
+    os.platform os.major portarchivetype installPlist registry.path porturl
 
     set oldpwd [pwd]
     if {$oldpwd eq ""} {
@@ -373,11 +373,40 @@
             # register files
             $regref map $installPlist
         }
-        
+
         # store portfile
-        set fd [open [file join ${portpath} Portfile]]
-        $regref portfile [read $fd]
-        close $fd
+        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 "port1.0/group/${pgname}-${pgversion}.tcl"]
+                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 "install_main: no portgroup ${pgname}-${pgversion}.tcl found"
+                }
+            }
+        }
     }
 
     _cd $oldpwd

Modified: trunk/base/src/port1.0/portutil.tcl (117406 => 117407)


--- 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)
@@ -2569,12 +2569,12 @@
 
 # Use a specified group/version.
 proc PortGroup {group version} {
-    global porturl PortInfo
+    global porturl PortInfo _portgroup_search_dirs
 
     lappend PortInfo(portgroups) [list $group $version]
 
-    if {[info exists portutil::portgroup_search_dirs]} {
-        foreach dir $portutil::portgroup_search_dirs {
+    if {[info exists _portgroup_search_dirs]} {
+        foreach dir $_portgroup_search_dirs {
             set groupFile ${dir}/${group}-${version}.tcl
             if {[file exists $groupFile]} {
                 uplevel "source $groupFile"

Modified: trunk/base/src/registry2.0/Makefile (117406 => 117407)


--- 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)
@@ -3,7 +3,8 @@
 SRCS = registry.tcl registry_autoconf.tcl registry_util.tcl receipt_flat.tcl receipt_sqlite.tcl portimage.tcl portuninstall.tcl
 OBJS = registry.o util.o \
 	entry.o entryobj.o \
-	file.o fileobj.o
+	file.o fileobj.o \
+	portgroup.o portgroupobj.o
 	#graph.o graphobj.o
 
 SHLIB_NAME= registry${SHLIB_SUFFIX}

Modified: trunk/base/src/registry2.0/entryobj.c (117406 => 117407)


--- 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)
@@ -356,6 +356,59 @@
     }
 }
 
+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, "addgroup name version sha256 size");
+        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], &tclsize);
+        sqlite_int64 size = (sqlite_int64)tclsize;
+        if (reg_entry_addgroup(entry, name, version, sha256, size, &error)) {
+            return TCL_OK;
+        }
+        return registry_failed(interp, &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, "groups_used");
+        return TCL_ERROR;
+    } else if (reg == NULL) {
+        return TCL_ERROR;
+    } else {
+        reg_portgroup** portgroups;
+        reg_error error;
+        int portgroup_count = reg_entry_getgroups(entry, &portgroups, &error);
+        if (portgroup_count >= 0) {
+            Tcl_Obj** objs;
+            int retval = TCL_ERROR;
+            if (list_portgroup_to_obj(interp, &objs, portgroups, portgroup_count, &error)){
+                Tcl_Obj* result = Tcl_NewListObj(portgroup_count, objs);
+                Tcl_SetObjResult(interp, result);
+                free(objs);
+                retval = TCL_OK;
+            } else {
+                retval = registry_failed(interp, &error);
+            }
+            free(portgroups);
+            return retval;
+        }
+        return registry_failed(interp, &error);
+    }
+}
+
 typedef struct {
     char* name;
     int (*function)(Tcl_Interp* interp, reg_entry* entry, int objc,
@@ -391,6 +444,9 @@
     { "dependents", entry_obj_dependents },
     { "dependencies", entry_obj_dependencies },
     { "depends", entry_obj_depends },
+    /* portgroups */
+    { "addgroup", entry_obj_add_portgroup },
+    { "groups_used", entry_obj_get_portgroups },
     { NULL, NULL }
 };
 

Added: trunk/base/src/registry2.0/portgroup.c (0 => 117407)


--- 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)
@@ -0,0 +1,294 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <sqlite3.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+
+#include <cregistry/portgroup.h>
+#include <cregistry/util.h>
+
+#include "portgroup.h"
+#include "portgroupobj.h"
+#include "registry.h"
+#include "util.h"
+
+/**
+ * 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, "portgroup", 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->proc);
+    portgroup->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, "open id name version size sha256");
+		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, &error);
+		if (portgroup != NULL) {
+			Tcl_Obj* result;
+			if (portgroup_to_obj(interp, &result, portgroup, NULL, &error)) {
+				Tcl_SetObjResult(interp, result);
+				return TCL_OK;
+			}
+		}
+		return registry_failed(interp, &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, "close portgroup");
+		return TCL_ERROR;
+	} else {
+		reg_error error;
+		char* proc = Tcl_GetString(objv[2]);
+		reg_portgroup* portgroup = get_portgroup(interp, proc, &error);
+		if (portgroup == NULL) {
+			return registry_failed(interp, &error);
+		} else {
+			Tcl_DeleteCommand(interp, proc);
+			return TCL_OK;
+		}
+	}
+}
+
+typedef struct {
+    char* name;
+    reg_strategy strategy;
+} strategy_type;
+
+static strategy_type strategies[] = {
+    { "-exact",  reg_strategy_exact },
+    { "-glob",   reg_strategy_glob },
+    { "-regexp", reg_strategy_regexp },
+    { "-null",   reg_strategy_null },
+    { "--",      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 < objc;) {
+            int index, strat_index, val_length;
+            if (Tcl_GetIndexFromObj(interp, objv[i], portgroup_props, "search key",
+                        0, &index) != TCL_OK) {
+                return TCL_ERROR;
+            }
+
+            /* we ate the key value */
+            i++;
+
+            /* check whether there's a strategy */
+            if (Tcl_GetString(objv[i])[0] == '-'
+                    && Tcl_GetIndexFromObjStruct(interp, objv[i], strategies,
+                        sizeof(strategy_type), "option", 0, &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], &val_length) == NULL
+                            || val_length == 0) {
+                        Tcl_WrongNumArgs(interp, 2, objv,
+                                "search ?key ?options? value ...?");
+                        return TCL_ERROR;
+                    }
+
+                    i++;
+                }
+            } else {
+                /* this key must also have a value */
+
+                if (Tcl_GetStringFromObj(objv[i], &val_length) == NULL
+                        || val_length == 0) {
+                    Tcl_WrongNumArgs(interp, 2, objv,
+                            "search ?key ?options? value ...?");
+                    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 < objc && j < key_count; j++) {
+            int strat_index;
+
+            keys[j] = Tcl_GetString(objv[i++]);
+
+            /* try to get the strategy */
+            if (Tcl_GetString(objv[i])[0] == '-'
+                    && Tcl_GetIndexFromObjStruct(interp, objv[i], strategies,
+                        sizeof(strategy_type), "option", 0, &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,
+                &portgroups, &error);
+        free(keys);
+        free(vals);
+        free(strats);
+        if (portgroup_count >= 0) {
+            int retval;
+            Tcl_Obj* resultObj;
+            Tcl_Obj** objs;
+            if (list_portgroup_to_obj(interp, &objs, portgroups, portgroup_count, &error)){
+                resultObj = Tcl_NewListObj(portgroup_count, objs);
+                Tcl_SetObjResult(interp, resultObj);
+                free(objs);
+                retval = TCL_OK;
+            } else {
+                retval = registry_failed(interp, &error);
+            }
+            free(portgroups);
+            return retval;
+        }
+        return registry_failed(interp, &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 */
+    { "open", portgroup_open },
+    { "close", portgroup_close },
+    { "search", 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 < 2) {
+        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?");
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObjStruct(interp, objv[1], portgroup_cmds,
+                sizeof(portgroup_cmd_type), "cmd", 0, &cmd_index) == TCL_OK) {
+        portgroup_cmd_type* cmd = &portgroup_cmds[cmd_index];
+        return cmd->function(interp, objc, objv);
+    }
+    return TCL_ERROR;
+}

Added: trunk/base/src/registry2.0/portgroup.h (0 => 117407)


--- 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)
@@ -0,0 +1,42 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <tcl.h>
+
+void delete_portgroup(ClientData clientData);
+
+int portgroup_cmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]);
+
+#endif /* _PORTGROUP_H */

Added: trunk/base/src/registry2.0/portgroupobj.c (0 => 117407)


--- 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)
@@ -0,0 +1,135 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <tcl.h>
+#include <sqlite3.h>
+
+#include "portgroupobj.h"
+#include "registry.h"
+#include "util.h"
+
+const char* portgroup_props[] = {
+    "name",
+    "version",
+    "size",
+    "sha256",
+    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 > 3) {
+        Tcl_WrongNumArgs(interp, 2, objv, "?value?");
+        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, "prop", 0, &index)
+                == TCL_OK) {
+            char* key = Tcl_GetString(objv[1]);
+            char* value;
+            reg_error error;
+            if (reg_portgroup_propget(portgroup, key, &value, &error)) {
+                Tcl_Obj* result = Tcl_NewStringObj(value, -1);
+                Tcl_SetObjResult(interp, result);
+                free(value);
+                return TCL_OK;
+            }
+            return registry_failed(interp, &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, "prop", 0, &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, &error)) {
+                return TCL_OK;
+            }
+            return registry_failed(interp, &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 */
+    { "name", portgroup_obj_prop },
+    { "version", portgroup_obj_prop },
+    { "size", portgroup_obj_prop },
+    { "sha256", 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 < 2) {
+        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?");
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObjStruct(interp, objv[1], portgroup_cmds,
+                sizeof(portgroup_obj_cmd_type), "cmd", 0, &cmd_index) == TCL_OK) {
+        portgroup_obj_cmd_type* cmd = &portgroup_cmds[cmd_index];
+        return cmd->function(interp, (reg_portgroup*)clientData, objc, objv);
+    }
+    return TCL_ERROR;
+}
+

Added: trunk/base/src/registry2.0/portgroupobj.h (0 => 117407)


--- 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)
@@ -0,0 +1,43 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <tcl.h>
+#include <sqlite3.h>
+
+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 */

Modified: trunk/base/src/registry2.0/portuninstall.tcl (117406 => 117407)


--- 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)
@@ -53,7 +53,7 @@
 
 proc uninstall {portname {version ""} {revision ""} {variants 0} {optionslist ""}} {
     global uninstall.force uninstall.nochecksum UI_PREFIX \
-           macports::portimagefilepath
+           macports::portimagefilepath macports::registry.path
     array set options $optionslist
     if {[info exists options(subport)]} {
         # don't want this set when calling registry::run_target
@@ -156,7 +156,7 @@
         # look up deps from the saved portfile if possible
         if {![catch {set mport [mportopen_installed [$port name] [$port version] [$port revision] [$port variants] $optionslist]}]} {
             array set depportinfo [mportinfo $mport]
-            mportclose_installed $mport
+            mportclose $mport
             foreach type $deptypes {
                 if {[info exists depportinfo($type)]} {
                     foreach dep $depportinfo($type) {
@@ -217,9 +217,37 @@
         # files so just ignore the failure
         catch {file delete [::file dirname $imagefile]}
 
+        # 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]]
+        }
+
         registry::write {
             registry::entry delete $port
         }
+
+        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}
+            }
+        }
     }
     
     # uninstall dependencies if requested

Modified: trunk/base/src/registry2.0/registry.c (117406 => 117407)


--- 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)
@@ -3,7 +3,7 @@
  * $Id$
  *
  * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
- * Copyright (c) 2012 The MacPorts Project
+ * Copyright (c) 2012, 2014 The MacPorts Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,6 +38,7 @@
 #include <tcl.h>
 
 #include <cregistry/registry.h>
+#include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 #include <cregistry/file.h>
 
@@ -46,6 +47,7 @@
 #include "file.h"
 #include "graph.h"
 #include "item.h"
+#include "portgroup.h"
 #include "registry.h"
 #include "util.h"
 
@@ -389,6 +391,7 @@
     /* Tcl_CreateObjCommand(interp, "registry::item", item_cmd, NULL, NULL); */
     Tcl_CreateObjCommand(interp, "registry::entry", entry_cmd, NULL, NULL);
     Tcl_CreateObjCommand(interp, "registry::file", file_cmd, NULL, NULL);
+    Tcl_CreateObjCommand(interp, "registry::portgroup", portgroup_cmd, NULL, NULL);
     Tcl_CreateObjCommand(interp, "registry::metadata", metadata_cmd, NULL, NULL);
     if (Tcl_PkgProvide(interp, "registry2", "2.0") != TCL_OK) {
         return TCL_ERROR;

Modified: trunk/base/src/registry2.0/registry.h (117406 => 117407)


--- 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)
@@ -34,6 +34,7 @@
 
 #include <tcl.h>
 #include <sqlite3.h>
+#include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 
 typedef struct _entry_list {

Modified: trunk/base/src/registry2.0/registry_util.tcl (117406 => 117407)


--- 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)
@@ -103,7 +103,7 @@
         if {[catch {set result [mportexec $mport $target]} result] || $result != 0} {
             global errorInfo
             ui_debug "$errorInfo"
-            catch {mportclose_installed $mport}
+            catch {mportclose $mport}
             ui_warn "Failed to execute portfile from registry for $portspec"
             switch $target {
                 activate {
@@ -127,7 +127,7 @@
             if {(![info exists keeplogs] || !$keeplogs) && $target ne "activate"} {
                 catch {mportexec $mport clean}
             }
-            mportclose_installed $mport
+            mportclose $mport
             return 1
         }
     } else {

Modified: trunk/base/src/registry2.0/util.c (117406 => 117407)


--- 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)
@@ -38,6 +38,7 @@
 #include "util.h"
 #include "entryobj.h"
 #include "fileobj.h"
+#include "portgroupobj.h"
 
 /**
  * Generates a unique proc name starting with prefix.
@@ -214,6 +215,29 @@
 }
 
 /**
+ * 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, "portgroup", portgroup_obj_cmd, NULL,
+                errPtr)) {
+        portgroup->proc = strdup(name);
+        if (!portgroup->proc) {
+            return 0;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+/**
  * Reports a sqlite3 error to Tcl.
  *
  * Queries the database for the most recent error message and sets it as the
@@ -297,6 +321,23 @@
     return 1;
 }
 
+int portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_portgroup* portgroup,
+        int* lower_bound, reg_error* errPtr) {
+    if (portgroup->proc == NULL) {
+        char* name = unique_name(interp, "::registry::portgroup", lower_bound);
+        if (!name) {
+            return 0;
+        }
+        if (!set_portgroup(interp, name, portgroup, errPtr)) {
+            free(name);
+            return 0;
+        }
+        free(name);
+    }
+    *obj = Tcl_NewStringObj(portgroup->proc, -1);
+    return 1;
+}
+
 int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
         reg_entry** entries, int entry_count, reg_error* errPtr) {
     int lower_bound = 0;
@@ -311,6 +352,13 @@
             (void***)objs, (void**)files, file_count, errPtr);
 }
 
+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, &lower_bound, NULL,
+            (void***)objs, (void**)portgroups, portgroup_count, errPtr);
+}
+
 static int obj_to_string(void* userdata UNUSED, char** string, Tcl_Obj* obj,
         void* param UNUSED, reg_error* errPtr UNUSED) {
     *string = Tcl_GetString(obj);

Modified: trunk/base/src/registry2.0/util.h (117406 => 117407)


--- 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)
@@ -37,6 +37,7 @@
 #include <sqlite3.h>
 
 #include <cregistry/registry.h>
+#include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 #include <cregistry/file.h>
 
@@ -60,6 +61,8 @@
         reg_error* errPtr);
 int set_file(Tcl_Interp* interp, char* name, reg_file* file,
         reg_error* errPtr);
+int set_portgroup(Tcl_Interp* interp, char* name, reg_portgroup* portgroup,
+        reg_error* errPtr);
 
 void set_sqlite_result(Tcl_Interp* interp, sqlite3* db, const char* query);
 
@@ -77,6 +80,10 @@
         int* lower_bound, reg_error* errPtr);
 int list_file_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
         reg_file** files, int file_count, reg_error* errPtr);
+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);
 
 void free_strings(void* userdata UNUSED, char** strings, int count);