<pre style='margin:0'>
Clemens Lang (neverpanic) pushed a commit to branch migration
in repository macports-base.

</pre>
<p><a href="https://github.com/macports/macports-base/commit/980a1f628d555d68c292ec79f917ab4129b9648b">https://github.com/macports/macports-base/commit/980a1f628d555d68c292ec79f917ab4129b9648b</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 980a1f628d555d68c292ec79f917ab4129b9648b
</span>Author: Umesh Singla <umeshksingla@gmail.com>
AuthorDate: Fri Jun 9 05:08:15 2017 +0530

<span style='display:block; white-space:pre;color:#404040;'>    Implement MacPorts migration
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    When an macOS install running MacPorts is upgraded, MacPorts will
</span><span style='display:block; white-space:pre;color:#404040;'>    require a rebuild of itself and reinstallation of all ports as
</span><span style='display:block; white-space:pre;color:#404040;'>    a robustness measure to ensure all ports continue working (and
</span><span style='display:block; white-space:pre;color:#404040;'>    compiling) as expected.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    This commit is a squash of the work done by Umesh Singla during Google
</span><span style='display:block; white-space:pre;color:#404040;'>    Summer of Code 2017 for MacPorts, implementing a MacPorts command 'port
</span><span style='display:block; white-space:pre;color:#404040;'>    migrate' that automates the entire process of reinstalling MacPorts and
</span><span style='display:block; white-space:pre;color:#404040;'>    re-installing all installed ports. It was squashed to simplify rebasing
</span><span style='display:block; white-space:pre;color:#404040;'>    the history against the current master.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    In addition to 'port migrate', this also adds two new commands 'port
</span><span style='display:block; white-space:pre;color:#404040;'>    snapshot' and 'port restore', which are the plumbing below migration.
</span><span style='display:block; white-space:pre;color:#404040;'>    Migration snapshots are stored in SQLite.
</span>---
 doc/port-selfupdate.1.txt                     |  13 +-
 src/cregistry/Makefile.in                     |   2 +-
 src/cregistry/entry.h                         |   1 +
 src/cregistry/registry.c                      |   1 +
 src/cregistry/snapshot.c                      | 470 ++++++++++++++++++++++++++
 src/cregistry/snapshot.h                      |  86 +++++
 src/cregistry/sql.c                           |  69 +++-
 src/macports1.0/Makefile.in                   |   3 +-
 src/macports1.0/macports.tcl                  |  87 ++++-
 src/macports1.0/migrate.tcl                   | 171 ++++++++++
 src/macports1.0/restore.tcl                   | 359 ++++++++++++++++++++
 src/macports1.0/selfupdate.tcl                |   5 +-
 src/macports1.0/snapshot.tcl                  |  82 +++++
 src/pextlib1.0/tracelib.c                     |   1 +
 src/port/port.tcl                             |  41 ++-
 src/registry2.0/Makefile.in                   |   3 +-
 src/registry2.0/registry.c                    |   3 +
 src/registry2.0/registry.h                    |   1 +
 src/registry2.0/snapshot.c                    | 209 ++++++++++++
 src/registry2.0/{registry.h => snapshot.h}    |  24 +-
 src/registry2.0/snapshotobj.c                 | 199 +++++++++++
 src/registry2.0/{registry.h => snapshotobj.h} |  23 +-
 src/registry2.0/util.c                        |  72 ++++
 src/registry2.0/util.h                        |   7 +
 24 files changed, 1887 insertions(+), 45 deletions(-)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/doc/port-selfupdate.1.txt b/doc/port-selfupdate.1.txt
</span><span style='display:block; white-space:pre;color:#808080;'>index f78aaa0..ca28e39 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/doc/port-selfupdate.1.txt
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/doc/port-selfupdate.1.txt
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -9,7 +9,7 @@ port-selfupdate - Upgrade MacPorts itself and update the port definition files.
</span> SYNOPSIS
 --------
 [cmdsynopsis]
<span style='display:block; white-space:pre;background:#ffe0e0;'>-*port* [*-qvdf*] *selfupdate* [--no-sync]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+*port* [*-qvdf*] *selfupdate* [--no-sync] [--migrate]
</span> 
 DESCRIPTION
 -----------
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -29,6 +29,11 @@ OPTIONS
</span>     Only check for updates - and install if available - for MacPorts itself. Do
     not update the ports tree.
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+*--migrate*::
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    Rebuild even if no new version is available, upgrade if a new version is
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    available. Contrary to *-f*, this flag will prevent downgrades. This flag is
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    useful when upgrading to a newer macOS major version.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> include::global-flags.txt[]
 
 *-q*::
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -39,9 +44,9 @@ include::global-flags.txt[]
</span>     currently installed one, but always rebuild and reinstall MacPorts.
     +
     You can use this to downgrade from a beta or development version to the
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    latest release. Note that there is no guarantee that there have not been
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    made incompatible and irreversible changes to MacPorts' internal data
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    structures, making a downgrade impossible.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    latest release. Note that there is no guarantee that no incompatible and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    irreversible changes have been made to MacPorts' internal data structures,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    which can make downgrading impossible.
</span> 
 EXAMPLES
 --------
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/cregistry/Makefile.in b/src/cregistry/Makefile.in
</span><span style='display:block; white-space:pre;color:#808080;'>index 408f94b..09311ff 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/cregistry/Makefile.in
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/cregistry/Makefile.in
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -2,7 +2,7 @@
</span> srcdir = @srcdir@
 VPATH  = @srcdir@
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-OBJS = registry.o entry.o sql.o vercomp.o util.o file.o portgroup.o
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+OBJS = registry.o entry.o sql.o vercomp.o util.o file.o portgroup.o snapshot.o
</span> STLIB_NAME = cregistry.a
 RANLIB = ranlib
 
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/cregistry/entry.h b/src/cregistry/entry.h
</span><span style='display:block; white-space:pre;color:#808080;'>index 0c37aba..2bcd3ba 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/cregistry/entry.h
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/cregistry/entry.h
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -33,6 +33,7 @@
</span> #endif
 
 #include "registry.h"
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "portgroup.h"
</span> 
 #include <sqlite3.h>
 
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/cregistry/registry.c b/src/cregistry/registry.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 8a8b5b3..1fc73f5 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/cregistry/registry.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/cregistry/registry.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -33,6 +33,7 @@
</span> 
 #include "portgroup.h"
 #include "entry.h"
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "snapshot.h"
</span> #include "file.h"
 #include "sql.h"
 
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/cregistry/snapshot.c b/src/cregistry/snapshot.c
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..1715d95
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/cregistry/snapshot.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,470 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * snapshot.c
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * vim:tw=80:expandtab
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (c) 2017 The MacPorts Project
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * All rights reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Redistribution and use in source and binary forms, with or without
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * modification, are permitted provided that the following conditions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * are met:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 1. Redistributions of source code must retain the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    notice, this list of conditions and the following disclaimer.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 2. Redistributions in binary form must reproduce the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    notice, this list of conditions and the following disclaimer in the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    documentation and/or other materials provided with the distribution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#if HAVE_CONFIG_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <config.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "entry.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "snapshot.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "registry.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "sql.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "util.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sqlite3.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <stdio.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <stdlib.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <string.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * helper to parse variants into 'struct variant' form
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] variants_str     the string to parse the variants from
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] delim            delimiter '+' for +ve variants, else '-'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] all_variants    list of 'struct variant's
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] variant_count   count of variants parsed till now
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return                      false
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int get_parsed_variants(char* variants_str, variant* all_variants,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* delim, int* variant_count) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char *token;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char *rest = variants_str;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    while ((token = strtok_r(rest, delim, &rest))) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        variant v;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        v.variant_name = token;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        v.variant_sign = delim;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        *(all_variants + *variant_count) = v;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        *variant_count = *variant_count + 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Converts a `sqlite3_stmt` into a `reg_snapshot`. The first column of the stmt's
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * row must be the id of an snapshot; the second either `SQLITE_NULL` or the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * address of the snapshot in memory.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] userdata     sqlite3 database
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] snapshot    snapshot described by `stmt`
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] stmt         `sqlite3_stmt` with appropriate columns
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr      unused
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return                  true if success; false if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int reg_stmt_to_snapshot(void* userdata, void** snapshot, void* stmt,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        void* calldata UNUSED, reg_error* errPtr UNUSED) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_registry* reg = (reg_registry*)userdata;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sqlite_int64 id = sqlite3_column_int64(stmt, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_snapshot* s = malloc(sizeof(reg_snapshot));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (!s) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    s->reg = reg;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    s->id = id;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    s->proc = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    *snapshot = s;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Type-safe version of `reg_all_objects` for `reg_snapshot`.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] reg       registry to select snapshots from
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] query     the select query to execute
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] query_len length of the query (or -1 for automatic)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] objects  the snapshots selected
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr   on error, a description of the error that occurred
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return               the number of snapshots if success; negative if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int reg_all_snapshots(reg_registry* reg, char* query, int query_len,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_snapshot*** objects, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int lower_bound = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return reg_all_objects(reg, query, query_len, (void***)objects,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            reg_stmt_to_snapshot, &lower_bound, NULL, errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Opens an existing snapshot in the registry.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * NOTE: This function is actually not required but only to make sure that
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *       the user has input a valid sqlite id for snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] reg      registry to open snapshot in
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] id       snapshot id as in registrydb
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr  on error, a description of the error that occurred
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return              the snapshot if success; NULL if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+reg_snapshot* reg_snapshot_open(reg_registry* reg, sqlite_int64 id, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sqlite3_stmt* stmt = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_snapshot* snapshot = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* query = "SELECT id FROM registry.snapshots WHERE id=?";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if ((sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            && (sqlite3_bind_int64(stmt, 1, id) == SQLITE_OK)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int r;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        do {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            r = sqlite3_step(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            switch (r) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_ROW:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    snapshot = (reg_snapshot*)malloc(sizeof(reg_snapshot));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    snapshot->id = sqlite3_column_int64(stmt, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    snapshot->reg = reg;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    snapshot->proc = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_DONE:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    errPtr->code = REG_NOT_FOUND;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    errPtr->description = sqlite3_mprintf("no snapshot found for id=%s", id);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    errPtr->free = (reg_error_destructor*) sqlite3_free;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_BUSY:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                default:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } while (r == SQLITE_BUSY);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (stmt) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        sqlite3_finalize(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return snapshot;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Lists the existing snapshots in the registry for the user to choose
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * from, for restore action
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] reg         registry to search in
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] snapshots  a list of snapshots
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr     on error, a description of the error that occurred
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return                 the number of snapshots if success; false if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int reg_snapshot_list(reg_registry* reg, reg_snapshot*** snapshots, int limit, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    // Currently limiting to last 10 snapshots in the registry
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int lower_bound = limit;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* query;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    query = sqlite3_mprintf("SELECT id FROM registry.snapshots ORDER BY id DESC LIMIT %d",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            lower_bound);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    result = reg_all_snapshots(reg, query, -1, snapshots, errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sqlite3_free(query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Creates a new snapshot in the snapshots registry.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] reg      the registry to create the snapshot in
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] note     any note/details to identify the snapshot by the user
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        if not time
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr  on error, a description of the error that occurred
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return              the snapshot if success; NULL if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+reg_snapshot* reg_snapshot_create(reg_registry* reg, char* note, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sqlite3_stmt* stmt = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_snapshot* snapshot = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* query = "INSERT INTO registry.snapshots (note) VALUES (?)";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if ((sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            && (sqlite3_bind_text(stmt, 1, note, -1, SQLITE_STATIC)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                == SQLITE_OK)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int r;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        do {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            r = sqlite3_step(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            switch (r) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_DONE:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    snapshot = (reg_snapshot*)malloc(sizeof(reg_snapshot));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if (snapshot) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        snapshot->id = sqlite3_last_insert_rowid(reg->db);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        snapshot->reg = reg;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        snapshot->proc = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        int ports_saved = snapshot_store_ports(reg, snapshot, errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        switch (ports_saved) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            case 1:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                // TODO: pass the custom SUCCESS message
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            case 0:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_BUSY:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                default:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } while (r == SQLITE_BUSY);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (stmt) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        sqlite3_finalize(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return snapshot;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * helper method for storing ports for this snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] reg          associated registry
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] snapshot     reg_snapshot, its id to use for foreignkey'ing the ports
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr      on error, a description of the error that occurred
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return                  true if success; 0 if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int snapshot_store_ports(reg_registry* reg, reg_snapshot* snapshot, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_entry** entries;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_error error;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int i, entry_count;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int result = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    entry_count = reg_entry_installed(reg, NULL, &entries, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* key1 = "name";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* key2 = "requested";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* key3 = "state";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* key4 = "variants";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* key5 = "negated_variants";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (entry_count >= 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        for ( i = 0; i < entry_count; i++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            char* port_name;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            char* requested;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            char* state;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            char* positive_variants_str;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            char* negative_variants_str;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            sqlite3_stmt* stmt = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            reg_entry* entry = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (reg_entry_propget(entries[i], key1, &port_name, &error)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                && reg_entry_propget(entries[i], key2, &requested, &error)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                && reg_entry_propget(entries[i], key3, &state, &error)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                && reg_entry_propget(entries[i], key4, &positive_variants_str, &error)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                && reg_entry_propget(entries[i], key5, &negative_variants_str, &error)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                char* query = "INSERT INTO registry.snapshot_ports "
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    "(snapshots_id, port_name, requested, state, variants, negated_variants) "
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    "VALUES (?, ?, ?, ?, ?, ?)";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if ((sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        && (sqlite3_bind_int64(stmt, 1, snapshot->id) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        && (sqlite3_bind_text(stmt, 2, port_name, -1, SQLITE_STATIC) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        && (sqlite3_bind_int64(stmt, 3, atoi(requested)) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        && (sqlite3_bind_text(stmt, 4, state, -1, SQLITE_STATIC) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        && (sqlite3_bind_text(stmt, 5, positive_variants_str, -1, SQLITE_STATIC) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        && (sqlite3_bind_text(stmt, 6, negative_variants_str, -1, SQLITE_STATIC) == SQLITE_OK)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    int r;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    do {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        r = sqlite3_step(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        switch (r) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            case SQLITE_DONE:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            case SQLITE_BUSY:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            default:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                result = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    } while (r == SQLITE_BUSY);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    result = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if (stmt) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    sqlite3_finalize(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(entry);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * reg_snapshot_ports_get: Gets the ports of a snapshot.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] snapshot   snapshot to get property from
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] ports     ports in the 'struct port' form defined in snapshot.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr    on error, a description of the error that occurred
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return                port_count if success; -1 if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int reg_snapshot_ports_get(reg_snapshot* snapshot, port*** ports, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_registry* reg = snapshot->reg;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sqlite3_stmt* stmt = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* query = "SELECT * FROM registry.snapshot_ports WHERE snapshots_id=?";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    const char* port_name;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    const char* state;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    const char* positive_variants;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    const char* negated_variants;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if ((sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        && (sqlite3_bind_int64(stmt, 1, snapshot->id) == SQLITE_OK )) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        // TODO: why 10?
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        port** result = (port**)malloc(10 * sizeof(port*));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (!result) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int result_count = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int result_space = 10;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int r;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        sqlite_int64 snapshot_port_id;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int requested;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        char* variantstr = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        do {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            r = sqlite3_step(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            switch (r) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_ROW:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    snapshot_port_id = sqlite3_column_int64(stmt, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port_name = (const char*) sqlite3_column_text(stmt, 2);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    requested = (int) sqlite3_column_int64(stmt, 3);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    state = (const char*) sqlite3_column_text(stmt, 4);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    positive_variants = (const char*) sqlite3_column_text(stmt, 5);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    negated_variants = (const char*) sqlite3_column_text(stmt, 6);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port* current_port = (port*) malloc(sizeof(port));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if (!current_port) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        return -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    variantstr = malloc(strlen(positive_variants) + strlen(negated_variants) + 1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if (!variantstr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        return -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    variantstr[0] = '\0';
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    strcat(variantstr, positive_variants);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    strcat(variantstr, negated_variants);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    current_port->name = strdup(port_name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    current_port->requested = requested;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    current_port->state = strdup(state);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    current_port->variants = variantstr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if (!reg_listcat((void***)&result, &result_count, &result_space, current_port)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            r = SQLITE_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_DONE:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_BUSY:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                default:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } while (r == SQLITE_ROW || r == SQLITE_BUSY);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        sqlite3_finalize(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (r == SQLITE_DONE) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            *ports = result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return result_count;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            int i;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            for (i=0; i<result_count; i++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                free(result[i]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (stmt) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            sqlite3_finalize(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Gets a named property of a snapshot. The property named must be one
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * that exists in the table and must not be one with internal meaning
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * such as `id` or `state`.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] snapshot   snapshot to get property from
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] key        property to get
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] value     the value of the property
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr    on error, a description of the error that occurred
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return                true if success; false if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int reg_snapshot_propget(reg_snapshot* snapshot, char* key, char** value,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_registry* reg = snapshot->reg;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int result = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sqlite3_stmt* stmt = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* query;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    const char *text;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    query = sqlite3_mprintf("SELECT %q FROM registry.snapshots WHERE id=%lld", key,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            snapshot->id);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int r;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        do {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            r = sqlite3_step(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            switch (r) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_ROW:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    text = (const char*)sqlite3_column_text(stmt, 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if (text) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        *value = strdup(text);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        result = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_DONE:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    errPtr->code = REG_INVALID;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    errPtr->description = "An invalid snapshot was passed";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    errPtr->free = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                case SQLITE_BUSY:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                default:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } while (r == SQLITE_BUSY);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_sqlite_error(reg->db, errPtr, query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (stmt) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        sqlite3_finalize(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sqlite3_free(query);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/cregistry/snapshot.h b/src/cregistry/snapshot.h
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..d169265
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/cregistry/snapshot.h
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,86 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * snapshot.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * vim:tw=80:expandtab
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (c) 2017 The MacPorts Project
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * All rights reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Redistribution and use in source and binary forms, with or without
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * modification, are permitted provided that the following conditions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * are met:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 1. Redistributions of source code must retain the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    notice, this list of conditions and the following disclaimer.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 2. Redistributions in binary form must reproduce the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    notice, this list of conditions and the following disclaimer in the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    documentation and/or other materials provided with the distribution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#ifndef _CSNAPSHOT_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define _CSNAPSHOT_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#if HAVE_CONFIG_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <config.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "registry.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "entry.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sqlite3.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// TODO: extend it to support requested variants
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* variant_name;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* variant_sign;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} variant;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* name;     /* port name */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int requested;  /* 1 if port os requested, else 0 */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* state;    /* 'imaged' or 'installed' */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int variant_count;  /* total number of variants */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* variants; /* string of the form: +var1-var2+var3 */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} port;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    sqlite_int64 id; /* rowid of snapshot in 'registry.snapshots' table */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* note;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    port* ports;    /* list of ports present while taking this snapshot */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_registry* reg; /* associated registry */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* proc; /* name of Tcl proc, if using Tcl */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} reg_snapshot;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// helper to parse variants into 'struct variant' form
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int get_parsed_variants(char* variants_str, variant* all_variants,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* delim, int* variant_count);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// get snapshot using id
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+reg_snapshot* reg_snapshot_open(reg_registry* reg, sqlite_int64 id,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// list all snapshots
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int reg_snapshot_list(reg_registry* reg, reg_snapshot*** snapshots,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int limit, reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// create snapshot method
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+reg_snapshot* reg_snapshot_create(reg_registry* reg, char* note,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// helper method for storing ports for this snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int snapshot_store_ports(reg_registry* reg, reg_snapshot* snapshot,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// snapshot properties retrieval methods
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int reg_snapshot_propget(reg_snapshot* snapshot, char* key, char** value,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int reg_snapshot_ports_get(reg_snapshot* snapshot, port*** ports,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif /* _CSNAPSHOT_H */
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/cregistry/sql.c b/src/cregistry/sql.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 7231918..dddaf28 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/cregistry/sql.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/cregistry/sql.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -134,7 +134,7 @@ int create_tables(sqlite3* db, reg_error* errPtr) {
</span> 
         /* metadata table */
         "CREATE TABLE registry.metadata (key UNIQUE, value)",
<span style='display:block; white-space:pre;background:#ffe0e0;'>-        "INSERT INTO registry.metadata (key, value) VALUES ('version', '1.204')",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        "INSERT INTO registry.metadata (key, value) VALUES ('version', '1.205')",
</span>         "INSERT INTO registry.metadata (key, value) VALUES ('created', strftime('%s', 'now'))",
 
         /* ports table */
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -196,6 +196,27 @@ int create_tables(sqlite3* db, reg_error* errPtr) {
</span>         "CREATE INDEX registry.portgroup_id ON portgroups(id)",
         "CREATE INDEX registry.portgroup_open ON portgroups(id, name, version, size, sha256)",
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* snapshots table */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        "CREATE TABLE registry.snapshots ("
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              "id INTEGER PRIMARY KEY"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", note TEXT"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ")",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* snapshot ports table */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* a complete copy of all the installed ports for a snapshot */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        "CREATE TABLE registry.snapshot_ports ("
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              "id INTEGER PRIMARY KEY"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", snapshots_id INTEGER"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", port_name TEXT COLLATE NOCASE"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", requested INTEGER"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", state TEXT COLLATE NOCASE"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", variants TEXT"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", negated_variants TEXT"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ", FOREIGN KEY(snapshots_id) REFERENCES snapshots(id)"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            " ON DELETE CASCADE"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ")",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>         "COMMIT",
         NULL
     };
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -692,7 +713,49 @@ int update_db(sqlite3* db, reg_error* errPtr) {
</span> #endif
 
                 "UPDATE registry.metadata SET value = '1.204' WHERE key = 'version'",
<span style='display:block; white-space:pre;background:#e0ffe0;'>+                "COMMIT",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                NULL
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            };
</span> 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+            sqlite3_finalize(stmt);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            stmt = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (!do_queries(db, version_1_204_queries, errPtr)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                rollback_db(db);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            did_update = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (sql_version(NULL, -1, version, -1, "1.205") < 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            /* Add tables required for the snapshot functionality used by 'port
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             * snapshot', 'port migrate' and 'port restore'. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            static char* version_1_205_queries[] = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                /* snapshots table */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                "CREATE TABLE registry.snapshots ("
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                      "id INTEGER PRIMARY KEY"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", note TEXT"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ")",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                /* snapshot ports table */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                "CREATE TABLE registry.snapshot_ports ("
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                      "id INTEGER PRIMARY KEY"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", snapshots_id INTEGER"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", port_name TEXT COLLATE NOCASE"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", requested INTEGER"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", state TEXT COLLATE NOCASE"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", variants TEXT"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", negated_variants TEXT"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ", FOREIGN KEY(snapshots_id) REFERENCES snapshots(id)"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    " ON DELETE CASCADE"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ")",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                /* Update version and commit */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                "UPDATE registry.metadata SET value = '1.205' WHERE key = 'version'",
</span>                 "COMMIT",
                 NULL
             };
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -700,7 +763,7 @@ int update_db(sqlite3* db, reg_error* errPtr) {
</span>             sqlite3_finalize(stmt);
             stmt = NULL;
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-            if (!do_queries(db, version_1_204_queries, errPtr)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (!do_queries(db, version_1_205_queries, errPtr)) {
</span>                 rollback_db(db);
                 return 0;
             }
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -718,7 +781,7 @@ int update_db(sqlite3* db, reg_error* errPtr) {
</span>          *  - update the current version number below
          */
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-        if (sql_version(NULL, -1, version, -1, "1.204") > 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (sql_version(NULL, -1, version, -1, "1.205") > 0) {
</span>             /* the registry was already upgraded to a newer version and cannot be used anymore */
             reg_throw(errPtr, REG_INVALID, "Version number in metadata table is newer than expected.");
             sqlite3_finalize(stmt);
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/Makefile.in b/src/macports1.0/Makefile.in
</span><span style='display:block; white-space:pre;color:#808080;'>index 4af9700..133136e 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/macports1.0/Makefile.in
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/Makefile.in
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4,7 +4,8 @@ VPATH  = @srcdir@
</span> include ../../Mk/macports.autoconf.mk
 
 SRCS=          macports.tcl macports_dlist.tcl macports_util.tcl \
<span style='display:block; white-space:pre;background:#ffe0e0;'>-                macports_autoconf.tcl diagnose.tcl reclaim.tcl selfupdate.tcl
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           macports_autoconf.tcl diagnose.tcl reclaim.tcl snapshot.tcl restore.tcl \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           migrate.tcl selfupdate.tcl
</span> OBJS=             macports.o get_systemconfiguration_proxies.o sysctl.o
 SHLIB_NAME=    MacPorts${SHLIB_SUFFIX}
 
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/macports.tcl b/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 2e18650..bf4ab36 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -38,6 +38,9 @@ package require macports_util 1.0
</span> package require diagnose 1.0
 package require reclaim 1.0
 package require selfupdate 1.0
<span style='display:block; white-space:pre;background:#e0ffe0;'>+package require snapshot 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require restore 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require migrate 1.0
</span> package require Tclx
 
 # catch wrapper shared with port1.0
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -585,7 +588,6 @@ proc macports::_is_valid_developer_dir {dir} {
</span>     return 1
 }
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span> proc mportinit {{up_ui_options {}} {up_options {}} {up_variations {}}} {
     # Disable unknown(n)'s behavior of running unknown commands in the system
     # shell
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -703,9 +705,10 @@ proc mportinit {{up_ui_options {}} {up_options {}} {up_variations {}}} {
</span>     }
 
     # Check that the current platform is the one we were configured for, otherwise need to do migration
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    if {($os_platform ne $macports::autoconf::os_platform) || ($os_major != $macports::autoconf::os_major)} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    set skip_migration_check [expr {[info exists macports::global_options(ports_no_migration_check)] && $macports::global_options(ports_no_migration_check)}]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if {!$skip_migration_check && [migrate::needs_migration]} {
</span>         ui_error "Current platform \"$os_platform $os_major\" does not match expected platform \"$macports::autoconf::os_platform $macports::autoconf::os_major\""
<span style='display:block; white-space:pre;background:#ffe0e0;'>-        ui_error "If you upgraded your OS, please follow the migration instructions: https://trac.macports.org/wiki/Migration"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_error "Please run 'sudo port migrate' or follow the migration instructions: https://trac.macports.org/wiki/Migration"
</span>         return -code error "OS platform mismatch"
     }
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4518,7 +4521,6 @@ proc macports::arch_runnable {arch} {
</span> }
 
 proc macports::diagnose_main {opts} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    
</span>     # Calls the main function for the 'port diagnose' command.
     #
     # Args: 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4555,6 +4557,82 @@ proc macports::reclaim_check_and_run {} {
</span>     }
 }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+# create a snapshot. A snapshot is basically an inventory of what is installed
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# along with meta data like requested and variants, and stored in the sqlite
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# database.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc macports::snapshot_main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Calls the main function for the 'port snapshot' command.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #           $opts having a 'note'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #           0 on successful execution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return [snapshot::main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# restores a snapshot.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc macports::restore_main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Calls the main function for the 'port restore' command.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #           $opts having a 'snapshot-id' but not compulsorily
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #           0 on successful execution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return [restore::main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+##
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Calls the main function for the 'port migrate' command.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# @returns 0 on success, -999 when MacPorts base has been upgraded and the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#          caller should re-run itself and invoke migration with the --continue
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#          flag set.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc macports::migrate_main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return [migrate::main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# create a snapshot. A snapshot is basically an inventory of what is installed
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# along with meta data like requested and variants, and stored in the sqlite
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# database.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc macports::snapshot_main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Calls the main function for the 'port snapshot' command.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #           $opts having a 'note'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #           0 on successful execution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return [snapshot::main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# restores a snapshot.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc macports::restore_main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Calls the main function for the 'port restore' command.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #           $opts having a 'snapshot-id' but not compulsorily
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #           0 on successful execution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return [restore::main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+##
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Calls the main function for the 'port migrate' command.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# @returns 0 on success, -999 when MacPorts base has been upgraded and the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#          caller should re-run itself and invoke migration with the --continue
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#          flag set.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc macports::migrate_main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return [migrate::main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> proc macports::reclaim_main {opts} {
     # Calls the main function for the 'port reclaim' command.
     #
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4633,7 +4711,6 @@ proc macports::get_actual_cxx_stdlib {binaries} {
</span>     }
 }
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span> ##
 # Execute the rev-upgrade scan and attempt to rebuild all ports found to be
 # broken. Depends on the revupgrade_mode setting from macports.conf.
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/migrate.tcl b/src/macports1.0/migrate.tcl
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..c4cba52
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/migrate.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,171 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# migrate.tcl
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Copyright (c) 2017 The MacPorts Project
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# All rights reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Redistribution and use in source and binary forms, with or without
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# modification, are permitted provided that the following conditions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# are met:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 1. Redistributions of source code must retain the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    notice, this list of conditions and the following disclaimer.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 2. Redistributions in binary form must reproduce the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    notice, this list of conditions and the following disclaimer in the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    documentation and/or other materials provided with the distribution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 3. Neither the name of Apple Inc. nor the names of its contributors
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    may be used to endorse or promote products derived from this software
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    without specific prior written permission.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package provide migrate 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require macports 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require registry 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require snapshot 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require restore 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require selfupdate 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+namespace eval migrate {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ##
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # The main function. Calls each individual step in order.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # @returns 0 on success, -999 when MacPorts base has been upgraded and the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #          caller should re-run itself and invoke migration with the --continue
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #          flag set.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set options $opts
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {[needs_migration]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[info exists macports::ui_options(questions_yesno)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set msg "Migration will first upgrade MacPorts and then reinstall all installed ports."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set retvalue [$macports::ui_options(questions_yesno) $msg "MigrationPrompt" "" {y} 0 "Would you like to continue?"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {$retvalue == 1} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    # quit as user answered 'no'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ui_msg "Aborting migration. You can re-run 'sudo port migrate' later or follow the migration instructions: https://trac.macports.org/wiki/Migration"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    return 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_msg "Upgrading MacPorts..."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set success no
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[catch {set success [upgrade_port_command]} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_debug $::errorInfo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_error "Upgrading port command failed. Try running 'sudo port -v selfupdate' and then 'sudo port migrate'."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {!$success} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_error "Upgrading port command failed or was not attempted. Please re-install MacPorts manually and then run 'sudo port migrate' again."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # MacPorts successfully upgraded, automatically re-run migration
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # from the new MacPorts installation
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return -999
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # If port migrate was not called with --continue, the user probably did
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # that manually and we do not have confirmation to run migration yet;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # do that now.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set continuation [expr {[info exists options(ports_migrate_continue)] && $options(ports_migrate_continue)}]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {!$continuation && [info exists macports::ui_options(questions_yesno)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set msg "Migration will reinstall all installed ports."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set retvalue [$macports::ui_options(questions_yesno) $msg "MigrationContinuationPrompt" "" {y} 0 "Would you like to continue?"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {$retvalue == 1} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # quit as user answered 'no'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_msg "Aborting migration. You can re-run 'sudo port migrate' later or follow the migration instructions: https://trac.macports.org/wiki/Migration"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # create a snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "Taking a snapshot of the current state..."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set snapshot [snapshot::main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {$snapshot == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return -1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set id [$snapshot id]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set note [$snapshot note]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set datetime [$snapshot created_at]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "Done: Snapshot '$id' : '$note' created at $datetime"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "Uninstalling all ports..."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        uninstall_installed
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "Restoring ports..."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return [restore_snapshot]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ##
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Check whether the current platform is the one this installation was
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # configured for. Returns true, if migration is needed, false otherwise.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # @return true iff the migration procedure is needed
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc needs_migration {} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {$macports::os_platform ne $macports::autoconf::os_platform} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {$macports::os_major != $macports::autoconf::os_major} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ##
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Uninstall all installed ports for migration
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # @return void on success, raises an error on failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc uninstall_installed {} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set options {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set portlist [restore::portlist_sort_dependencies_later [registry::entry imaged]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach port $portlist {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_msg "Uninstalling: [$port name]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {![registry::run_target $port uninstall $options]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_error "Error uninstalling [$port name]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ##
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Restore the list of ports from the latest snapshot using the equivalent
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # of 'port restore --last'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # @return 0 on success, an error on failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc restore_snapshot {} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set options {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set options(ports_restore_last) yes
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return [restore::main [array get options]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ##
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Run MacPorts selfupdate, but avoid downgrading pre-release installations
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Will return true on success, false if no error occured but MacPorts was
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # not re-installed (e.g. because the currently installed version is newer
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # than the downloaded release). If reinstallation fails, an error is
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # raised.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # @return true on success, false if no update was performed, an error on
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    #         failure.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc upgrade_port_command {} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set optionslist {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Force rebuild, but do not allow downgrade
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set optionslist(ports_selfupdate_migrate) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Avoid portindex, which would trigger 'portindex', which does not work
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set optionslist(ports_selfupdate_nosync) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        selfupdate::main [array get optionslist] base_updated
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return $base_updated
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/restore.tcl b/src/macports1.0/restore.tcl
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..d9561ef
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/restore.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,359 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# restore.tcl
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Copyright (c) 2017 The MacPorts Project
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# All rights reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Redistribution and use in source and binary forms, with or without
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# modification, are permitted provided that the following conditions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# are met:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 1. Redistributions of source code must retain the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    notice, this list of conditions and the following disclaimer.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 2. Redistributions in binary form must reproduce the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    notice, this list of conditions and the following disclaimer in the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    documentation and/or other materials provided with the distribution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 3. Neither the name of Apple Inc. nor the names of its contributors
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    may be used to endorse or promote products derived from this software
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    without specific prior written permission.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package provide restore 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require macports 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require migrate 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require registry 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require snapshot 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+namespace eval restore {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # The main function. If the action is provided a snapshot id, then it deactivates
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # all the current ports and restores the specified snapshot.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # If '--last', then it assumes that this is a continution step of migration and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # no need of deactivating, since no ports exist. It simply sorts and installs the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # last snapshot ports
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # If none, then it lists the last k (or all) snapshots with id for the user to
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # choose from. Deactivates and installs the selected snapshot.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #           opts - options array.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #           0 if success
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set options $opts
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {[migrate::needs_migration]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_error "You need to run 'sudo port migrate' before running restore"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {[info exists options(ports_restore_snapshot-id)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # use the specified snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set snapshot [fetch_snapshot $options(ports_restore_snapshot-id)]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_msg "Deactivating all ports installed.."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            deactivate_all
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } elseif {[info exists options(ports_restore_last)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set snapshot [fetch_snapshot_last]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set snapshots [list_snapshots]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set human_readable_snapshots {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            foreach snapshot $snapshots {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                lappend human_readable_snapshots "[$snapshot note], created at [$snapshot created_at] (ID: [$snapshot id])"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[llength $snapshots] == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_error "There are no snapshots to restore. You must run 'sudo port snapshot' first."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set retstring [$macports::ui_options(questions_singlechoice) "Select any one snapshot to restore:" "" $human_readable_snapshots]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set snapshot [lindex $snapshots $retstring]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_msg "Deactivating all ports installed.."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            deactivate_all
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "Restoring snapshot '[$snapshot note]' created at [$snapshot created_at]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "Fetching ports to install..."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set snapshot_portlist [$snapshot ports]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "Restoring the selected snapshot.."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        restore_state [$snapshot ports]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc fetch_snapshot {snapshot_id} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return [registry::snapshot get_by_id $snapshot_id]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc fetch_snapshot_last {} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return [registry::snapshot get_last]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc list_snapshots {} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return [registry::snapshot get_all]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc portlist_sort_dependencies_later {portlist} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Sorts a list of port references such that ports come before
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # their dependencies. Ideal for uninstalling a port.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       portlist - the list of port references
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       the list in dependency-sorted order
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach port $portlist {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set portname [$port name]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            lappend entries($portname) $port
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # Avoid adding ports in loop
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {![info exists dependents($portname)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set dependents($portname) {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                foreach result [$port dependents] {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    lappend dependents($portname) [$result name]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set ret {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach port $portlist {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            portlist_sort_dependencies_later_helper $port entries dependents seen ret
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return $ret
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc portlist_sort_dependencies_later_helper {port up_entries up_dependents up_seen up_retlist} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        upvar 1 $up_seen seen
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {![info exists seen($port)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set seen($port) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            upvar 1 $up_entries entries $up_dependents dependents $up_retlist retlist
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set name [$port name]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            foreach dependent $dependents($name) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {[info exists entries($dependent)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    foreach entry $entries($dependent) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        portlist_sort_dependencies_later_helper $entry entries dependents seen retlist
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            lappend retlist $port
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc deactivate_all {} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set portlist [portlist_sort_dependencies_later [registry::entry imaged]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach port $portlist {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_msg "Deactivating: [$port name]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[$port state] eq "installed"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {[registry::run_target $port deactivate {}]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    continue
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc portlist_sort_dependencies_first {portlist} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Sorts a list of port references such that ports appear after
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # their dependencies. Ideal for installing a port.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       portlist - the list of port references
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       the list in dependency-sorted order
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set port_installed {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set port_deps {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set port_in_list {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set new_list [list]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach port $portlist {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set name [lindex $port 0]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set requested [lindex $port 1]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {$requested eq 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                continue
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set active 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[lindex $port 2] eq "installed"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set active 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set variantstr [lindex $port 3]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {$variantstr eq "(null)"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set variantstr ""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set variants ""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[info exists variantstr]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                while 1 {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    set nextplus [string last + $variantstr]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    set nextminus [string last - $variantstr]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if {$nextplus > $nextminus} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        set next $nextplus
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        set sign +
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        set next $nextminus
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        set sign -
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if {$next == -1} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        break
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    set v [string range $variantstr [expr $next + 1] end]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    lappend variants $v $sign
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    set variantstr [string range $variantstr 0 [expr $next - 1]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {![info exists port_in_list($name)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set port_in_list($name) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set port_installed($name) 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                incr port_in_list($name)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {![info exists port_deps(${name},${variants})]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set port_deps(${name},${variants}) [portlist_sort_dependencies_first_helper $name $variants]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            lappend new_list [list $name $variants $active]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set operation_list [list]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        while {[llength $new_list] > 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set oldLen [llength $new_list]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            foreach port $new_list {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                foreach {name variants active} $port break
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {$active && $port_installed($name) < ($port_in_list($name) - 1)} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    continue
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set installable 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                foreach dep $port_deps(${name},${variants}) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if {[info exists port_installed($dep)] && $port_installed($dep) == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        set installable 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        break
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {$installable} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    lappend operation_list [list $name $variants $active]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    incr port_installed($name)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    set index [lsearch $new_list [list $name $variants $active]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    set new_list [lreplace $new_list $index $index]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[llength $new_list] == $oldLen} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return -code error "Stuck in loop"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return $operation_list
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc portlist_sort_dependencies_first_helper {portname variant_info} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set dependency_list [list]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set port_search_result [mportlookup $portname]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {[llength $port_search_result] < 2} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_warn "Skipping $portname (not in the ports tree)"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return $dependency_list
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set portinfo [lindex $port_search_result 1]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {[catch {set mport [mportopen $portinfo(porturl) [list subport $portinfo(name)] $variant_info]} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            global errorInfo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            puts stderr "$errorInfo"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return -code error "Unable to open port '$portname': $result"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array unset portinfo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set portinfo [mportinfo $mport]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        mportclose $mport
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set dependency_types { depends_fetch depends_extract depends_build depends_lib depends_run }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach dependency_type $dependency_types {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[info exists portinfo($dependency_type)] && [string length $portinfo($dependency_type)] > 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                foreach dependency $portinfo($dependency_type) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    lappend dependency_list [lindex [split $dependency:] end]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return $dependency_list
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc restore_state {snapshot_portlist} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "Installing ports:"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set snapshot_portlist [lsort -index 0 -nocase $snapshot_portlist]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach port $snapshot_portlist {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # 0: port name
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # 1: requested (0/1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # 2: state (imaged/installed, i.e. inactive/active)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # 3: variants
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[lindex $port 1] == 1} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # Hide unrequested ports
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {[lindex $port 2] eq "installed"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ui_msg "   [lindex $port 0] [lindex $port 3]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ui_msg "   [lindex $port 0] [lindex $port 3] (inactive)"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set sorted_snapshot_portlist [portlist_sort_dependencies_first $snapshot_portlist]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach port $sorted_snapshot_portlist {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set name [string trim [lindex $port 0]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set variations [lindex $port 1]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set active [lindex $port 2]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {!$active} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set target install
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_msg "Installing (not activating): $name $variations"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set target activate
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_msg "Installing (and activating): $name $variations"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[catch {set res [mportlookup $name]} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                global errorInfo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_debug "$errorInfo"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return -code error "lookup of port $name failed: $result"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[llength $res] < 2} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                # not in the index, but we already warned about that earlier
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                continue
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            array unset portinfo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            array set portinfo [lindex $res 1]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set porturl $portinfo(porturl)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set options(ports_requested) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set options(subport) $portinfo(name)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[catch {set workername [mportopen $porturl [array get options] $variations]} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                global errorInfo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                puts stderr "$errorInfo"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return -code error "Unable to open port '$name': $result"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[catch {set result [mportexec $workername $target]} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                global errorInfo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                mportclose $workername
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                ui_msg "$errorInfo"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return -code error "Unable to execute target 'install' for port '$name': $result"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                mportclose $workername
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # TODO: some ports may get re-activated to fulfil dependencies - recheck?
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/selfupdate.tcl b/src/macports1.0/selfupdate.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index eea5a40..009d5a7 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/macports1.0/selfupdate.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/selfupdate.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -158,7 +158,10 @@ proc selfupdate::main {{optionslist {}} {updatestatusvar {}}} {
</span>         }
     }
 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    if {$use_the_force_luke || $comp > 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # Check whether we need to re-install base because of a migration
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    set migrating [expr {[info exists options(ports_selfupdate_migrate)] && $options(ports_selfupdate_migrate)}]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if {$use_the_force_luke || $comp > 0 || ($comp == 0 && $migrating)} {
</span>         if {[info exists options(ports_dryrun)] && $options(ports_dryrun)} {
             ui_msg "$macports::ui_prefix MacPorts base is outdated, selfupdate would install $macports_version_new (dry run)"
         } else {
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/snapshot.tcl b/src/macports1.0/snapshot.tcl
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..04d4707
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/snapshot.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,82 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# snapshot.tcl
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Copyright (c) 2017 The MacPorts Project
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# All rights reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Redistribution and use in source and binary forms, with or without
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# modification, are permitted provided that the following conditions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# are met:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 1. Redistributions of source code must retain the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    notice, this list of conditions and the following disclaimer.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 2. Redistributions in binary form must reproduce the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    notice, this list of conditions and the following disclaimer in the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    documentation and/or other materials provided with the distribution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# 3. Neither the name of Apple Inc. nor the names of its contributors
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    may be used to endorse or promote products derived from this software
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#    without specific prior written permission.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package provide snapshot 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require macports 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+package require registry 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+namespace eval snapshot {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   proc main {opts} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           # Function to create a snapshot of the current state of ports.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #           opts - The options passed in.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #           registry::snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        array set options $opts
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        registry::write {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # An option used by user while creating snapshot manually
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # to identify a snapshot, usually followed by `port restore`
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[info exists options(ports_snapshot_note)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set note $options(ports_snapshot_note)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set note "snapshot created for migration"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set inactive_ports  [list]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            foreach port [registry::entry imaged] {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {[$port state] eq "imaged"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    lappend inactive_ports "[$port name] @[$port version]_[$port revision] [$port variants][$port negated_variants]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if {[llength $inactive_ports] != 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set msg "Following inactive ports will not be a part of this snapshot and won't be installed while restoring:"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                set inactive_ports [lsort -index 0 -nocase $inactive_ports]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {[info exists macports::ui_options(questions_yesno)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    set retvalue [$macports::ui_options(questions_yesno) $msg "Continue?" $inactive_ports {y} 0]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if {$retvalue != 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        ui_msg "Not creating a snapshot!"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        return 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    puts $msg
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    foreach port $inactive_ports {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        puts $port
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set snapshot [registry::snapshot create $note]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            # TODO: catch
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return $snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/pextlib1.0/tracelib.c b/src/pextlib1.0/tracelib.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 9ca810a..ceb3a3a 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/pextlib1.0/tracelib.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/pextlib1.0/tracelib.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -56,6 +56,7 @@
</span> #include <sys/un.h>
 #include <unistd.h>
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <cregistry/snapshot.h>
</span> #include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 #include <registry2.0/registry.h>
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/port/port.tcl b/src/port/port.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 94cf93e..91520ad 100755
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/port/port.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/port/port.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -2796,6 +2796,32 @@ proc action_reclaim { action portlist opts } {
</span>     return $status
 }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+proc action_snapshot { action portlist opts } {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if {[catch {macports::snapshot_main $opts} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_debug $::errorInfo
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   return 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc action_restore { action portlist opts } {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return [macports::restore_main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc action_migrate { action portlist opts } {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    set result [macports::migrate_main $opts]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if {$result == -999} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # MacPorts base was upgraded, re-execute migrate with the --continue flag
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        execl $::argv0 [list {*}$::argv "--continue"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_debug "Would have executed $::argv0 $::argv --continue"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_error "Failed to re-execute MacPorts migration, please run 'sudo port migrate' manually."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if {$result == -1} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # snapshot error
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_error "Failed to create a snapshot and migration cannot proceed."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return $result
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span> 
 proc action_upgrade { action portlist opts } {
     if {[require_portlist portlist "yes"] || (![macports::global_option_isset ports_dryrun] && [prefix_unwritable])} {
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4354,6 +4380,10 @@ array set action_array [list \
</span>     mpkg        [list action_target         [ACTION_ARGS_PORTS]] \
     pkg         [list action_target         [ACTION_ARGS_PORTS]] \
     \
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    snapshot    [list action_snapshot       [ACTION_ARGS_STRINGS]] \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    restore     [list action_restore        [ACTION_ARGS_STRINGS]] \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    migrate     [list action_migrate        [ACTION_ARGS_STRINGS]] \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    \
</span>     quit        [list action_exit           [ACTION_ARGS_NONE]] \
     exit        [list action_exit           [ACTION_ARGS_NONE]] \
 ]
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4435,7 +4465,7 @@ array set cmd_opts_array {
</span>                  depends description epoch exact glob homepage line
                  long_description maintainer maintainers name platform
                  platforms portdir regex revision variant variants version}
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    selfupdate  {no-sync nosync}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    selfupdate  {migrate no-sync nosync}
</span>     space       {{units 1} total}
     activate    {no-exec}
     deactivate  {no-exec}
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4453,6 +4483,9 @@ array set cmd_opts_array {
</span>     reclaim     {enable-reminders disable-reminders}
     fetch       {no-mirrors}
     bump        {patch}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    snapshot    {{note 1}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    restore     {{snapshot-id 1} last}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    migrate     {continue}
</span> }
 
 ##
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -5732,7 +5765,11 @@ set remaining_args [lrange $cmd_argv $cmd_argn end]
</span> # shell mode
 if { [llength $remaining_args] == 0 && ![info exists ui_options(ports_commandfiles)] } {
     lappend ui_options(ports_commandfiles) -
<span style='display:block; white-space:pre;background:#ffe0e0;'>-} elseif {[lookahead] eq "selfupdate" || [lookahead] eq "sync"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} elseif {[lookahead] in {"selfupdate" "migrate"}} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    # tell mportinit not to tell the user they should selfupdate and skip the migration check
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    set ui_options(ports_no_old_index_warning) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    set global_options(ports_no_migration_check) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} elseif {[lookahead] eq "sync"} {
</span>     # tell mportinit not to tell the user they should selfupdate
     set ui_options(ports_no_old_index_warning) 1
 }
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/Makefile.in b/src/registry2.0/Makefile.in
</span><span style='display:block; white-space:pre;color:#808080;'>index 7ab536f..a3899c6 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/registry2.0/Makefile.in
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/Makefile.in
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -9,7 +9,8 @@ SRCS = registry.tcl registry_util.tcl receipt_flat.tcl receipt_sqlite.tcl portim
</span> OBJS = registry.o util.o \
        entry.o entryobj.o \
        file.o fileobj.o \
<span style='display:block; white-space:pre;background:#ffe0e0;'>-        portgroup.o portgroupobj.o
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   portgroup.o portgroupobj.o \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   snapshot.o snapshotobj.o
</span> 
 SHLIB_NAME= registry${SHLIB_SUFFIX}
 INSTALLDIR= ${TCL_PACKAGE_PATH}/registry2.0
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/registry.c b/src/registry2.0/registry.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 40a860c..596917d 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/registry2.0/registry.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/registry.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -39,11 +39,13 @@
</span> #include <cregistry/registry.h>
 #include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <cregistry/snapshot.h>
</span> #include <cregistry/file.h>
 
 #include "entry.h"
 #include "entryobj.h"
 #include "file.h"
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "snapshot.h"
</span> #include "portgroup.h"
 #include "registry.h"
 #include "util.h"
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -385,6 +387,7 @@ int Registry_Init(Tcl_Interp* interp) {
</span>     Tcl_CreateObjCommand(interp, "registry::read", registry_read, NULL, NULL);
     Tcl_CreateObjCommand(interp, "registry::write", registry_write, NULL, NULL);
     Tcl_CreateObjCommand(interp, "registry::entry", entry_cmd, NULL, NULL);
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    Tcl_CreateObjCommand(interp, "registry::snapshot", snapshot_cmd, NULL, NULL);
</span>     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);
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/registry.h b/src/registry2.0/registry.h
</span><span style='display:block; white-space:pre;color:#808080;'>index b5e7bd2..185390f 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/registry2.0/registry.h
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/registry.h
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -35,6 +35,7 @@
</span> #include <sqlite3.h>
 #include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <cregistry/snapshot.h>
</span> 
 typedef struct _entry_list {
     reg_entry* entry;
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/snapshot.c b/src/registry2.0/snapshot.c
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..da727b6
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/snapshot.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,209 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * snapshot.c
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * vim:tw=80:expandtab
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (c) 2017 The MacPorts Project
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * All rights reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Redistribution and use in source and binary forms, with or without
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * modification, are permitted provided that the following conditions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * are met:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 1. Redistributions of source code must retain the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    notice, this list of conditions and the following disclaimer.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 2. Redistributions in binary form must reproduce the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    notice, this list of conditions and the following disclaimer in the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    documentation and/or other materials provided with the distribution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#if HAVE_CONFIG_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <config.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <string.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <stdlib.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <tcl.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sqlite3.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <cregistry/util.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "snapshot.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "snapshotobj.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "registry.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "util.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Removes the snapshot from the Tcl interpreter. Doesn't actually delete it since
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * that's the registry's job. This is written to be used as the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * `Tcl_CmdDeleteProc` for a snapshot object command.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] clientData address of a reg_snapshot to remove
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+void delete_snapshot(ClientData clientData) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_snapshot* snapshot = (reg_snapshot*)clientData;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    free(snapshot->proc);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    snapshot->proc = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * registry::snaphot create note
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * note is required
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int snapshot_create(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_registry* reg = registry_for(interp, reg_attached);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc > 3) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 2, objv, "create_snapshot ?note?");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else if (reg == NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        char* note = Tcl_GetString(objv[2]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error error;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_snapshot* new_snaphot = reg_snapshot_create(reg, note, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (new_snaphot != NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            Tcl_Obj* result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (snapshot_to_obj(interp, &result, new_snaphot, NULL, &error)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                Tcl_SetObjResult(interp, result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return TCL_OK;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * registry::snaphot get snapshot_id
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * snapshot_id is required
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int snapshot_get(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_registry* reg = registry_for(interp, reg_attached);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc > 3) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 2, objv, "get_by_id ?snapshot_id?");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else if (reg == NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        sqlite_int64 id = atoll(Tcl_GetString(objv[2]));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error error;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_snapshot* snapshot = reg_snapshot_open(reg, id, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (snapshot != NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            Tcl_Obj* result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (snapshot_to_obj(interp, &result, snapshot, NULL, &error)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                Tcl_SetObjResult(interp, result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return TCL_OK;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * registry::snaphot get_last
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int snapshot_get_last(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_registry* reg = registry_for(interp, reg_attached);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc > 2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 1, objv, "get_last");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else if (reg == NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error error;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_snapshot** snapshots;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int limit = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        // 1 passed in reg_snapshot_list is to get the last '1' snapshot.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int snapshot_count = reg_snapshot_list(reg, &snapshots, limit, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (snapshot_count >= 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            int retval;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            Tcl_Obj* result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (snapshot_to_obj(interp, &result, snapshots[0], NULL, &error)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                Tcl_SetObjResult(interp, result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                retval = TCL_OK;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                retval = registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(snapshots);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return retval;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * registry::snaphot get_all
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int snapshot_get_list(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    reg_registry* reg = registry_for(interp, reg_attached);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc > 2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 1, objv, "get_all");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else if (reg == NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error error;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_snapshot** snapshots;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int limit = 10;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        // limit = k passed in reg_snapshot_list is to get the last 'k' snapshots
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int snapshot_count = reg_snapshot_list(reg, &snapshots, limit, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (snapshot_count >= 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            int retval;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            Tcl_Obj* result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            Tcl_Obj** objs;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (list_snapshot_to_obj(interp, &objs, snapshots, snapshot_count, &error)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                result = Tcl_NewListObj(snapshot_count, objs);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                Tcl_SetObjResult(interp, result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                free(objs);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                retval = TCL_OK;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                retval = registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(snapshots);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return retval;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* name;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int (*function)(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} snapshot_cmd_type;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static snapshot_cmd_type snapshot_cmds[] = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    /* Global commands */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { "create", snapshot_create},
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { "get_by_id", snapshot_get},
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { "get_last", snapshot_get_last},
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { "get_all", snapshot_get_list},
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { NULL, NULL }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * registry::snapshot cmd ?arg ...?
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Commands manipulating snapshots in the registry.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int snapshot_cmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int cmd_index;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc < 2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (Tcl_GetIndexFromObjStruct(interp, objv[1], snapshot_cmds,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                sizeof(snapshot_cmd_type), "cmd", 0, &cmd_index) == TCL_OK) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        snapshot_cmd_type* cmd = &snapshot_cmds[cmd_index];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return cmd->function(interp, objc, objv);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/registry.h b/src/registry2.0/snapshot.h
</span>similarity index 75%
copy from src/registry2.0/registry.h
copy to src/registry2.0/snapshot.h
<span style='display:block; white-space:pre;color:#808080;'>index b5e7bd2..e262065 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/registry2.0/registry.h
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/snapshot.h
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -1,7 +1,8 @@
</span> /*
<span style='display:block; white-space:pre;background:#ffe0e0;'>- * registry.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * snapshot.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * vim:tw=80:expandtab
</span>  *
<span style='display:block; white-space:pre;background:#ffe0e0;'>- * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (c) 2017 The MacPorts Project
</span>  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -24,24 +25,19 @@
</span>  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#ifndef _REGISTRY_H
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-#define _REGISTRY_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#ifndef _SNAPSHOT_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define _SNAPSHOT_H
</span> 
 #if HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 #include <tcl.h>
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#include <sqlite3.h>
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-#include <cregistry/portgroup.h>
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-#include <cregistry/entry.h>
</span> 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-typedef struct _entry_list {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    reg_entry* entry;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    struct _entry_list* next;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-} entry_list;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+void delete_snapshot(ClientData clientData);
</span> 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-reg_registry* registry_for(Tcl_Interp* interp, int status);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-int registry_failed(Tcl_Interp* interp, reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int snapshot_cmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_Obj* CONST objv[]);
</span> 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#endif /* _REGISTRY_H */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif /* _SNAPSHOT_H */
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/snapshotobj.c b/src/registry2.0/snapshotobj.c
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..63fb525
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/snapshotobj.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,199 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * snapshotobj.c
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * vim:tw=80:expandtab
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (c) 2017 The MacPorts Project
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * All rights reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Redistribution and use in source and binary forms, with or without
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * modification, are permitted provided that the following conditions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * are met:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 1. Redistributions of source code must retain the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    notice, this list of conditions and the following disclaimer.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 2. Redistributions in binary form must reproduce the above copyright
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    notice, this list of conditions and the following disclaimer in the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *    documentation and/or other materials provided with the distribution.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#if HAVE_CONFIG_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <config.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* required for asprintf(3) on macOS */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define _DARWIN_C_SOURCE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* required for asprintf(3) on Linux */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define _GNU_SOURCE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <string.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <stdlib.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <tcl.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sqlite3.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "snapshotobj.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "registry.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "util.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+const char* snapshot_props[] = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    "id",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    "note",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    "ports",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    "created_at",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    NULL
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* ${snapshot} id */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int snapshot_obj_id(Tcl_Interp* interp, reg_snapshot* snapshot, int objc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int index;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc > 3) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 2, objv, "?value?");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc == 2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* ${snapshot} id; return the current value */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        char value[sizeof(snapshot->id)+1];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        sprintf(value, "%lld", snapshot->id);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (Tcl_GetIndexFromObj(interp, objv[1], snapshot_props, "prop", 0, &index)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                == TCL_OK) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            Tcl_Obj* result = Tcl_NewStringObj(value, -1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            Tcl_SetObjResult(interp, result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return TCL_OK;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* ${snapshot} prop */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int snapshot_obj_prop(Tcl_Interp* interp, reg_snapshot* snapshot, int objc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int index;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc > 3) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 2, objv, "?value?");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc == 2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* ${snapshot} prop; return the current value */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_registry* reg = registry_for(interp, reg_attached);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (reg == NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (Tcl_GetIndexFromObj(interp, objv[1], snapshot_props, "prop", 0, &index)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                == TCL_OK) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            char* key = Tcl_GetString(objv[1]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            char* value;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            reg_error error;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (reg_snapshot_propget(snapshot, key, &value, &error)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                Tcl_Obj* result = Tcl_NewStringObj(value, -1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                Tcl_SetObjResult(interp, result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                free(value);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                return TCL_OK;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* ${snapshot} ports */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int snapshot_obj_ports(Tcl_Interp* interp, reg_snapshot* snapshot, int objc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int index;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc > 3) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 2, objv, "?value?");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc == 2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* ${snapshot} prop; return the current value */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_registry* reg = registry_for(interp, reg_attached);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (reg == NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (Tcl_GetIndexFromObj(interp, objv[1], snapshot_props, "prop", 0, &index)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                == TCL_OK) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            port** ports;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            reg_error error;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            int port_count = reg_snapshot_ports_get(snapshot, &ports, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (port_count >= 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                char* portstrs[port_count + 1];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                int i;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                for(i = 0; i < port_count; i++){
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port* current_port = ports[i];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    portstrs[i] = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    if (asprintf(&portstrs[i], "%s %d %s %s",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            current_port->name,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            current_port->requested,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            current_port->state,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                            current_port->variants) < 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                Tcl_Obj** objs;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if (list_string_to_obj(&objs, portstrs, port_count, &error)){
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    Tcl_Obj* result = Tcl_NewListObj(port_count, objs);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    Tcl_SetObjResult(interp, result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    free(objs);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    for (i=0; i < port_count; i++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        free(ports[i]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    free(ports);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    return TCL_OK;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    return registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return registry_failed(interp, &error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    char* name;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int (*function)(Tcl_Interp* interp, reg_snapshot* snapshot, int objc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            Tcl_Obj* CONST objv[]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} snapshot_obj_cmd_type;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static snapshot_obj_cmd_type snapshot_cmds[] = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    /* keys */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { "id", snapshot_obj_id },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { "note", snapshot_obj_prop },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { "created_at", snapshot_obj_prop },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    /* ports */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { "ports", snapshot_obj_ports },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    { NULL, NULL }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* ${snapshot} cmd ?arg ...? */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* This function implements the command that will be called when a snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * created by `registry::snapshot` is used as a procedure. Since all data is kept
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * in a temporary sqlite3 database that is created for the current interpreter,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * none of the sqlite3 functions used have any error checking. That should be a
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * safe assumption, since nothing outside of registry:: should ever have the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * chance to touch it.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int snapshot_obj_cmd(ClientData clientData, Tcl_Interp* interp, int objc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_Obj* CONST objv[]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int cmd_index;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (objc < 2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (Tcl_GetIndexFromObjStruct(interp, objv[1], snapshot_cmds,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                sizeof(snapshot_obj_cmd_type), "cmd", 0, &cmd_index) == TCL_OK) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        snapshot_obj_cmd_type* cmd = &snapshot_cmds[cmd_index];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return cmd->function(interp, (reg_snapshot*)clientData, objc, objv);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/registry.h b/src/registry2.0/snapshotobj.h
</span>similarity index 76%
copy from src/registry2.0/registry.h
copy to src/registry2.0/snapshotobj.h
<span style='display:block; white-space:pre;color:#808080;'>index b5e7bd2..1946ebe 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/registry2.0/registry.h
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/snapshotobj.h
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -1,7 +1,8 @@
</span> /*
<span style='display:block; white-space:pre;background:#ffe0e0;'>- * registry.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * snapshotobj.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * vim:tw=80:expandtab
</span>  *
<span style='display:block; white-space:pre;background:#ffe0e0;'>- * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (c) 2017 The MacPorts Project
</span>  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -24,8 +25,9 @@
</span>  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#ifndef _REGISTRY_H
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-#define _REGISTRY_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#ifndef _SNAPSHOT_OBJ_CMD_H
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define _SNAPSHOT_OBJ_CMD_H
</span> 
 #if HAVE_CONFIG_H
 #include <config.h>
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -33,15 +35,10 @@
</span> 
 #include <tcl.h>
 #include <sqlite3.h>
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#include <cregistry/portgroup.h>
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-#include <cregistry/entry.h>
</span> 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-typedef struct _entry_list {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    reg_entry* entry;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-    struct _entry_list* next;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-} entry_list;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+extern const char* snapshot_props[];
</span> 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-reg_registry* registry_for(Tcl_Interp* interp, int status);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-int registry_failed(Tcl_Interp* interp, reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int snapshot_obj_cmd(ClientData clientData, Tcl_Interp* interp, int objc,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        Tcl_Obj* CONST objv[]);
</span> 
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#endif /* _REGISTRY_H */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif /* _SNAPSHOT_OBJ_CMD_H */
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/util.c b/src/registry2.0/util.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 3edd752..09210a4 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/registry2.0/util.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/util.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -36,6 +36,7 @@
</span> 
 #include "util.h"
 #include "entryobj.h"
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "snapshotobj.h"
</span> #include "fileobj.h"
 #include "portgroupobj.h"
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -191,6 +192,29 @@ int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
</span> }
 
 /**
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Sets a given name to be an snapshot object.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] interp     Tcl interpreter to create the snapshot within
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] name       name to associate the given snapshot with
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [in] snapshot   snapshot to associate with the given name
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @param [out] errPtr    description of error if it couldn't be set
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @return                true if success; false if failure
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * @see set_object
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int set_snapshot(Tcl_Interp* interp, char* name, reg_snapshot* snapshot,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (set_object(interp, name, snapshot, "snapshot", snapshot_obj_cmd, NULL,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                errPtr)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        snapshot->proc = strdup(name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (!snapshot->proc) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</span>  * Sets a given name to be a file object.
  *
  * @param [in] interp  Tcl interpreter to create the file within
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -303,6 +327,47 @@ int entry_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_entry* entry,
</span>     return 1;
 }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+int snapshot_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_snapshot* snapshot,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int* lower_bound, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (snapshot->proc == NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        char* name = unique_name(interp, "::registry::snapshot", lower_bound);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (!name) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (!set_snapshot(interp, name, snapshot, errPtr)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            free(name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        free(name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    *obj = Tcl_NewStringObj(snapshot->proc, -1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// int snapshot_port_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, port* port,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//         int* lower_bound, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//     if (port->proc == NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//         char* name = unique_name(interp, "::registry::snapshot_port", lower_bound);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//         if (!name) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//             return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//         }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//         if (!set_snapshot_port(interp, name, snapshot, errPtr)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//             free(name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//             return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//         }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//         free(name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//     }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//     *obj = Tcl_NewStringObj(snapshot->proc, -1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//     return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// int list_snapshot_ports_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, port** snapshot_ports,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//     int snapshot_port_count, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//     int lower_bound = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//     return recast(interp, (cast_function*)snapshot_port_to_obj, &lower_bound, NULL,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+//             (void***)objs, (void**)snapshot_ports, snapshot_port_count, errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+// }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> int file_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_file* file,
         int* lower_bound, reg_error* errPtr) {
     if (file->proc == NULL) {
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -344,6 +409,13 @@ int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
</span>             (void***)objs, (void**)entries, entry_count, errPtr);
 }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+int list_snapshot_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_snapshot** snapshots, int snapshot_count, reg_error* errPtr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    int lower_bound = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return recast(interp, (cast_function*)snapshot_to_obj, &lower_bound, NULL,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            (void***)objs, (void**)snapshots, snapshot_count, errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> int list_file_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
         reg_file** files, int file_count, reg_error* errPtr) {
     int lower_bound = 0;
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/registry2.0/util.h b/src/registry2.0/util.h
</span><span style='display:block; white-space:pre;color:#808080;'>index c990591..e5f6e60 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/registry2.0/util.h
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/registry2.0/util.h
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -37,6 +37,7 @@
</span> 
 #include <cregistry/registry.h>
 #include <cregistry/portgroup.h>
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <cregistry/snapshot.h>
</span> #include <cregistry/entry.h>
 #include <cregistry/file.h>
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -58,6 +59,8 @@ int set_object(Tcl_Interp* interp, char* name, void* value, char* type,
</span>         Tcl_ObjCmdProc* proc, Tcl_CmdDeleteProc* deleteProc, reg_error* errPtr);
 int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
         reg_error* errPtr);
<span style='display:block; white-space:pre;background:#e0ffe0;'>+int set_snapshot(Tcl_Interp* interp, char* name, reg_snapshot* snapshot,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_error* errPtr);
</span> 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,
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -75,6 +78,10 @@ int entry_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_entry* entry,
</span>         int* lower_bound, reg_error* errPtr);
 int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
         reg_entry** entries, int entry_count, reg_error* errPtr);
<span style='display:block; white-space:pre;background:#e0ffe0;'>+int snapshot_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_snapshot* snapshot,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        int* lower_bound, reg_error* errPtr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int list_snapshot_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        reg_snapshot** snapshots, int snapshot_count, reg_error* errPtr);
</span> int file_to_obj(Tcl_Interp* interp, Tcl_Obj** ibj, reg_file* file,
         int* lower_bound, reg_error* errPtr);
 int list_file_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
</pre><pre style='margin:0'>

</pre>