<pre style='margin:0'>
Joshua Root (jmroot) pushed a commit to branch master
in repository macports-base.
</pre>
<p><a href="https://github.com/macports/macports-base/commit/6e2f4538233e9b36173381416b64e0ec94d4fe93">https://github.com/macports/macports-base/commit/6e2f4538233e9b36173381416b64e0ec94d4fe93</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 6e2f4538233e9b36173381416b64e0ec94d4fe93
</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 | 70 +++-
src/macports1.0/Makefile.in | 3 +-
src/macports1.0/macports.tcl | 86 ++++-
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 2c21ea6c6..1a8de6fd3 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 bf9409f7d..de78fbd87 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 0c37aba4a..2bcd3bad4 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 d50cbf951..e53e02cc3 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 000000000..1715d95e2
</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 000000000..d1692659d
</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 75851566b..4d8d07783 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;'>@@ -141,7 +141,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.211')",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "INSERT INTO registry.metadata (key, value) VALUES ('version', '1.212')",
</span> "INSERT INTO registry.metadata (key, value) VALUES ('created', strftime('%s', 'now'))",
/* ports table */
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -203,6 +203,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;'>@@ -699,14 +720,12 @@ 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:#ffe0e0;'>-
</span> "COMMIT",
NULL
};
sqlite3_finalize(stmt);
stmt = NULL;
<span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span> if (!do_queries(db, version_1_204_queries, errPtr)) {
rollback_db(db);
return 0;
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -859,6 +878,49 @@ int update_db(sqlite3* db, reg_error* errPtr) {
</span> continue;
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (sql_version(NULL, -1, version, -1, "1.212") < 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_212_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.212' WHERE key = 'version'",
</span><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;'>+
</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;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (!do_queries(db, version_1_212_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>
/* add new versions here, but remember to:
* - finalize the version query statement and set stmt to NULL
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -869,7 +931,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.211") > 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (sql_version(NULL, -1, version, -1, "1.212") > 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 4af97003e..133136eae 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 3ca660177..ebc45f69f 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;'>@@ -1056,9 +1059,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_platform eq "darwin" && $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;'>@@ -5271,7 +5275,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;'>@@ -5308,6 +5311,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;'>@@ -5386,7 +5465,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 000000000..5dfbce1b1
</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_platform eq "darwin" && $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 000000000..d9561ef9f
</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 9b0d416bd..e148e0dcb 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;'>@@ -169,7 +169,10 @@ proc selfupdate::main {{options {}} {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 {[dict exists $options ports_selfupdate_migrate] && [dict get $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 {[dict exists $options ports_dryrun] && [dict get $options ports_dryrun]} {
ui_msg "$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 000000000..04d4707c2
</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 e3eda2659..7178037aa 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 e8efafe47..5e3f95366 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;'>@@ -2498,6 +2498,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;'>@@ -4039,6 +4065,10 @@ set action_array [dict create \
</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;'>@@ -4115,7 +4145,7 @@ set cmd_opts_array [dict create {*}{
</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;'>@@ -4133,6 +4163,9 @@ set cmd_opts_array [dict create {*}{
</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;'>@@ -5439,7 +5472,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 933e9c3d9..83716762f 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 bb9d1bed4..2df0507f0 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;'>@@ -403,6 +405,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 b5e7bd27b..185390fe7 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 000000000..da727b691
</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 b5e7bd27b..e262065bc 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 000000000..63fb525f3
</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 b5e7bd27b..1946ebe1b 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 355cbcc35..1a28c2c55 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;'>@@ -38,6 +38,7 @@
</span> #include "entry.h"
#include "entryobj.h"
#include "file.h"
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include "snapshotobj.h"
</span> #include "fileobj.h"
#include "portgroup.h"
#include "portgroupobj.h"
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -196,6 +197,29 @@ int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
</span> return 0;
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+/**
</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> /**
* Sets a given name to be a file object.
*
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -310,6 +334,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,
void* param UNUSED, reg_error* errPtr) {
static unsigned int lower_bound = 0;
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -352,6 +417,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) {
return recast(interp, (cast_function*)file_to_obj, NULL, NULL,
<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 b9c0ab694..b45645409 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> void* param UNUSED, 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,
void* param UNUSED, reg_error* errPtr);
int list_file_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
</pre><pre style='margin:0'>
</pre>