[28029] trunk/base
source_changes at macosforge.org
source_changes at macosforge.org
Sat Aug 18 09:00:00 PDT 2007
Revision: 28029
http://trac.macosforge.org/projects/macports/changeset/28029
Author: sfiera at macports.org
Date: 2007-08-18 08:59:59 -0700 (Sat, 18 Aug 2007)
Log Message:
-----------
Incremental update on registry2
Also added ${prefix}/var/macports/registry to prefix.mtree for the future
Modified Paths:
--------------
trunk/base/doc/prefix.mtree.in
trunk/base/src/cregistry/entry.c
trunk/base/src/cregistry/entry.h
trunk/base/src/cregistry/registry.c
trunk/base/src/cregistry/registry.h
trunk/base/src/cregistry/sql.c
trunk/base/src/registry2.0/Makefile
trunk/base/src/registry2.0/entry.c
trunk/base/src/registry2.0/entryobj.c
trunk/base/src/registry2.0/tests/common.tcl
trunk/base/src/registry2.0/tests/entry.tcl
trunk/base/src/registry2.0/util.c
trunk/base/src/registry2.0/util.h
Added Paths:
-----------
trunk/base/src/registry2.0/portimage.tcl
trunk/base/src/registry2.0/portuninstall.tcl
trunk/base/src/registry2.0/registry_util.tcl
trunk/base/src/registry2.0/tests/depends.tcl
Modified: trunk/base/doc/prefix.mtree.in
===================================================================
--- trunk/base/doc/prefix.mtree.in 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/doc/prefix.mtree.in 2007-08-18 15:59:59 UTC (rev 28029)
@@ -242,6 +242,8 @@
distfiles
..
receipts
+ ..
+ registry
..
..
..
Modified: trunk/base/src/cregistry/entry.c
===================================================================
--- trunk/base/src/cregistry/entry.c 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/cregistry/entry.c 2007-08-18 15:59:59 UTC (rev 28029)
@@ -48,11 +48,23 @@
*
* TODO: process the 'state' keyword in a more efficient way. I still believe
* there could be benefits to be reaped in the future by allowing
- * arbitrary values but at the same time it will always have very discrete
- * values. These could be more efficiently dealt with as integers.
+ * arbitrary values; but at the same time the field will (or should)
+ * always have very discrete values. These could be more efficiently dealt
+ * with as integers.
*
* TODO: move the utility functions to util.h or something. Not important until
* there are more types in the registry than entry, though.
+ *
+ * TODO: considering a "weak" flag in registry.files. The meaning of this would
+ * be "I wish for my version of this file to be activated when I am, but
+ * not to be deactivated when I am; nor should other ports be prevented
+ * from overwriting this file." This would be useful for files like e.g.
+ * perllocal.pod (or whatever it's called; the one that causes problems).
+ *
+ * TODO: expose a file's mtime attribute. This should be used during port
+ * deactivation to determine if any files have local modifications. If so,
+ * they should be moved aside instead of being removed when the port is
+ * deactivated/uninstalled.
*/
/**
@@ -236,8 +248,8 @@
char* revision, char* variants, char* epoch, reg_error* errPtr) {
sqlite3_stmt* stmt;
reg_entry* entry = NULL;
- char* query = "SELECT registry.ports.id FROM registry.ports "
- "WHERE name=? AND version=? AND revision=? AND variants=? AND epoch=?";
+ char* query = "SELECT id FROM registry.ports WHERE name=? AND version=? "
+ "AND revision=? AND variants=? AND epoch=?";
if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
&& (sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC)
== SQLITE_OK)
@@ -287,17 +299,53 @@
int reg_entry_delete(reg_entry* entry, reg_error* errPtr) {
reg_registry* reg = entry->reg;
int result = 0;
- sqlite3_stmt* stmt;
- char* query = "DELETE FROM registry.ports WHERE id=?";
- if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
- && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
+ sqlite3_stmt* ports;
+ sqlite3_stmt* files;
+ sqlite3_stmt* dependencies;
+ char* ports_query = "DELETE FROM registry.ports WHERE id=?";
+ char* files_query = "DELETE FROM registry.files WHERE id=?";
+ char* dependencies_query = "DELETE FROM registry.dependencies WHERE id=?";
+ if ((sqlite3_prepare(reg->db, ports_query, -1, &ports, NULL) == SQLITE_OK)
+ && (sqlite3_bind_int64(ports, 1, entry->id) == SQLITE_OK)
+ && (sqlite3_prepare(reg->db, files_query, -1, &files, NULL)
+ == SQLITE_OK)
+ && (sqlite3_bind_int64(files, 1, entry->id) == SQLITE_OK)
+ && (sqlite3_prepare(reg->db, dependencies_query, -1, &dependencies,
+ NULL) == SQLITE_OK)
+ && (sqlite3_bind_int64(dependencies, 1, entry->id) == SQLITE_OK)) {
int r;
do {
- r = sqlite3_step(stmt);
+ r = sqlite3_step(ports);
switch (r) {
case SQLITE_DONE:
if (sqlite3_changes(reg->db) > 0) {
- result = 1;
+ do {
+ r = sqlite3_step(files);
+ switch (r) {
+ case SQLITE_DONE:
+ do {
+ r = sqlite3_step(dependencies);
+ switch (r) {
+ case SQLITE_DONE:
+ result = 1;
+ break;
+ case SQLITE_BUSY:
+ break;
+ case SQLITE_ERROR:
+ reg_sqlite_error(reg->db,
+ errPtr, NULL);
+ break;
+ }
+ } while (r == SQLITE_BUSY);
+ break;
+ case SQLITE_BUSY:
+ break;
+ case SQLITE_ERROR:
+ reg_sqlite_error(reg->db, errPtr, NULL);
+ break;
+ }
+ } while (r == SQLITE_BUSY);
+ break;
} else {
errPtr->code = REG_INVALID;
errPtr->description = "an invalid entry was passed";
@@ -307,14 +355,16 @@
case SQLITE_BUSY:
break;
case SQLITE_ERROR:
- reg_sqlite_error(reg->db, errPtr, query);
+ reg_sqlite_error(reg->db, errPtr, NULL);
break;
}
} while (r == SQLITE_BUSY);
} else {
- reg_sqlite_error(reg->db, errPtr, query);
+ reg_sqlite_error(reg->db, errPtr, NULL);
}
- sqlite3_finalize(stmt);
+ sqlite3_finalize(ports);
+ sqlite3_finalize(files);
+ sqlite3_finalize(dependencies);
return result;
}
@@ -432,8 +482,8 @@
int i;
char* kwd = " WHERE ";
char* query;
- int query_len = 44;
- int query_space = 44;
+ int query_len = 29;
+ int query_space = 29;
int result;
/* get the strategy */
char* op = reg_strategy_op(strategy, errPtr);
@@ -441,7 +491,7 @@
return -1;
}
/* build the query */
- query = strdup("SELECT registry.ports.id FROM registry.ports");
+ query = strdup("SELECT id FROM registry.ports");
for (i=0; i<key_count; i++) {
char* cond = sqlite3_mprintf(op, keys[i], vals[i]);
reg_strcat(&query, &query_len, &query_space, kwd);
@@ -459,8 +509,11 @@
* Finds ports which are installed as an image, and/or those which are active
* in the filesystem. When the install mode is 'direct', this will be equivalent
* to `reg_entry_installed`.
- * @todo add more arguments (epoch, revision, variants), maybe
*
+ * Note that the name is a bit of a misnomer, since you can install a port with
+ * installtype direct and it will still be in this list. It's really "imaged or
+ * installed" but that's usually redundant and too long to type.
+ *
* @param [in] reg registry object as created by `registry_open`
* @param [in] name specific port to find (NULL for any)
* @param [in] version specific version to find (NULL for any)
@@ -468,23 +521,37 @@
* @param [out] errPtr description of error encountered, if any
* @return the number of entries if success; false if failure
*/
-int reg_entry_imaged(reg_registry* reg, char* name, char* version,
- reg_entry*** entries, reg_error* errPtr) {
- char* format;
+int reg_entry_imaged(reg_registry* reg, const char* name, const char* version,
+ const char* revision, const char* variants, reg_entry*** entries,
+ reg_error* errPtr) {
char* query;
int result;
- char* select = "SELECT registry.ports.id FROM registry.ports";
- if (name == NULL) {
- format = "%s WHERE (state='imaged' OR state='installed')";
- } else if (version == NULL) {
- format = "%s WHERE (state='imaged' OR state='installed') AND name='%q'";
- } else {
- format = "%s WHERE (state='imaged' OR state='installed') AND name='%q' "
- "AND version='%q'";
+ char* empty = "";
+ char* name_clause = empty;
+ char* version_clause = empty;
+ char* revision_clause = empty;
+ char* variants_clause = empty;
+ if (name != NULL) {
+ name_clause = sqlite3_mprintf(" AND name='%q'", name);
}
- query = sqlite3_mprintf(format, select, name, version);
+ if (version != NULL) {
+ version_clause = sqlite3_mprintf(" AND version='%q'", version);
+ }
+ if (revision != NULL) {
+ revision_clause = sqlite3_mprintf(" AND revision='%q'", revision);
+ }
+ if (variants != NULL) {
+ variants_clause = sqlite3_mprintf(" AND variants='%q'", variants);
+ }
+ query = sqlite3_mprintf("SELECT id FROM ports WHERE (state='imaged' OR "
+ "state='installed')%s%s%s%s", name_clause,
+ version_clause, revision_clause, variants_clause);
result = reg_all_entries(reg, query, -1, entries, errPtr);
sqlite3_free(query);
+ if (name_clause != empty) sqlite3_free(name_clause);
+ if (version_clause != empty) sqlite3_free(version_clause);
+ if (revision_clause != empty) sqlite3_free(revision_clause);
+ if (variants_clause != empty) sqlite3_free(variants_clause);
return result;
}
@@ -504,7 +571,7 @@
char* format;
char* query;
int result;
- char* select = "SELECT registry.ports.id FROM registry.ports";
+ char* select = "SELECT id FROM registry.ports";
if (name == NULL) {
format = "%s WHERE state='installed'";
} else {
@@ -530,9 +597,7 @@
reg_error* errPtr) {
int result = 0;
sqlite3_stmt* stmt;
- char* query = "SELECT registry.files.id FROM registry.files INNER JOIN "
- "registry.ports USING(id) WHERE registry.ports.state = 'installed' "
- "AND registry.files.path=?";
+ char* query = "SELECT id FROM registry.files WHERE actual_path=? AND active";
if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
&& (sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC)
== SQLITE_OK)) {
@@ -577,9 +642,7 @@
sqlite_int64 reg_entry_owner_id(reg_registry* reg, char* path) {
sqlite3_stmt* stmt;
sqlite_int64 result = 0;
- char* query = "SELECT registry.files.id FROM registry.files INNER JOIN "
- "registry.ports USING(id) WHERE registry.ports.state = 'installed' "
- "AND registry.files.path=?";
+ char* query = "SELECT id FROM registry.files WHERE actual_path=? AND active";
if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
&& (sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC)
== SQLITE_OK)) {
@@ -706,12 +769,14 @@
int reg_entry_map(reg_entry* entry, char** files, int file_count,
reg_error* errPtr) {
reg_registry* reg = entry->reg;
+ int result = 1;
sqlite3_stmt* stmt;
- char* insert = "INSERT INTO registry.files (id, path) VALUES (?, ?)";
+ char* insert = "INSERT INTO registry.files (id, path, mtime, active) "
+ "VALUES (?, ?, 0, 0)";
if ((sqlite3_prepare(reg->db, insert, -1, &stmt, NULL) == SQLITE_OK)
&& (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
int i;
- for (i=0; i<file_count; i++) {
+ for (i=0; i<file_count && result; i++) {
if (sqlite3_bind_text(stmt, 2, files[i], -1, SQLITE_STATIC)
== SQLITE_OK) {
int r;
@@ -725,23 +790,21 @@
break;
default:
reg_sqlite_error(reg->db, errPtr, insert);
- sqlite3_finalize(stmt);
- return i;
+ result = 0;
+ break;
}
} while (r == SQLITE_BUSY);
} else {
reg_sqlite_error(reg->db, errPtr, insert);
- sqlite3_finalize(stmt);
- return i;
+ result = 0;
}
}
- sqlite3_finalize(stmt);
- return file_count;
} else {
reg_sqlite_error(reg->db, errPtr, insert);
- sqlite3_finalize(stmt);
- return 0;
+ result = 0;
}
+ sqlite3_finalize(stmt);
+ return result;
}
/**
@@ -757,13 +820,14 @@
int reg_entry_unmap(reg_entry* entry, char** files, int file_count,
reg_error* errPtr) {
reg_registry* reg = entry->reg;
+ int result = 1;
sqlite3_stmt* stmt;
- char* query = "DELETE FROM registry.files WHERE id=? AND path=?";
+ char* query = "DELETE FROM registry.files WHERE path=? AND id=?";
if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
- && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
+ && (sqlite3_bind_int64(stmt, 2, entry->id) == SQLITE_OK)) {
int i;
- for (i=0; i<file_count; i++) {
- if (sqlite3_bind_text(stmt, 2, files[i], -1, SQLITE_STATIC)
+ for (i=0; i<file_count && result; i++) {
+ if (sqlite3_bind_text(stmt, 1, files[i], -1, SQLITE_STATIC)
== SQLITE_OK) {
int r;
do {
@@ -771,41 +835,97 @@
switch (r) {
case SQLITE_DONE:
if (sqlite3_changes(reg->db) == 0) {
- errPtr->code = REG_INVALID;
- errPtr->description = "this entry does not own "
- "the given file";
- errPtr->free = NULL;
- sqlite3_finalize(stmt);
- return i;
+ reg_throw(errPtr, REG_INVALID, "this entry "
+ "does not own the given file");
+ result = 0;
+ } else {
+ sqlite3_reset(stmt);
}
- sqlite3_reset(stmt);
break;
case SQLITE_BUSY:
break;
default:
reg_sqlite_error(reg->db, errPtr, query);
- sqlite3_finalize(stmt);
- return i;
+ result = 0;
+ break;
}
} while (r == SQLITE_BUSY);
} else {
reg_sqlite_error(reg->db, errPtr, query);
- sqlite3_finalize(stmt);
- return i;
+ result = 0;
}
}
+ } else {
+ reg_sqlite_error(reg->db, errPtr, query);
+ result = 0;
+ }
+ sqlite3_finalize(stmt);
+ return result;
+}
+
+/**
+ * Gets a list of files provided by the given port. These files are in the port
+ * image and do not necessarily correspond to active files on the filesystem.
+ *
+ * TODO: check that the port's installtype is image
+ *
+ * @param [in] entry entry to get the list for
+ * @param [out] files a list of files provided by the port
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return the number of files if success; negative if failure
+ */
+int reg_entry_imagefiles(reg_entry* entry, char*** files, reg_error* errPtr) {
+ reg_registry* reg = entry->reg;
+ sqlite3_stmt* stmt;
+ char* query = "SELECT path FROM registry.files WHERE id=? ORDER BY path";
+ if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
+ && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
+ char** result = malloc(10*sizeof(char*));
+ int result_count = 0;
+ int result_space = 10;
+ int r;
+ do {
+ char* element;
+ r = sqlite3_step(stmt);
+ switch (r) {
+ case SQLITE_ROW:
+ element = strdup((const char*)sqlite3_column_text(stmt, 0));
+ reg_listcat((void***)&result, &result_count, &result_space,
+ element);
+ break;
+ case SQLITE_DONE:
+ case SQLITE_BUSY:
+ break;
+ default:
+ reg_sqlite_error(reg->db, errPtr, query);
+ break;
+ }
+ } while (r == SQLITE_ROW || r == SQLITE_BUSY);
sqlite3_finalize(stmt);
- return file_count;
+ if (r == SQLITE_DONE) {
+ *files = result;
+ return result_count;
+ } else {
+ int i;
+ for (i=0; i<result_count; i++) {
+ free(result[i]);
+ }
+ free(result);
+ return -1;
+ }
} else {
reg_sqlite_error(reg->db, errPtr, query);
sqlite3_finalize(stmt);
- return 0;
+ return -1;
}
}
/**
- * Gets a list of files owned by the given port.
+ * Gets a list of files owned by the given port. These files are active in the
+ * filesystem and could be different from the port's imagefiles.
*
+ * TODO: check that the port is active
+ *
* @param [in] entry entry to get the list for
* @param [out] files a list of files owned by the port
* @param [out] errPtr on error, a description of the error that occurred
@@ -814,7 +934,8 @@
int reg_entry_files(reg_entry* entry, char*** files, reg_error* errPtr) {
reg_registry* reg = entry->reg;
sqlite3_stmt* stmt;
- char* query = "SELECT path FROM registry.files WHERE id=? ORDER BY path";
+ char* query = "SELECT actual_path FROM registry.files WHERE id=? "
+ "AND active ORDER BY actual_path";
if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
&& (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)) {
char** result = malloc(10*sizeof(char*));
@@ -858,6 +979,250 @@
}
/**
+ * Sets an entry's files as being active in the filesystem. This entry will be
+ * subsequently returned by `reg_entry_owner` on those files' path. If all files
+ * are being activated as the names they are in the registry, then `as_files`
+ * may be NULL. If they are being activated to different paths than the original
+ * files, then `as_files` should be a list of the same length as `files`.
+ *
+ * @param [in] entry entry to assign the file to
+ * @param [in] files a list of files to activate
+ * @param [in] as_files NULL, or a list of paths the files are activated as
+ * @param [in] file_count number of files to activate
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return true if success; false if failure
+ */
+int reg_entry_activate(reg_entry* entry, char** files, char** as_files,
+ int file_count, reg_error* errPtr) {
+ reg_registry* reg = entry->reg;
+ int result = 1;
+ int i;
+ sqlite3_stmt* select;
+ sqlite3_stmt* update;
+ char* select_query = "SELECT id FROM registry.files WHERE actual_path=? "
+ "AND active";
+ char* update_query = "UPDATE registry.files SET actual_path=?, active=1 "
+ "WHERE path=? AND id=?";
+
+ /* if as_files wasn't specified, activate as the original files */
+ if (as_files == NULL) {
+ as_files = files;
+ }
+
+ if (sqlite3_prepare(reg->db, select_query, -1, &select, NULL) == SQLITE_OK){
+ if ((sqlite3_prepare(reg->db, update_query, -1, &update, NULL)
+ == SQLITE_OK)
+ && (sqlite3_bind_int64(update, 3, entry->id) == SQLITE_OK)) {
+ for (i=0; i<file_count && result; i++) {
+ if ((sqlite3_bind_text(select, 1, files[i], -1, SQLITE_STATIC)
+ == SQLITE_OK)
+ && (sqlite3_bind_text(update, 1, as_files[i], -1,
+ SQLITE_STATIC) == SQLITE_OK)
+ && (sqlite3_bind_text(update, 2, files[i], -1,
+ SQLITE_STATIC) == SQLITE_OK)) {
+ int r;
+ do {
+ r = sqlite3_step(select);
+ switch (r) {
+ case SQLITE_ROW:
+ reg_throw(errPtr, REG_ALREADY_ACTIVE, "%s is "
+ "being used by another port", files[i]);
+ result = 0;
+ break;
+ case SQLITE_DONE:
+ do {
+ r = sqlite3_step(update);
+ switch (r) {
+ case SQLITE_DONE:
+ if (sqlite3_changes(reg->db) == 0) {
+ reg_throw(errPtr, REG_INVALID,
+ "%s is not provided by "
+ "this port", files[i]);
+ result = 0;
+ } else {
+ sqlite3_reset(select);
+ sqlite3_reset(update);
+ }
+ break;
+ case SQLITE_BUSY:
+ break;
+ case SQLITE_ERROR:
+ reg_sqlite_error(reg->db, errPtr,
+ update_query);
+ result = 0;
+ break;
+ }
+ } while (r == SQLITE_BUSY);
+ break;
+ case SQLITE_BUSY:
+ break;
+ case SQLITE_ERROR:
+ reg_sqlite_error(reg->db, errPtr, select_query);
+ result = 0;
+ break;
+ }
+ } while (r == SQLITE_BUSY);
+ } else {
+ reg_sqlite_error(reg->db, errPtr, NULL);
+ result = 0;
+ }
+ }
+ } else {
+ reg_sqlite_error(reg->db, errPtr, update_query);
+ result = 0;
+ }
+ sqlite3_finalize(update);
+ } else {
+ reg_sqlite_error(reg->db, errPtr, select_query);
+ result = 0;
+ }
+ sqlite3_finalize(select);
+ return result;
+}
+
+/**
+ * Deactivates files owned by a given entry. That entry's version of all files
+ * must currently be active.
+ *
+ * @param [in] entry current owner of the files
+ * @param [in] files a list of files to deactivate
+ * @param [in] file_count number of files to deactivate
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return true if success; false if failure
+ */
+int reg_entry_deactivate(reg_entry* entry, char** files, int file_count,
+ reg_error* errPtr) {
+ reg_registry* reg = entry->reg;
+ int result = 1;
+ int i;
+ sqlite3_stmt* stmt;
+ char* query = "UPDATE registry.files SET active=0 WHERE actual_path=? AND id=?";
+ if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
+ && (sqlite3_bind_int64(stmt, 2, entry->id) == SQLITE_OK)) {
+ for (i=0; i<file_count && result; i++) {
+ if (sqlite3_bind_text(stmt, 1, files[i], -1, SQLITE_STATIC)
+ == SQLITE_OK) {
+ int r;
+ do {
+ r = sqlite3_step(stmt);
+ switch (r) {
+ case SQLITE_DONE:
+ if (sqlite3_changes(reg->db) == 0) {
+ reg_throw(errPtr, REG_INVALID, "this entry "
+ "does not own the given file");
+ result = 0;
+ } else {
+ sqlite3_reset(stmt);
+ }
+ break;
+ case SQLITE_BUSY:
+ break;
+ default:
+ reg_sqlite_error(reg->db, errPtr, query);
+ result = 0;
+ break;
+ }
+ } while (r == SQLITE_BUSY);
+ } else {
+ reg_sqlite_error(reg->db, errPtr, query);
+ result = 0;
+ }
+ }
+ } else {
+ reg_sqlite_error(reg->db, errPtr, query);
+ result = 0;
+ }
+ sqlite3_finalize(stmt);
+ return result;
+}
+
+/**
+ * Gets a list of ports that depend on this one. Uninstalling the given port
+ * could potentially break any port listed in its dependents, and could not
+ * break any other.
+ *
+ * N.B.: an inactive port has no dependents, since it can be safely removed.
+ *
+ * @param [in] entry a port
+ * @param [out] dependents a list of ports dependent on the given port
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return true if success; false if failure
+ */
+int reg_entry_dependents(reg_entry* entry, reg_entry*** dependents,
+ reg_error* errPtr) {
+ reg_registry* reg = entry->reg;
+ char* query = sqlite3_mprintf("SELECT dependent.id FROM ports port "
+ "INNER JOIN dependencies USING(name) INNER JOIN ports dependent "
+ "USING(id) WHERE port.id=%lld AND port.state = 'installed' "
+ "AND dependent.state = 'installed'",
+ entry->id);
+ int result = reg_all_entries(reg, query, -1, dependents, errPtr);
+ sqlite3_free(query);
+ return result;
+}
+
+/**
+ * Gets a list of ports that this one depends on. This only returns ports which
+ * have state "installed" and should really only be called upon ports which have
+ * state "installed" themselves. Uninstalling any port in this list could break
+ * the given port, but uninstalling any other could not break it.
+ *
+ * @param [in] entry a port
+ * @param [out] dependencies a list of ports the given port depends on
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return number of deps if success; negative if failure
+ */
+int reg_entry_dependencies(reg_entry* entry, reg_entry*** dependencies,
+ reg_error* errPtr) {
+ reg_registry* reg = entry->reg;
+ char* query = sqlite3_mprintf("SELECT ports.id FROM registry.dependencies "
+ "INNER JOIN registry.ports USING(name) WHERE dependencies.id=%lld AND "
+ "ports.state = 'installed'", entry->id);
+ int result = reg_all_entries(reg, query, -1, dependencies, errPtr);
+ sqlite3_free(query);
+ return result;
+}
+
+/**
+ * Sets the given port to depend on the named port. This is a weak link; it
+ * refers to a name and not an actual port.
+ *
+ * @param [in] entry a port
+ * @param [in] name the name of a port the given port depends on
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return true if success; false if failure
+ */
+int reg_entry_depends(reg_entry* entry, char* name, reg_error* errPtr) {
+ reg_registry* reg = entry->reg;
+ int result = 0;
+ sqlite3_stmt* stmt;
+ char* query = "INSERT INTO registry.dependencies (id, name) VALUES (?,?)";
+ if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
+ && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)
+ && (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC)
+ == SQLITE_OK)) {
+ int r;
+ do {
+ r = sqlite3_step(stmt);
+ switch (r) {
+ case SQLITE_DONE:
+ result = 1;
+ break;
+ case SQLITE_BUSY:
+ break;
+ default:
+ reg_sqlite_error(reg->db, errPtr, query);
+ break;
+ }
+ } while (r == SQLITE_BUSY);
+ } else {
+ reg_sqlite_error(reg->db, errPtr, query);
+ }
+ sqlite3_finalize(stmt);
+ return result;
+}
+
+/**
* Fetches a list of all open entries.
*
* @param [in] reg registry to fetch entries from
Modified: trunk/base/src/cregistry/entry.h
===================================================================
--- trunk/base/src/cregistry/entry.h 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/cregistry/entry.h 2007-08-18 15:59:59 UTC (rev 28029)
@@ -60,8 +60,9 @@
int reg_entry_search(reg_registry* reg, char** keys, char** vals, int key_count,
int strategy, reg_entry*** entries, reg_error* errPtr);
-int reg_entry_imaged(reg_registry* reg, char* name, char* version,
- reg_entry*** entries, reg_error* errPtr);
+int reg_entry_imaged(reg_registry* reg, const char* name, const char* version,
+ const char* revision, const char* variants, reg_entry*** entries,
+ reg_error* errPtr);
int reg_entry_installed(reg_registry* reg, char* name, reg_entry*** entries,
reg_error* errPtr);
@@ -79,7 +80,19 @@
reg_error* errPtr);
int reg_entry_files(reg_entry* entry, char*** files, reg_error* errPtr);
+int reg_entry_imagefiles(reg_entry* entry, char*** files, reg_error* errPtr);
+int reg_entry_activate(reg_entry* entry, char** files, char** as_files,
+ int file_count, reg_error* errPtr);
+int reg_entry_deactivate(reg_entry* entry, char** files, int file_count,
+ reg_error* errPtr);
+
+int reg_entry_dependents(reg_entry* entry, reg_entry*** dependents,
+ reg_error* errPtr);
+int reg_entry_dependencies(reg_entry* entry, reg_entry*** dependencies,
+ reg_error* errPtr);
+int reg_entry_depends(reg_entry* entry, char* name, reg_error* errPtr);
+
int reg_all_open_entries(reg_registry* reg, reg_entry*** entries);
#endif /* _CENTRY_H */
Modified: trunk/base/src/cregistry/registry.c
===================================================================
--- trunk/base/src/cregistry/registry.c 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/cregistry/registry.c 2007-08-18 15:59:59 UTC (rev 28029)
@@ -34,6 +34,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
+#include <stdarg.h>
#include <sqlite3.h>
#include <sys/stat.h>
#include <errno.h>
@@ -42,10 +43,6 @@
#include <cregistry/sql.h>
/*
- * TODO: create a better system for throwing errors. It'd be really awesome to
- * have e.g. `reg_throw_error(code, fmt, ...)` but I don't feel like
- * messing with varargs right now.
- *
* TODO: maybe all the errPtrs could be made a property of `reg_registry`
* instead, and be destroyed with it? That would make memory-management
* much easier for errors, and there's no need to have more than one error
@@ -86,6 +83,16 @@
}
}
+void reg_throw(reg_error* errPtr, char* code, char* fmt, ...) {
+ va_list list;
+ va_start(list, fmt);
+ errPtr->description = sqlite3_vmprintf(fmt, list);
+ va_end(list);
+
+ errPtr->code = code;
+ errPtr->free = (reg_error_destructor*)sqlite3_free;
+}
+
/**
* Creates a new registry object. To start using a registry, one must first be
* attached with `reg_attach`.
@@ -125,10 +132,8 @@
free(reg);
return 1;
} else {
- errPtr->code = REG_SQLITE_ERROR;
- errPtr->description = sqlite3_mprintf("registry db not closed "
- "correctly (%s)\n", sqlite3_errmsg(reg->db));
- errPtr->free = (reg_error_destructor*)sqlite3_free;
+ reg_throw(errPtr, REG_SQLITE_ERROR, "registry db not closed correctly "
+ "(%s)\n", sqlite3_errmsg(reg->db));
return 0;
}
}
@@ -149,9 +154,8 @@
int can_write = 1; /* can write to this location */
int result = 0;
if (reg->status & reg_attached) {
- errPtr->code = REG_MISUSE;
- errPtr->description = "a database is already attached to this registry";
- errPtr->free = NULL;
+ reg_throw(errPtr, REG_MISUSE, "a database is already attached to this "
+ "registry");
return 0;
}
if (stat(path, &sb) != 0) {
@@ -208,10 +212,8 @@
sqlite3_finalize(stmt);
sqlite3_free(query);
} else {
- errPtr->code = REG_CANNOT_INIT;
- errPtr->description = sqlite3_mprintf("port registry doesn't exist at "
+ reg_throw(errPtr, REG_CANNOT_INIT, "port registry doesn't exist at "
"\"%q\" and couldn't write to this location", path);
- errPtr->free = (reg_error_destructor*)sqlite3_free;
}
return result;
}
@@ -230,9 +232,7 @@
int result = 0;
char* query = "DETACH DATABASE registry";
if (!(reg->status & reg_attached)) {
- errPtr->code = REG_MISUSE;
- errPtr->description = "no database is attached to this registry";
- errPtr->free = NULL;
+ reg_throw(errPtr,REG_MISUSE,"no database is attached to this registry");
return 0;
}
if (sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
@@ -275,9 +275,8 @@
*/
static int reg_start(reg_registry* reg, const char* query, reg_error* errPtr) {
if (reg->status & reg_transacting) {
- errPtr->code = REG_MISUSE;
- errPtr->description = "couldn't start transaction because a "
- "transaction is already open";
+ reg_throw(errPtr, REG_MISUSE, "couldn't start transaction because a "
+ "transaction is already open");
errPtr->free = NULL;
return 0;
} else {
@@ -333,10 +332,8 @@
*/
static int reg_end(reg_registry* reg, const char* query, reg_error* errPtr) {
if (!(reg->status & reg_transacting)) {
- errPtr->code = REG_MISUSE;
- errPtr->description = "couldn't end transaction because no transaction "
- "is open";
- errPtr->free = NULL;
+ reg_throw(errPtr, REG_MISUSE, "couldn't end transaction because no "
+ "transaction is open");
return 0;
} else {
int r;
Modified: trunk/base/src/cregistry/registry.h
===================================================================
--- trunk/base/src/cregistry/registry.h 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/cregistry/registry.h 2007-08-18 15:59:59 UTC (rev 28029)
@@ -41,6 +41,7 @@
#define REG_SQLITE_ERROR "registry::sqlite-error"
#define REG_MISUSE "registry::misuse"
#define REG_CANNOT_INIT "registry::cannot-init"
+#define REG_ALREADY_ACTIVE "registry::already-active"
typedef void reg_error_destructor(const char* description);
@@ -52,6 +53,7 @@
void reg_sqlite_error(sqlite3* db, reg_error* errPtr, char* query);
void reg_error_destruct(reg_error* errPtr);
+void reg_throw(reg_error* errPtr, char* code, char* fmt, ...);
typedef int (cast_function)(void* userdata, void** dst, void* src,
reg_error* errPtr);
Modified: trunk/base/src/cregistry/sql.c
===================================================================
--- trunk/base/src/cregistry/sql.c 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/cregistry/sql.c 2007-08-18 15:59:59 UTC (rev 28029)
@@ -295,9 +295,16 @@
"CREATE INDEX registry.port_state ON ports (state)",
/* file map */
- "CREATE TABLE registry.files (id, path, mtime)",
+ "CREATE TABLE registry.files (id, path, actual_path, active, mtime, "
+ "md5sum, editable)",
"CREATE INDEX registry.file_port ON files (id)",
+ "CREATE INDEX registry.file_path ON files(path)",
+ "CREATE INDEX registry.file_actual ON files(actual_path)",
+ /* dependency map */
+ "CREATE TABLE registry.dependencies (id, name)",
+ "CREATE INDEX registry.dep_name ON dependencies (name)",
+
"COMMIT",
NULL
};
Modified: trunk/base/src/registry2.0/Makefile
===================================================================
--- trunk/base/src/registry2.0/Makefile 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/registry2.0/Makefile 2007-08-18 15:59:59 UTC (rev 28029)
@@ -1,4 +1,5 @@
# $Id$
+SRCS= registry_util.tcl portimage.tcl portuninstall.tcl
OBJS= registry.o util.o \
entry.o entryobj.o
#graph.o graphobj.o
@@ -15,3 +16,12 @@
test:: ${SHLIB_NAME}
${TCLSH} tests/entry.tcl ${SHLIB_NAME}
+ ${TCLSH} tests/depends.tcl ${SHLIB_NAME}
+
+install::
+ $(INSTALL) -d -o ${DSTUSR} -g ${DSTGRP} -m ${DSTMODE} ${INSTALLDIR}
+ $(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 ${SHLIB_NAME} ${INSTALLDIR}
+ $(SILENT) set -x; for file in ${SRCS}; do \
+ $(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 $$file ${INSTALLDIR}/$$file; \
+ done
+ $(SILENT) $(TCLSH) ../pkg_mkindex.tcl ${INSTALLDIR}
Modified: trunk/base/src/registry2.0/entry.c
===================================================================
--- trunk/base/src/registry2.0/entry.c 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/registry2.0/entry.c 2007-08-18 15:59:59 UTC (rev 28029)
@@ -66,28 +66,6 @@
entry->proc = NULL;
}
-/**
- * Sets a given name to be an entry object.
- *
- * @param [in] interp Tcl interpreter to create the entry within
- * @param [in] name name to associate the given entry with
- * @param [in] entry entry to associate with the given name
- * @param [out] errPtr description of error if it couldn't be set
- * @return true if success; false if failure
- * @see set_object
- */
-static int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
- reg_error* errPtr) {
- if (set_object(interp, name, entry, "entry", entry_obj_cmd, NULL,
- errPtr)) {
- int size = strlen(name) + 1;
- entry->proc = malloc(size*sizeof(char));
- memcpy(entry->proc, name, size);
- return 1;
- }
- return 0;
-}
-
static int obj_to_entry(Tcl_Interp* interp, reg_entry** entry, Tcl_Obj* obj,
reg_error* errPtr) {
reg_entry* result = get_entry(interp, Tcl_GetString(obj), errPtr);
@@ -99,34 +77,16 @@
}
}
+/*
static int list_obj_to_entry(Tcl_Interp* interp, reg_entry*** entries,
const Tcl_Obj** objv, int objc, reg_error* errPtr) {
return recast(interp, (cast_function*)obj_to_entry, NULL, (void***)entries,
(void**)objv, objc, errPtr);
}
+*/
-static int entry_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_entry* entry,
- reg_error* errPtr) {
- if (entry->proc == NULL) {
- char* name = unique_name(interp, "registry::entry");
- if (!set_entry(interp, name, entry, errPtr)) {
- free(name);
- return 0;
- }
- free(name);
- }
- *obj = Tcl_NewStringObj(entry->proc, -1);
- return 1;
-}
-static int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
- reg_entry** entries, int entry_count, reg_error* errPtr) {
- return recast(interp, (cast_function*)entry_to_obj, NULL, (void***)objs,
- (void**)entries, entry_count, errPtr);
-}
-
-
-/**
+/*
* registry::entry create portname version revision variants epoch
*
* Unlike the old registry::new_entry, revision, variants, and epoch are all
@@ -161,7 +121,7 @@
}
}
-/**
+/*
* registry::entry delete entry
*
* Deletes an entry from the registry (then closes it). If this is done within a
@@ -201,7 +161,7 @@
}
}
-/**
+/*
* registry::entry open portname version revision variants epoch ?name?
*
* Opens an entry matching the given parameters.
@@ -347,7 +307,7 @@
}
}
-/**
+/*
* registry::entry exists name
*
* Note that this is <i>not</i> the same as entry_exists from registry1.0. This
@@ -369,7 +329,7 @@
return TCL_OK;
}
-/**
+/*
* registry::entry imaged ?name? ?version?
*
* Returns a list of all ports installed as images and/or active in the
@@ -382,29 +342,29 @@
* no analogue in 'direct' mode (it will be equivalent to `registry::entry
* installed`). That is, these ports are available but cannot meet dependencies.
*
+ * I would have liked to allow implicit variants, but there's no convenient way
+ * to distinguish between variants not being specified and being specified as
+ * empty. So, if a revision is specified, so must variants be.
+ *
* TODO: add more arguments (epoch, revision, variants), maybe
*/
static int entry_imaged(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
reg_registry* reg = registry_for(interp, reg_attached);
- if (objc > 4) {
- Tcl_WrongNumArgs(interp, 2, objv, "?name? ?version?");
+ if (objc == 5 || objc > 6) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?name ?version ?revision variants???");
return TCL_ERROR;
} else if (reg == NULL) {
return TCL_ERROR;
} else {
- char* name = (objc >= 3) ? Tcl_GetString(objv[2]) : NULL;
- char* version = (objc == 4) ? Tcl_GetString(objv[3]) : NULL;
+ const char* name = (objc >= 3) ? string_or_null(objv[2]) : NULL;
+ const char* version = (objc >= 4) ? string_or_null(objv[3]) : NULL;
+ const char* revision = (objc >= 6) ? string_or_null(objv[4]) : NULL;
+ const char* variants = (revision != 0) ? Tcl_GetString(objv[5]) : NULL;
reg_entry** entries;
reg_error error;
int entry_count;
- /* name or version of "" means not specified */
- if (name != NULL && *name == '\0') {
- name = NULL;
- }
- if (version != NULL && *version == '\0') {
- version = NULL;
- }
- entry_count = reg_entry_imaged(reg, name, version, &entries, &error);
+ entry_count = reg_entry_imaged(reg, name, version, revision, variants,
+ &entries, &error);
if (entry_count >= 0) {
Tcl_Obj* resultObj;
Tcl_Obj** objs;
@@ -419,7 +379,7 @@
}
}
-/**
+/*
* registry::entry installed ?name?
*
* Returns a list of all installed and active ports. If `name` is specified,
@@ -462,7 +422,7 @@
}
-/**
+/*
*/
static int entry_owner(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
reg_registry* reg = registry_for(interp, reg_attached);
@@ -509,7 +469,7 @@
{ NULL, NULL }
};
-/**
+/*
* registry::entry cmd ?arg ...?
*
* Commands manipulating port entries in the registry. This could be called
Modified: trunk/base/src/registry2.0/entryobj.c
===================================================================
--- trunk/base/src/registry2.0/entryobj.c 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/registry2.0/entryobj.c 2007-08-18 15:59:59 UTC (rev 28029)
@@ -85,7 +85,7 @@
return TCL_ERROR;
} else {
/* ${entry} prop name value; set a new value */
- reg_registry* reg = registry_for(interp, reg_attached | reg_can_write);
+ reg_registry* reg = registry_for(interp, reg_attached);
if (reg == NULL) {
return TCL_ERROR;
}
@@ -103,35 +103,42 @@
}
}
-/*
- * ${entry} map file-list
- *
- * Maps the listed files to the port represented by ${entry}. This will throw an
- * error if a file is mapped to an already-existing file, but not a very
- * descriptive one.
- *
- * TODO: more descriptive error on duplicated file
- */
-static int entry_obj_map(Tcl_Interp* interp, reg_entry* entry, int objc,
+typedef struct {
+ char* name;
+ int (*function)(reg_entry* entry, char** files, int file_count,
+ reg_error* errPtr);
+} filemap_op;
+
+static filemap_op filemap_cmds[] = {
+ { "map", reg_entry_map },
+ { "unmap", reg_entry_unmap },
+ { "deactivate", reg_entry_deactivate },
+ { NULL, NULL }
+};
+
+static int entry_obj_filemap(Tcl_Interp* interp, reg_entry* entry, int objc,
Tcl_Obj* CONST objv[]) {
reg_registry* reg = registry_for(interp, reg_attached);
+ int op;
if (objc != 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "map file-list");
+ Tcl_WrongNumArgs(interp, 2, objv, "file-list");
return TCL_ERROR;
} else if (reg == NULL) {
return TCL_ERROR;
+ } else if (Tcl_GetIndexFromObjStruct(interp, objv[1], filemap_cmds,
+ sizeof(filemap_op), "cmd", 0, &op) != TCL_OK) {
+ return TCL_ERROR;
} else {
char** files;
reg_error error;
Tcl_Obj** listv;
int listc;
- int i;
int result = TCL_ERROR;
if (Tcl_ListObjGetElements(interp, objv[2], &listc, &listv) != TCL_OK) {
return TCL_ERROR;
}
if (list_obj_to_string(&files, listv, listc, &error)) {
- if (reg_entry_map(entry, files, listc, &error) == listc) {
+ if (filemap_cmds[op].function(entry, files, listc, &error)) {
result = TCL_OK;
} else {
result = registry_failed(interp, &error);
@@ -144,32 +151,117 @@
}
}
-/*
- * ${entry} unmap file-list
- *
- * Unmaps the listed files from the given port. Will throw an error if a file
- * that is not mapped to the port is attempted to be unmapped.
- */
-static int entry_obj_unmap(Tcl_Interp* interp, reg_entry* entry, int objc,
+static int entry_obj_files(Tcl_Interp* interp, reg_entry* entry, int objc,
Tcl_Obj* CONST objv[]) {
reg_registry* reg = registry_for(interp, reg_attached);
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "map file-list");
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "files");
return TCL_ERROR;
} else if (reg == NULL) {
return TCL_ERROR;
} else {
char** files;
reg_error error;
+ int file_count = reg_entry_files(entry, &files, &error);
+ int i;
+ if (file_count >= 0) {
+ Tcl_Obj** objs;
+ int retval = TCL_ERROR;
+ if (list_string_to_obj(&objs, files, file_count, &error)) {
+ Tcl_Obj* result = Tcl_NewListObj(file_count, objs);
+ Tcl_SetObjResult(interp, result);
+ free(objs);
+ retval = TCL_OK;
+ } else {
+ retval = registry_failed(interp, &error);
+ }
+ for (i=0; i<file_count; i++) {
+ free(files[i]);
+ }
+ free(files);
+ return retval;
+ }
+ return registry_failed(interp, &error);
+ }
+}
+
+static int entry_obj_imagefiles(Tcl_Interp* interp, reg_entry* entry, int objc,
+ Tcl_Obj* CONST objv[]) {
+ reg_registry* reg = registry_for(interp, reg_attached);
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "files");
+ return TCL_ERROR;
+ } else if (reg == NULL) {
+ return TCL_ERROR;
+ } else {
+ char** files;
+ reg_error error;
+ int file_count = reg_entry_imagefiles(entry, &files, &error);
+ int i;
+ if (file_count >= 0) {
+ Tcl_Obj** objs;
+ int retval = TCL_ERROR;
+ if (list_string_to_obj(&objs, files, file_count, &error)) {
+ Tcl_Obj* result = Tcl_NewListObj(file_count, objs);
+ Tcl_SetObjResult(interp, result);
+ free(objs);
+ retval = TCL_OK;
+ } else {
+ retval = registry_failed(interp, &error);
+ }
+ for (i=0; i<file_count; i++) {
+ free(files[i]);
+ }
+ free(files);
+ return retval;
+ }
+ return registry_failed(interp, &error);
+ }
+}
+
+static int entry_obj_activate(Tcl_Interp* interp, reg_entry* entry, int objc,
+ Tcl_Obj* CONST objv[]) {
+ reg_registry* reg = registry_for(interp, reg_attached);
+ if (objc > 4) {
+ Tcl_WrongNumArgs(interp, 1, objv, "activate file-list ?as-file-list?");
+ return TCL_ERROR;
+ } else if (reg == NULL) {
+ return TCL_ERROR;
+ } else {
+ char** files;
+ char** as_files = NULL;
+ reg_error error;
+ Tcl_Obj* as;
+ Tcl_Obj** as_listv;
Tcl_Obj** listv;
int listc;
- int i;
+ int as_listc;
int result = TCL_ERROR;
+ if (objc >= 4) {
+ as = objv[3];
+ } else {
+ as = NULL;
+ as_listv = NULL;
+ }
if (Tcl_ListObjGetElements(interp, objv[2], &listc, &listv) != TCL_OK) {
return TCL_ERROR;
}
- if (list_obj_to_string(&files, listv, listc, &error)) {
- if (reg_entry_unmap(entry, files, listc, &error) == listc) {
+ if (as != NULL) {
+ if (Tcl_ListObjGetElements(interp, as, &as_listc, &as_listv)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (listc != as_listc) {
+ /* TODO: set an error code */
+ Tcl_SetResult(interp, "list and as_list must be of equal "
+ "length", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ }
+ if (list_obj_to_string(&files, listv, listc, &error)
+ && (as_listv == NULL || list_obj_to_string(&as_files, as_listv,
+ as_listc, &error))) {
+ if (reg_entry_activate(entry, files, as_files, listc, &error)) {
result = TCL_OK;
} else {
result = registry_failed(interp, &error);
@@ -182,40 +274,84 @@
}
}
-static int entry_obj_files(Tcl_Interp* interp, reg_entry* entry, int objc,
+static int entry_obj_dependencies(Tcl_Interp* interp, reg_entry* entry,
+ int objc, Tcl_Obj* CONST objv[]) {
+ reg_registry* reg = registry_for(interp, reg_attached);
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "dependents");
+ return TCL_ERROR;
+ } else if (reg == NULL) {
+ return TCL_ERROR;
+ } else {
+ reg_entry** entries;
+ reg_error error;
+ int entry_count = reg_entry_dependencies(entry, &entries, &error);
+ if (entry_count >= 0) {
+ Tcl_Obj** objs;
+ int retval = TCL_ERROR;
+ if (list_entry_to_obj(interp, &objs, entries, entry_count, &error)){
+ Tcl_Obj* result = Tcl_NewListObj(entry_count, objs);
+ Tcl_SetObjResult(interp, result);
+ free(objs);
+ retval = TCL_OK;
+ } else {
+ retval = registry_failed(interp, &error);
+ }
+ free(entries);
+ return retval;
+ }
+ return registry_failed(interp, &error);
+ }
+}
+
+static int entry_obj_dependents(Tcl_Interp* interp, reg_entry* entry, int objc,
Tcl_Obj* CONST objv[]) {
reg_registry* reg = registry_for(interp, reg_attached);
if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "files");
+ Tcl_WrongNumArgs(interp, 1, objv, "dependents");
return TCL_ERROR;
} else if (reg == NULL) {
return TCL_ERROR;
} else {
- char** files;
+ reg_entry** entries;
reg_error error;
- int file_count = reg_entry_files(entry, &files, &error);
- if (file_count >= 0) {
- int i;
+ int entry_count = reg_entry_dependents(entry, &entries, &error);
+ if (entry_count >= 0) {
Tcl_Obj** objs;
int retval = TCL_ERROR;
- if (list_string_to_obj(&objs, files, file_count, &error)) {
- Tcl_Obj* result = Tcl_NewListObj(file_count, objs);
+ if (list_entry_to_obj(interp, &objs, entries, entry_count, &error)){
+ Tcl_Obj* result = Tcl_NewListObj(entry_count, objs);
Tcl_SetObjResult(interp, result);
free(objs);
retval = TCL_OK;
} else {
retval = registry_failed(interp, &error);
}
- for (i=0; i<file_count; i++) {
- free(files[i]);
- }
- free(files);
+ free(entries);
return retval;
}
return registry_failed(interp, &error);
}
}
+static int entry_obj_depends(Tcl_Interp* interp, reg_entry* entry, int objc,
+ Tcl_Obj* CONST objv[]) {
+ reg_registry* reg = registry_for(interp, reg_attached);
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "depends portname");
+ return TCL_ERROR;
+ } else if (reg == NULL) {
+ return TCL_ERROR;
+ } else {
+ char* port = Tcl_GetString(objv[2]);
+ reg_error error;
+ if (reg_entry_depends(entry, port, &error)) {
+ return TCL_OK;
+ }
+ return registry_failed(interp, &error);
+ }
+}
+
typedef struct {
char* name;
int (*function)(Tcl_Interp* interp, reg_entry* entry, int objc,
@@ -223,6 +359,7 @@
} entry_obj_cmd_type;
static entry_obj_cmd_type entry_cmds[] = {
+ /* keys */
{ "name", entry_obj_prop },
{ "portfile", entry_obj_prop },
{ "url", entry_obj_prop },
@@ -235,9 +372,17 @@
{ "date", entry_obj_prop },
{ "state", entry_obj_prop },
{ "installtype", entry_obj_prop },
- { "map", entry_obj_map },
- { "unmap", entry_obj_unmap },
+ /* filemap */
+ { "map", entry_obj_filemap },
+ { "unmap", entry_obj_filemap },
{ "files", entry_obj_files },
+ { "imagefiles", entry_obj_imagefiles },
+ { "activate", entry_obj_activate },
+ { "deactivate", entry_obj_filemap },
+ /* dep map */
+ { "dependents", entry_obj_dependents },
+ { "dependencies", entry_obj_dependencies },
+ { "depends", entry_obj_depends },
{ NULL, NULL }
};
Added: trunk/base/src/registry2.0/portimage.tcl
===================================================================
--- trunk/base/src/registry2.0/portimage.tcl (rev 0)
+++ trunk/base/src/registry2.0/portimage.tcl 2007-08-18 15:59:59 UTC (rev 28029)
@@ -0,0 +1,445 @@
+# et:ts=4
+# portimage.tcl
+# $Id$
+#
+# Copyright (c) 2004 Will Barton <wbb4 at opendarwin.org>
+# Copyright (c) 2002 Apple Computer, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+package provide portimage 2.0
+
+package require macports 1.0
+package require registry2 2.0
+package require registry_util 2.0
+package require Pextlib 1.0
+
+set UI_PREFIX "--> "
+
+#
+# Port Images are basically just installations of the destroot of a port into
+# ${macports::registry.path}/software/${name}/${version}_${revision}${variants}
+# They allow the user to instal multiple versions of the same port, treating
+# each revision and each different combination of variants as a "version".
+#
+# From there, the user can "activate" a port image. This creates {sym,hard}links for
+# all files in the image into the ${prefix}. Directories are created.
+# Activation checks the registry's file_map for any files which conflict with
+# other "active" ports, and will not overwrite the links to the those files.
+# The conflicting port must be deactivated first.
+#
+# The user can also "deactivate" an active port. This will remove all {sym,hard}links
+# from ${prefix}, and if any directories are empty, remove them as well. It
+# will also remove all of the references of the files from the registry's
+# file_map
+#
+# Compacting and Uncompacting of port images to save space will be implemented
+# at some point.
+#
+# For the creating and removing of links during activation and deactivation,
+# code very similar to what is used in portinstall is used.
+#
+
+namespace eval portimage {
+
+# Activate a "Port Image"
+proc activate {name specifier optionslist} {
+ global macports::prefix macports::registry.path UI_PREFIX
+ array set options $optionslist
+
+ if {[info exists options(ports_force)] && [string is true $options(ports_force)] } {
+ set force 1
+ } else {
+ set force 0
+ }
+
+ ui_msg "$UI_PREFIX [format [msgcat::mc "Activating %s %s"] $name $specifier]"
+
+ registry::read {
+
+ set requested [_check_registry $name $specifier]
+ set version [$requested version]
+ set revision [$requested revision]
+ set variants [$requested variants]
+ set specifier "${version}_$revision$variants"
+
+ set current [registry::entry installed $name]
+ if { [llength $current] > 1 } {
+ foreach i $current {
+ set iname [$i name]
+ set iversion [$i version]
+ set irevision [$i revision]
+ set ivariants [$i variants]
+ set ispecifier "${iversion}_$irevision$ivariants"
+ if { ![string equal $specifier $ispecifier]
+ && [string equal [$i state] "installed"] } {
+ return -code error "Image error: Another version of this port ($iname @${iversion}_${irevision}${ivariants}) is already active."
+ }
+ }
+ }
+
+ # this shouldn't be possible
+ if { ![string equal [$requested installtype] "image"] } {
+ return -code error "Image error: ${name} @${version}_${revision}${variants} not installed as an image."
+ }
+
+ if { [string equal [$requested state] "active" } {
+ return -code error "Image error: ${name} @${version}_${revision}${variants} is already active."
+ }
+
+ # compaction is not yet supported
+ #if { [$requested compact] != 0 } {
+ # return -code error "Image error: ${name} @${version}_${revision}${variants} is compacted."
+ #}
+ }
+
+ _activate_contents $port $force
+ $requested state active
+}
+
+proc deactivate {name spec optionslist} {
+ global UI_PREFIX
+ array set options $optionslist
+
+ if {[info exists options(ports_force)] && [string is true $options(ports_force)] } {
+ set force 1
+ } else {
+ set force 0
+ }
+
+ ui_msg "$UI_PREFIX [format [msgcat::mc "Deactivating %s %s"] $name $v]"
+
+ if { [string equal $name {}] } {
+ throw registry::image-error "Registry error: Please specify the name of the port."
+ }
+ set ilist [registry::entry installed $name]
+ if { [llength $ilist] == 1 } {
+ set requested [lindex $ilist 0]
+ } else {
+ throw registry::image-error "Image error: port ${name} is not active."
+ }
+ set version [$requested version]
+ set revision [$requested revision]
+ set variants [$requested variants]
+ set specifier ${version}_$revision$variants
+
+ if { ![string equal $spec {}] && ![string equal $spec $specifier] } {
+ return -code error "Active version of $name is not $spec but $specifier."
+ }
+ if { ![string equal [$requested installtype] "image"] } {
+ return -code error "Image error: ${name} @${specifier} not installed as an image."
+ }
+ # this shouldn't be possible
+ if { [$requested state] != "installed" } {
+ return -code error "Image error: ${name} @${specifier} is not active."
+ }
+
+ # compaction not yet supported
+ #if { [registry::property_retrieve $ref compact] != 0 } {
+ # return -code error "Image error: ${name} @${specifier} is compacted."
+ #}
+
+ registry::check_dependents $port $force
+
+ set imagedir [$requested imagedir]
+ set imagefiles [$requested files]
+
+ _deactivate_contents $requested $force
+ $requested state imaged
+}
+
+proc compact {name v} {
+ global UI_PREFIX
+
+ throw registry::image-error "Image error: compact/uncompact not yet implemented."
+}
+
+proc uncompact {name v} {
+ global UI_PREFIX
+
+ throw registry::image-error "Image error: compact/uncompact not yet implemented."
+}
+
+proc _check_registry {name specifier} {
+ global UI_PREFIX
+
+ if { [registry::decode_spec $specifier version revision variants] } {
+ set ilist [registry::entry imaged $name $version $revision $variants]
+ set valid 1
+ } else {
+ set valid [string equal $specifier {}]
+ set ilist [registry::entry imaged $name]
+ }
+
+ if { [llength $ilist] > 1 || (!$valid && [llength $ilist] == 1) } {
+ ui_msg "$UI_PREFIX [msgcat::mc "The following versions of $name are currently installed:"]"
+ foreach i $ilist {
+ set iname [$i name]
+ set iversion [$i version]
+ set irevision [$i revision]
+ set ivariants [$i variants]
+ if { [$i state] == "installed" } {
+ ui_msg "$UI_PREFIX [format [msgcat::mc " %s @%s_%s%s (active)"] $iname $iversion $irevision $ivariants]"
+ } else {
+ ui_msg "$UI_PREFIX [format [msgcat::mc " %s @%s_%s%s"] $iname $iversion $irevision $ivariants]"
+ }
+ }
+ if { $valid } {
+ throw registry::invalid "Registry error: Please specify the full version as recorded in the port registry."
+ } else {
+ throw registry::invalid "Registry error: Invalid version specified. Please specify a version as recorded in the port registry."
+ }
+ } else if { [llength $ilist] == 1 } {
+ return [lindex $ilist 0]
+ }
+ throw registry::invalid "Registry error: No port of $name installed."
+}
+
+## Activates a file from an image into the filesystem. Deals with symlinks,
+## directories and files.
+##
+## @param [in] srcfile path to file in image
+## @param [in] dstfile path to activate file to
+proc _activate_file {srcfile dstfile} {
+ switch { [file type $srcfile] } {
+ case link {
+ ui_debug "activating link: $dstfile"
+ file copy -force $srcfile $dstfile
+ }
+ case directory {
+ # Don't recursively copy directories
+ ui_debug "activating directory: $dstfile"
+ # Don't do anything if the directory already exists.
+ if { ![file isdirectory $dstfile] } {
+ file mkdir $dstfile
+ # copy attributes, set mtime and atime
+ eval file attributes [list $dstfile] [file attributes $srcfile]
+ file mtime $dstfile [file mtime $srcfile]
+ file atime $dstfile [file atime $srcfile]
+ }
+ }
+ case file {
+ ui_debug "activating file: $dstfile"
+ # Try a hard link first and if that fails, a symlink
+ try {
+ compat filelinkhard $dstfile $srcfile
+ } catch {*} {
+ ui_debug "hardlinking $srcfile to $dstfile failed; symlinking instead"
+ compat filelinksymbolic $dstfile $srcfile
+ }
+ }
+ default {
+ # don't activate e.g. a unix socket
+ ui_warning "skipped file $srcfile of unknown type [file type $srcfile]"
+ }
+ }
+}
+
+## Activates the contents of a port
+proc _activate_contents {port force} {
+ global macports::prefix
+
+ set files [list]
+ set imagedir [$port imagedir]
+ set imagefiles [$port imagefiles]
+
+ # first, ensure all files exist in the image dir
+ foreach file $imagefiles {
+ set srcfile $imagedir$file
+ # To be able to install links, we test if we can lstat the file to
+ # figure out if the source file exists (file exists will return
+ # false for symlinks on files that do not exist)
+ try {
+ file lstat $srcfile dummystatvar
+ } catch {*} {
+ throw registry::image-error "Image error: Source file $srcfile does not appear to exist (cannot lstat it). Unable to activate port [$port name]."
+ }
+ }
+
+ set baksuffix .mp_[clock seconds]
+ set backups [list]
+
+ # This is big and hairy and probably could be done better.
+
+ # Then we remove the $imagedir from the path of the file in the contents
+ # list and check to see if that file exists
+ # Last, if the file exists, and belongs to another port, and force is set
+ # we remove the file from the file_map, take ownership of it, and
+ # clobber it
+ try {
+ registry::write {
+ foreach file $imagefiles {
+ set srcfile ${imagedir}${file}
+
+ set owner [registry::entry owner $file]
+
+ if { [string is true $force] } {
+ # if we're forcing the activation, then we move any existing
+ # files to a backup file, both in the filesystem and in the
+ # registry
+ if { [file exists $file] } {
+ ui_warn "File $file already exists. Moving to: $bakfile."
+ file rename -force $file $file$baksuffix
+ lappend backups $file
+ }
+ if { $owner != {} } {
+ $owner deactivate [list $file]
+ $owner activate [list $file] [list $file$baksuffix]
+ }
+ } else {
+ # if we're not forcing the activation, then we bail out if
+ # we find any files that already exist, or have entries in
+ # the registry
+ if { $owner != {} && $owner != $port } {
+ throw registry::image-error "Image error: $file is being used by the active [$owner name] port. Please deactivate this port first, or use the -f flag to force the activation."
+ } elseif { $owner == {} && [file exists $file] } {
+ throw registry::image-error "Image error: $file already exists and does not belong to a registered port. Unable to activate port [$owner name]."
+ }
+ }
+
+ # Split out the filename's subpaths and add them to the
+ # imagefile list.
+ # We need directories first to make sure they will be there
+ # before links. However, because file mkdir creates all parent
+ # directories, we don't need to have them sorted from root to
+ # subpaths. We do need, nevertheless, all sub paths to make sure
+ # we'll set the directory attributes properly for all
+ # directories.
+ set directory [file dirname $file]
+ while { [lsearch -exact $files $directory] == -1 } {
+ lappend files $directory
+ set directory [file dirname $directory]
+ }
+
+ # Also add the filename to the imagefile list.
+ lappend files $file
+ }
+
+ # Sort the list in forward order, removing duplicates.
+ # Since the list is sorted in forward order, we're sure that
+ # directories are before their elements.
+ # We don't have to do this as mentioned above, but it makes the
+ # debug output of activate make more sense.
+ set theList [lsort -increasing -unique $files]
+
+ # Activate it, and catch errors so we can roll-back
+ try {
+ [$port activate $imagefiles]
+ foreach file $theList {
+ _activate_file $imagedir$file $file
+ }
+ } catch {*} {
+ ui_debug "Activation failed, rolling back."
+ _deactivate_contents $port yes
+ throw
+ }
+ }
+ } catch {*} {
+ # if any errors occurred, move backed-up files back to their original
+ # locations, then rethrow the error. Transaction rollback will take care
+ # of this in the registry.
+ foreach file $backups {
+ file rename -force $file$baksuffix $file
+ }
+ throw
+ }
+}
+
+proc _deactivate_file {dstfile} {
+ switch { [file type $dstfile] } {
+ case link {
+ ui_debug "deactivating link: $dstfile"
+ file delete -- $dstfile
+ }
+ case directory {
+ # 0 item means empty.
+ if { [llength [readdir $dstfile]] == 0 } {
+ ui_debug "deactivating directory: $dstfile"
+ file delete -- $dstfile
+ } else {
+ ui_debug "$dstfile is not empty"
+ }
+ }
+ case file {
+ ui_debug "deactivating file: $dstfile"
+ file delete -- $dstfile
+ }
+ default {
+ # don't deactivate e.g. a unix socket
+ ui_warning "skipped file $dstfile of unknown type [file type $dstfile]"
+ }
+ }
+}
+
+proc _deactivate_contents {port force} {
+
+ set files [list]
+
+ set realfiles [$port files]
+
+ foreach file $realfiles {
+ set owner [registry::entry owner $file]
+ if { [file exists $file] || (![catch {file type $file}] && [file type $file] == "link") } {
+ # Normalize the file path to avoid removing the intermediate
+ # symlinks (remove the empty directories instead)
+ # Remark: paths in the registry may be not normalized.
+ # This is not really a problem and it is in fact preferable.
+ # Indeed, if I change the activate code to include normalized paths
+ # instead of the paths we currently have, users' registry won't
+ # match and activate will say that some file exists but doesn't
+ # belong to any port.
+ set theFile [compat filenormalize $file]
+ lappend files $theFile
+
+ # Split out the filename's subpaths and add them to the image list
+ # as well.
+ set directory [file dirname $theFile]
+ while { [lsearch -exact $files $directory] == -1 } {
+ lappend files $directory
+ set directory [file dirname $directory]
+ }
+ } else {
+ ui_debug "$file does not exist."
+ }
+ }
+
+ # Sort the list in reverse order, removing duplicates.
+ # Since the list is sorted in reverse order, we're sure that directories
+ # are after their elements.
+ set theList [lsort -decreasing -unique $files]
+
+ registry::write {
+ # Remove all elements.
+ $port deactivate $realfiles
+ foreach file $theList {
+ _deactivate_file $file
+ }
+ }
+}
+
+# End of portimage namespace
+}
Added: trunk/base/src/registry2.0/portuninstall.tcl
===================================================================
--- trunk/base/src/registry2.0/portuninstall.tcl (rev 0)
+++ trunk/base/src/registry2.0/portuninstall.tcl 2007-08-18 15:59:59 UTC (rev 28029)
@@ -0,0 +1,190 @@
+# et:ts=4
+# portuninstall.tcl
+# $Id$
+#
+# Copyright (c) 2002 - 2003 Apple Computer, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+package provide portuninstall 2.0
+
+package require registry2 2.0
+package require registry_util 2.0
+
+set UI_PREFIX "---> "
+
+namespace eval portuninstall {
+
+proc uninstall {portname {specifier ""} optionslist} {
+ global uninstall.force uninstall.nochecksum UI_PREFIX
+ array set options $optionslist
+
+ if {[info exists options(ports_force)] && [string is true $options(ports_force)] } {
+ set force 1
+ } else {
+ set force 0
+ }
+
+ if { [registry::decode_spec $specifier version revision variants] } {
+ set ilist [registry::entry imaged $portname $version $revision $variants]
+ set valid 1
+ } else {
+ set valid [string equal $specifier {}]
+ set ilist [registry::entry imaged $portname]
+ }
+
+ if { [llength $ilist] > 1 } {
+ ui_msg "$UI_PREFIX [format [msgcat::mc "The following versions of %s are currently installed:"] $portname]"
+ foreach i $ilist {
+ set iname [lindex $i 0]
+ set iactive [lindex $i 4]
+ set ispec "[$i version]_[$i revision][$i variants]"
+ if { [string equal [$i state] installed] } {
+ ui_msg "$UI_PREFIX [format [msgcat::mc " %s @%s (active)"] $iname $ispec]"
+ } elseif { $iactive == 1 } {
+ ui_msg "$UI_PREFIX [format [msgcat::mc " %s @%s"] $iname $ispec]"
+ }
+ }
+ if { $valid } {
+ throw registry::invalid "Registry error: Please specify the full version as recorded in the port registry."
+ } else {
+ throw registry::invalid "Registry error: Invalid version specified. Please specify a version as recorded in the port registry."
+ }
+ } elseif { [llength $ilist] == 1 } {
+ set port [lindex $ilist 0]
+ } else {
+ throw registry::invalid "Registry error: $portname not registered as installed"
+ }
+
+ if { [string equal [$port installtype] direct] } {
+ # if port is installed directly, check its dependents
+ registry::check_dependents $port $force
+ } else {
+ # if it's an image, deactivate it (and check dependents there)
+ if { [string equal [$port state] installed] } {
+ portimage::deactivate $portname ${version}_${revision}${variants} $optionslist
+ }
+ }
+
+ ui_msg "$UI_PREFIX [format [msgcat::mc "Uninstalling %s %s_%s%s"] $portname $version $revision $variants]"
+
+ # pkg_uninstall isn't used anywhere as far as I can tell and I intend to add
+ # some proper pre-/post- hooks to uninstall/deactivate.
+
+ # Look to see if the port has registered an uninstall procedure
+ #set uninstall [registry::property_retrieve $ref pkg_uninstall]
+ #if { $uninstall != 0 } {
+ # if {![catch {eval $uninstall} err]} {
+ # pkg_uninstall $portname ${version}_${revision}${variants}
+ # } else {
+ # global errorInfo
+ # ui_debug "$errorInfo"
+ # ui_error [format [msgcat::mc "Could not evaluate pkg_uninstall procedure: %s"] $err]
+ # }
+ #}
+
+ set contents [$port files]
+
+ set bak_suffix .mp_[time seconds]
+ set uninst_err 0
+ set files [list]
+ foreach file $contents {
+ if { !([info exists uninstall.nochecksum]
+ && [string is true $uninstall.nochecksum]) } {
+ set sum1 [$port md5sum $file]
+ if {![catch {set sum2 [md5 $file]}] && ![string match $sum1 $sum2]}{
+ ui_info "$UI_PREFIX [format [msgcat::mc "Original checksum does not match for %s, saving a copy to %s"] $file $file$bak_suffix]"
+ file copy $file $file$bak_suffix
+ }
+ }
+
+ # Normalize the file path to avoid removing the intermediate
+ # symlinks (remove the empty directories instead)
+ set theFile [compat filenormalize $file
+ lappend files $theFile
+
+ # Split out the filename's subpaths and add them to the
+ # list as well.
+ set directory [file dirname $theFile]
+ while { [lsearch -exact $files $directory] == -1 } {
+ lappend files $directory
+ set directory [file dirname $directory]
+ }
+ }
+
+ # Sort the list in reverse order, removing duplicates.
+ # Since the list is sorted in reverse order, we're sure that directories
+ # are after their elements.
+ set theList [lsort -decreasing -unique $files]
+
+ # Remove all elements.
+ foreach file $theList {
+ _uninstall_file $file
+ }
+
+ ui_info "$UI_PREFIX [format [msgcat::mc "Uninstall is removing %s from the port registry."] $portname]"
+ registry::entry delete $port
+ return 0
+}
+
+proc _uninstall_file {dstfile} {
+ if { ![catch {set type [file type $dstfile]}] } {
+ switch {$type} {
+ case link {
+ ui_debug "uninstalling link: $dstfile"
+ file delete -- $dstfile
+ }
+ case directory {
+ # 0 item means empty.
+ if { [llength [readdir $dstfile]] == 0 } {
+ ui_debug "uninstalling directory: $dstfile"
+ file delete -- $dstfile
+ } else {
+ ui_debug "$dstfile is not empty"
+ }
+ }
+ case file {
+ ui_debug "uninstalling file: $dstfile"
+ file delete -- $dstfile
+ }
+ default {
+ ui_debug "skip file of unknown type $type: $dstfile"
+ }
+ }
+ } else {
+ ui_debug "skip missing file: $dstfile"
+ }
+}
+
+proc _uninstall_list {filelist} {
+ foreach file $filelist {
+ _uninstall_file $file
+ }
+}
+
+# End of portuninstall namespace
+}
Added: trunk/base/src/registry2.0/registry_util.tcl
===================================================================
--- trunk/base/src/registry2.0/registry_util.tcl (rev 0)
+++ trunk/base/src/registry2.0/registry_util.tcl 2007-08-18 15:59:59 UTC (rev 28029)
@@ -0,0 +1,79 @@
+# et:ts=4
+# registry_util.tcl
+# $Id$
+#
+# Copyright (c) 2007 Chris Pickel
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+package provide registry_util 2.0
+
+package require registry2 2.0
+
+namespace eval registry {
+
+## Decodes a version specifier into its component values. Values will be
+## returned into the variables named by `version`, `revision`, and `variants`,
+## with `revision` and `variants` possibly being set to the empty string if none
+## were specified.
+##
+## This accept a full specifier such as `1.2.1_3+cool-lame` (to disable MP3
+## support) or a bare version, such as `1.2.1`. If a revision is not specified,
+## then the returned revision and variants will be empty.
+##
+## @param [in] specifier a specifier, as described above
+## @param [out] version name of a variable to return version into
+## @param [out] revision name of a variable to return revision into
+## @param [out] variants name of a variable to return variants into
+## @return true if `specifier` is a valid specifier, else false
+proc decode_spec {specifier version revision variants} {
+ upvar 1 $version ver $revision rev $variants var
+ return [regexp {^([^-+_]+)(_([^-+]+)(([-+][^-+]+)*))?$} - ver - rev var]
+}
+
+## Checks that the given port has no dependent ports. If it does, throws an
+## error if force wasn't set (emits a message if it is).
+##
+## @param [in] port a registry::entry to check
+## @param [in] force if true, continue even if there are dependents
+proc check_dependents {port force} {
+ # Check and make sure no ports depend on this one
+ set deplist [$port dependents]
+ if { [llength $deplist] > 0 } {
+ ui_msg "$UI_PREFIX [format [msgcat::mc "Unable to uninstall %s %s_%s%s, the following ports depend on it:"] $portname $version $revision $variants]"
+ foreach depport $deplist {
+ ui_msg "$UI_PREFIX [format [msgcat::mc " %s"] $depport]"
+ }
+ if { [string is true $force] } {
+ ui_warn "Uninstall forced. Proceeding despite dependencies."
+ } else {
+ throw registry::uninstall-error "Please uninstall the ports that depend on $portname first."
+ }
+ }
+}
+
+}
Modified: trunk/base/src/registry2.0/tests/common.tcl
===================================================================
--- trunk/base/src/registry2.0/tests/common.tcl 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/registry2.0/tests/common.tcl 2007-08-18 15:59:59 UTC (rev 28029)
@@ -34,6 +34,23 @@
}"
}
+proc test_set {statement value} {
+ uplevel 1 "\
+ puts -nonewline {checking if $statement is \[list $value\]... }
+ if {\[catch {
+ set actual \[lsort $statement\]
+ if {\$actual == \[lsort \[subst {\[list $value\]}\]\]} { \n\
+ puts yes
+ } else { \n\
+ puts \"no (was \$actual)\" \n\
+ exit 1 \n\
+ } \n\
+ } msg\]} { \n\
+ puts \"caught error: \$msg\" \n\
+ exit 1 \n\
+ }"
+}
+
proc test_throws {statement error} {
uplevel 1 "\
puts -nonewline {checking if \[$statement\] throws $error... }
Added: trunk/base/src/registry2.0/tests/depends.tcl
===================================================================
--- trunk/base/src/registry2.0/tests/depends.tcl (rev 0)
+++ trunk/base/src/registry2.0/tests/depends.tcl 2007-08-18 15:59:59 UTC (rev 28029)
@@ -0,0 +1,163 @@
+# Test file for registry::entry dependencies
+# Syntax:
+# tclsh depends.tcl <Pextlib name>
+
+proc main {pextlibname} {
+ load $pextlibname
+
+ # totally lame that file delete won't do it
+ eval exec rm -f [glob -nocomplain test.db*]
+
+ registry::open test.db
+
+ # some really contrived ports to test with
+ # this is the dependency graph, roughly:
+
+ # a1 a2 a3
+ # \ / /
+ # b1&b2 /
+ # /\ /\ c
+ # f d g /
+ # \ /
+ # e
+
+ registry::write {
+ set a1 [registry::entry create a 1 0 0 {}]
+ set a2 [registry::entry create a 2 0 0 {}]
+ set a3 [registry::entry create a 3 0 0 {}]
+ $a1 depends b
+ $a2 depends b
+ $a3 depends c
+
+ set b1 [registry::entry create b 1 0 0 {}]
+ set b2 [registry::entry create b 2 0 0 {}]
+ $b1 depends d
+ $b1 depends f
+ $b2 depends d
+ $b2 depends g
+
+ set c [registry::entry create c 1 0 0 {}]
+ $c depends e
+
+ set d [registry::entry create d 1 0 0 {}]
+ $d depends e
+
+ set e [registry::entry create e 1 0 0 {}]
+ set f [registry::entry create f 1 0 0 {}]
+ set g [registry::entry create g 1 0 0 {}]
+
+ $a1 state installed
+ $a2 state imaged
+ $a3 state imaged
+ $b1 state installed
+ $b2 state imaged
+ $c state installed
+ $d state installed
+ $e state installed
+ $f state installed
+ $g state imaged
+ }
+
+ registry::read {
+ test_set {[$a1 dependents]} {}
+ test_set {[$a2 dependents]} {}
+ test_set {[$a3 dependents]} {}
+ test_set {[$b1 dependents]} {$a1}
+ test_set {[$b2 dependents]} {}
+ test_set {[$c dependents]} {}
+ test_set {[$d dependents]} {$b1}
+ test_set {[$e dependents]} {$c $d}
+ test_set {[$f dependents]} {$b1}
+ test_set {[$g dependents]} {}
+
+ test_set {[$a1 dependencies]} {$b1}
+ test_set {[$b1 dependencies]} {$d $f}
+ test_set {[$c dependencies]} {$e}
+ test_set {[$d dependencies]} {$e}
+ test_set {[$e dependencies]} {}
+ test_set {[$f dependencies]} {}
+ test_set {[$g dependencies]} {}
+ }
+
+ registry::write {
+ $b1 state imaged
+ $b2 state installed
+ $g state installed
+ }
+
+ registry::read {
+ test_set {[$b1 dependents]} {}
+ test_set {[$b2 dependents]} {$a1}
+ test_set {[$c dependents]} {}
+ test_set {[$d dependents]} {$b2}
+ test_set {[$e dependents]} {$c $d}
+ test_set {[$f dependents]} {}
+ test_set {[$g dependents]} {$b2}
+
+ test_set {[$a1 dependencies]} {$b2}
+ test_set {[$b2 dependencies]} {$d $g}
+ test_set {[$c dependencies]} {$e}
+ test_set {[$d dependencies]} {$e}
+ }
+
+ registry::write {
+ $a1 state imaged
+ $a2 state installed
+ $f state imaged
+ }
+
+ registry::read {
+ test_set {[$b1 dependents]} {}
+ test_set {[$b2 dependents]} {$a2}
+ test_set {[$c dependents]} {}
+ test_set {[$d dependents]} {$b2}
+ test_set {[$e dependents]} {$c $d}
+ test_set {[$f dependents]} {}
+ test_set {[$g dependents]} {$b2}
+
+ test_set {[$a2 dependencies]} {$b2}
+ test_set {[$b2 dependencies]} {$d $g}
+ test_set {[$c dependencies]} {$e}
+ test_set {[$d dependencies]} {$e}
+ }
+
+ registry::write {
+ $a2 state imaged
+ $a3 state installed
+ }
+
+ registry::read {
+ test_set {[$b1 dependents]} {}
+ test_set {[$b2 dependents]} {}
+ test_set {[$c dependents]} {$a3}
+ test_set {[$d dependents]} {$b2}
+ test_set {[$e dependents]} {$c $d}
+ test_set {[$f dependents]} {}
+ test_set {[$g dependents]} {$b2}
+
+ test_set {[$a3 dependencies]} {$c}
+ test_set {[$b2 dependencies]} {$d $g}
+ test_set {[$c dependencies]} {$e}
+ test_set {[$d dependencies]} {$e}
+ }
+
+ registry::write {
+ $b2 state imaged
+ $d state imaged
+ }
+
+ registry::read {
+ test_set {[$c dependents]} {$a3}
+ test_set {[$d dependents]} {}
+ test_set {[$e dependents]} {$c}
+ test_set {[$g dependents]} {}
+
+ test_set {[$a3 dependencies]} {$c}
+ test_set {[$d dependencies]} {$e}
+ }
+
+ file delete test.db
+}
+
+source tests/common.tcl
+main $argv
Modified: trunk/base/src/registry2.0/tests/entry.tcl
===================================================================
--- trunk/base/src/registry2.0/tests/entry.tcl 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/registry2.0/tests/entry.tcl 2007-08-18 15:59:59 UTC (rev 28029)
@@ -1,16 +1,17 @@
# $Id$
-# Test file for registry::item
+# Test file for registry::entry
# Syntax:
-# tclsh item.tcl <Pextlib name>
+# tclsh entry.tcl <Pextlib name>
proc main {pextlibname} {
load $pextlibname
- file delete [glob -nocomplain test.db*]
+ # totally lame that file delete won't do it
+ eval exec rm -f [glob -nocomplain test.db*]
# can't create registry in some brain-dead place or in protected place
test_throws {registry::open /some/brain/dead/place} registry::cannot-init
- test_throws {registry::open /etc/macports_test~} registry::cannot-init
+ test_throws {registry::open /etc/macports_test_prot~} registry::cannot-init
# can't use registry before it's opened
test_throws {registry::write {}} registry::misuse
@@ -25,11 +26,11 @@
registry::write {
# create some (somewhat contrived) ports to play with
- set vim1 [registry::entry create vim 7.1.000 0 {multibyte +} 0]
+ set vim1 [registry::entry create vim 7.1.000 0 +cscope+multibyte 0]
set vim2 [registry::entry create vim 7.1.002 0 {} 0]
- set vim3 [registry::entry create vim 7.1.002 0 {multibyte +} 0]
+ set vim3 [registry::entry create vim 7.1.002 0 +cscope+multibyte 0]
set zlib [registry::entry create zlib 1.2.3 1 {} 0]
- set pcre [registry::entry create pcre 7.1 1 {utf8 +} 0]
+ set pcre [registry::entry create pcre 7.1 1 +utf8 0]
# check that their properties can be set
$vim1 state imaged
@@ -38,6 +39,12 @@
$zlib state installed
$pcre state imaged
+ $vim1 installtype image
+ $vim2 installtype image
+ $vim3 installtype image
+ $zlib installtype direct
+ $pcre installtype image
+
}
# check that their properties can be retrieved
@@ -47,59 +54,90 @@
test_equal {[$vim2 epoch]} 0
test_equal {[$vim3 version]} 7.1.002
test_equal {[$zlib revision]} 1
- test_equal {[$pcre variants]} {utf8 +}
+ test_equal {[$pcre variants]} +utf8
# check that imaged and installed give correct results
- # have to sort these because their orders aren't defined
- set imaged [registry::entry imaged]
- test_equal {[lsort $imaged]} {[lsort "$vim1 $vim2 $vim3 $zlib $pcre"]}
+ test_set {[registry::entry imaged]} {$vim1 $vim2 $vim3 $zlib $pcre}
+ test_set {[registry::entry installed]} {$vim3 $zlib}
+ test_set {[registry::entry imaged vim]} {$vim1 $vim2 $vim3}
+ test_set {[registry::entry imaged vim 7.1.000]} {$vim1}
+ test_set {[registry::entry imaged vim 7.1.002]} {$vim2 $vim3}
+ test_set {[registry::entry imaged vim 7.1.002 0 {}]} {$vim2}
+ test_set {[registry::entry imaged vim 7.1.002 0 +cscope+multibyte]} \
+ {$vim3}
- set installed [registry::entry installed]
- test_equal {[lsort $installed]} {[lsort "$vim3 $zlib"]}
+ # try searching for ports
+ # note that 7.1.2 != 7.1.002 but the VERSION collation should be smart
+ # enough to ignore the zeroes
+ test_set {[registry::entry search name vim version 7.1.2]} {$vim2 $vim3}
+ test_set {[registry::entry search variants {}]} {$vim2 $zlib}
+ test_set {[registry::entry search -glob name vi*]} {$vim1 $vim2 $vim3}
+ test_set {[registry::entry search -regexp name {zlib|pcre}]} \
+ {$zlib $pcre}
}
+ # try mapping files and checking their owners
+ registry::write {
- # try searching for ports
- # note that 7.1.2 != 7.1.002 but the VERSION collation should be smart
- # enough to ignore the zeroes
- set vim71002 [registry::entry search name vim version 7.1.2]
- test_equal {[lsort $vim71002]} {[lsort "$vim2 $vim3"]}
+ test_equal {[registry::entry owner /opt/local/bin/vimtutor]} {}
+ test_equal {[$vim3 files]} {}
- set no_variants [registry::entry search variants {}]
- test_equal {[lsort $no_variants]} {[lsort "$vim2 $zlib"]}
+ $vim1 map {}
+ $vim1 map /opt/local/bin/vim
+ $vim1 map [list /opt/local/bin/vimdiff /opt/local/bin/vimtutor]
+ $vim2 map [$vim1 imagefiles]
+ $vim3 map [$vim1 imagefiles]
+ test_equal {[registry::entry owner /opt/local/bin/vimtutor]} {}
+ test_equal {[$vim3 files]} {}
- set vistar [registry::entry search -glob name vi*]
- test_equal {[lsort $vistar]} {[lsort "$vim1 $vim2 $vim3"]}
-
- set zlibpcre [registry::entry search -regexp name {zlib|pcre}]
- test_equal {[lsort $zlibpcre]} {[lsort "$zlib $pcre"]}
-
- # try mapping files and checking their owners
- registry::write {
- $vim3 map [list /opt/local/bin/vim]
- $vim3 map [list /opt/local/bin/vimdiff /opt/local/bin/vimtutor]
+ $vim3 activate [$vim3 imagefiles]
test_equal {[registry::entry owner /opt/local/bin/vimtutor]} {$vim3}
test_equal {[registry::entry owner /opt/local/bin/emacs]} {}
- # don't have to sort because order is defined as alpha
- test_equal {[$vim3 files]} {[list /opt/local/bin/vim \
- /opt/local/bin/vimdiff /opt/local/bin/vimtutor]}
- test_equal {[$zlib files]} {[list]}
+ test_set {[$vim3 imagefiles]} {/opt/local/bin/vim \
+ /opt/local/bin/vimdiff /opt/local/bin/vimtutor}
+ test_set {[$vim3 files]} [$vim3 imagefiles]
+ test_set {[$zlib imagefiles]} {}
+ # try activating over files
+ test_throws {$vim2 activate [$vim2 imagefiles]} registry::already-active
+
# try unmapping and remapping
- $vim3 unmap {/opt/local/bin/vim}
+ $vim3 unmap /opt/local/bin/vimtutor
+ test_equal {[registry::entry owner /opt/local/bin/vimtutor]} {}
+
+ $vim3 deactivate /opt/local/bin/vim
test_equal {[registry::entry owner /opt/local/bin/vim]} {}
- $vim3 map {/opt/local/bin/vim}
+ $vim3 unmap /opt/local/bin/vim
+ test_equal {[registry::entry owner /opt/local/bin/vim]} {}
+ $vim3 map /opt/local/bin/vim
+ test_equal {[registry::entry owner /opt/local/bin/vim]} {}
+ $vim3 activate /opt/local/bin/vim
+ puts [$vim3 files]
+ puts [registry::entry owner /opt/local/bin/vim]
test_equal {[registry::entry owner /opt/local/bin/vim]} {$vim3}
+ # activate to a different location
+ $vim3 deactivate /opt/local/bin/vimdiff
+ $vim3 activate /opt/local/bin/vimdiff /opt/local/bin/vimdiff.0
+ $vim2 activate /opt/local/bin/vimdiff
+ test_set {[$vim3 files]} {/opt/local/bin/vim /opt/local/bin/vimdiff.0}
+ test_set {[$vim3 imagefiles]} {/opt/local/bin/vim \
+ /opt/local/bin/vimdiff}
+ test_equal {[registry::entry owner /opt/local/bin/vimdiff]} {$vim2}
+ test_equal {[registry::entry owner /opt/local/bin/vimdiff.0]} {$vim3}
+
# make sure you can't unmap a file you don't own
test_throws {$zlib unmap [list /opt/local/bin/vim]} registry::invalid
test_throws {$zlib unmap [list /opt/local/bin/emacs]} registry::invalid
}
+ test_set {[$vim3 imagefiles]} {/opt/local/bin/vim /opt/local/bin/vimdiff}
+ test_set {[$vim3 files]} {/opt/local/bin/vim /opt/local/bin/vimdiff.0}
+
# try some deletions
- test_equal {[registry::entry installed zlib]} {$zlib}
- test_equal {[registry::entry imaged pcre]} {$pcre}
+ test_set {[registry::entry installed zlib]} {$zlib}
+ test_set {[registry::entry imaged pcre]} {$pcre}
# try rolling a deletion back
registry::write {
@@ -110,7 +148,7 @@
# try actually deleting something
registry::entry delete $pcre
- test_throws {registry::entry open pcre 7.1 1 {utf8 +} 0} \
+ test_throws {registry::entry open pcre 7.1 1 +utf8 0} \
registry::not-found
test {![registry::entry exists $pcre]}
@@ -135,12 +173,12 @@
test {[registry::entry exists $zlib]}
# check that pcre is still gone
- test_throws {registry::entry open pcre 7.1 1 {utf8 +} 0} \
+ test_throws {registry::entry open pcre 7.1 1 +utf8 0} \
registry::not-found
registry::close
- file delete [glob -nocomplain test.db*]
+ file delete test.db
}
source tests/common.tcl
Modified: trunk/base/src/registry2.0/util.c
===================================================================
--- trunk/base/src/registry2.0/util.c 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/registry2.0/util.c 2007-08-18 15:59:59 UTC (rev 28029)
@@ -35,6 +35,7 @@
#include <tcl.h>
#include "util.h"
+#include "entryobj.h"
/**
* Generates a unique proc name starting with prefix.
@@ -158,6 +159,28 @@
}
/**
+ * Sets a given name to be an entry object.
+ *
+ * @param [in] interp Tcl interpreter to create the entry within
+ * @param [in] name name to associate the given entry with
+ * @param [in] entry entry to associate with the given name
+ * @param [out] errPtr description of error if it couldn't be set
+ * @return true if success; false if failure
+ * @see set_object
+ */
+int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
+ reg_error* errPtr) {
+ if (set_object(interp, name, entry, "entry", entry_obj_cmd, NULL,
+ errPtr)) {
+ int size = strlen(name) + 1;
+ entry->proc = malloc(size*sizeof(char));
+ memcpy(entry->proc, name, size);
+ return 1;
+ }
+ return 0;
+}
+
+/**
* Reports a sqlite3 error to Tcl.
*
* Queries the database for the most recent error message and sets it as the
@@ -213,6 +236,15 @@
return TCL_ERROR;
}
+const char* string_or_null(Tcl_Obj* obj) {
+ const char* string = Tcl_GetString(obj);
+ if (string[0] == '\0') {
+ return NULL;
+ } else {
+ return string;
+ }
+}
+
int recast(void* userdata, cast_function* fn, free_function* del, void*** outv,
void** inv, int inc, reg_error* errPtr) {
void** result = malloc(inc*sizeof(void*));
@@ -232,13 +264,33 @@
return 1;
}
+int entry_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_entry* entry,
+ reg_error* errPtr) {
+ if (entry->proc == NULL) {
+ char* name = unique_name(interp, "registry::entry");
+ if (!set_entry(interp, name, entry, errPtr)) {
+ free(name);
+ return 0;
+ }
+ free(name);
+ }
+ *obj = Tcl_NewStringObj(entry->proc, -1);
+ return 1;
+}
+
+int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
+ reg_entry** entries, int entry_count, reg_error* errPtr) {
+ return recast(interp, (cast_function*)entry_to_obj, NULL, (void***)objs,
+ (void**)entries, entry_count, errPtr);
+}
+
static int obj_to_string(void* userdata UNUSED, char** string, Tcl_Obj* obj,
reg_error* errPtr UNUSED) {
*string = Tcl_GetString(obj);
return 1;
}
-int list_obj_to_string(char*** strings, const Tcl_Obj** objv, int objc,
+int list_obj_to_string(char*** strings, Tcl_Obj** objv, int objc,
reg_error* errPtr) {
return recast(NULL, (cast_function*)obj_to_string, NULL, (void***)strings,
(void**)objv, objc, errPtr);
@@ -254,7 +306,7 @@
Tcl_DecrRefCount(obj);
}
-int list_string_to_obj(Tcl_Obj*** objv, const char** strings, int objc,
+int list_string_to_obj(Tcl_Obj*** objv, char** strings, int objc,
reg_error* errPtr) {
return recast(NULL, (cast_function*)string_to_obj, (free_function*)free_obj,
(void***)objv, (void**)strings, objc, errPtr);
Modified: trunk/base/src/registry2.0/util.h
===================================================================
--- trunk/base/src/registry2.0/util.h 2007-08-18 15:08:33 UTC (rev 28028)
+++ trunk/base/src/registry2.0/util.h 2007-08-18 15:59:59 UTC (rev 28029)
@@ -36,6 +36,7 @@
#include <sqlite3.h>
#include <cregistry/registry.h>
+#include <cregistry/entry.h>
typedef struct {
char* option;
@@ -53,6 +54,8 @@
Tcl_ObjCmdProc* proc, reg_error* errPtr);
int set_object(Tcl_Interp* interp, char* name, void* value, char* type,
Tcl_ObjCmdProc* proc, Tcl_CmdDeleteProc* deleteProc, reg_error* errPtr);
+int set_entry(Tcl_Interp* interp, char* name, reg_entry* entry,
+ reg_error* errPtr);
void set_sqlite_result(Tcl_Interp* interp, sqlite3* db, const char* query);
@@ -61,14 +64,21 @@
int all_objects(Tcl_Interp* interp, sqlite3* db, char* query, char* prefix,
set_object_function* setter);
+const char* string_or_null(Tcl_Obj* obj);
+
int recast(void* userdata, cast_function* fn, free_function* del, void*** outv,
void** inv, int inc, reg_error* errPtr);
+int entry_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_entry* entry,
+ reg_error* errPtr);
+int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
+ reg_entry** entries, int entry_count, reg_error* errPtr);
+
void free_strings(void* userdata UNUSED, char** strings, int count);
-int list_obj_to_string(char*** strings, const Tcl_Obj** objv, int objc,
+int list_obj_to_string(char*** strings, Tcl_Obj** objv, int objc,
reg_error* errPtr);
-int list_string_to_obj(Tcl_Obj*** objv, const char** strings, int objc,
+int list_string_to_obj(Tcl_Obj*** objv, char** strings, int objc,
reg_error* errPtr);
#endif /* _UTIL_H */
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20070818/1b3e68fb/attachment.html
More information about the macports-changes
mailing list