[27527] trunk/base/src

source_changes at macosforge.org source_changes at macosforge.org
Mon Aug 6 13:13:19 PDT 2007


Revision: 27527
          http://trac.macosforge.org/projects/macports/changeset/27527
Author:   sfiera at macports.org
Date:     2007-08-06 13:13:19 -0700 (Mon, 06 Aug 2007)

Log Message:
-----------
Documentation; some memory fixes

Modified Paths:
--------------
    trunk/base/src/cregistry/entry.c
    trunk/base/src/cregistry/registry.c
    trunk/base/src/cregistry/sql.c
    trunk/base/src/registry2.0/entryobj.c
    trunk/base/src/registry2.0/registry.c
    trunk/base/src/registry2.0/tests/entry.tcl
    trunk/base/src/registry2.0/util.c

Modified: trunk/base/src/cregistry/entry.c
===================================================================
--- trunk/base/src/cregistry/entry.c	2007-08-06 20:13:12 UTC (rev 27526)
+++ trunk/base/src/cregistry/entry.c	2007-08-06 20:13:19 UTC (rev 27527)
@@ -39,12 +39,15 @@
 #include <cregistry/sql.h>
 
 /**
- * Concatenates `src` to string `dst`.
+ * Concatenates `src` to string `dst`. Simple concatenation. Only guaranteed to
+ * work with strings that have been allocated with `malloc`. Amortizes cost of
+ * expanding string buffer for O(N) concatenation and such. Uses `memcpy` in
+ * favor of `strcpy` in hopes it will perform a bit better.
  *
- * Simple concatenation. Only guaranteed to work with strings that have been
- * allocated with `malloc`. Amortizes cost of expanding string buffer for O(N)
- * concatenation and such. Uses `memcpy` in favor of `strcpy` in hopes it will
- * perform a bit better.
+ * @param [in,out] dst       a reference to a null-terminated string
+ * @param [in,out] dst_len   number of characters currently in `dst`
+ * @param [in,out] dst_space number of characters `dst` can hold
+ * @param [in] src           string to concatenate to `dst`
  */
 void reg_strcat(char** dst, int* dst_len, int* dst_space, char* src) {
     int src_len = strlen(src);
@@ -64,18 +67,20 @@
 }
 
 /**
- * Appends `src` to the list `dst`.
+ * Appends element `src` to the list `dst`. It's like `reg_strcat`, except `src`
+ * represents a single element and not a sequence of `char`s.
  *
- * It's like `reg_strcat`, except `src` represents an element and not a sequence
- * of `char`s.
+ * @param [in,out] dst       a reference to a list of pointers
+ * @param [in,out] dst_len   number of elements currently in `dst`
+ * @param [in,out] dst_space number of elements `dst` can hold
+ * @param [in] src           elements to append to `dst`
  */
 static void reg_listcat(void*** dst, int* dst_len, int* dst_space, void* src) {
     if (*dst_len == *dst_space) {
         void** old_dst = *dst;
-        void** new_dst = malloc(*dst_space * 2 * sizeof(void*));
         *dst_space *= 2;
-        memcpy(new_dst, old_dst, *dst_len);
-        *dst = new_dst;
+        *dst = malloc(*dst_space * sizeof(void*));
+        memcpy(*dst, old_dst, *dst_len);
         free(old_dst);
     }
     (*dst)[*dst_len] = src;
@@ -84,6 +89,10 @@
 
 /**
  * Returns the operator to use for the given strategy.
+ *
+ * @param [in] strategy a strategy (one of the `reg_strategy_*` constants)
+ * @param [out] errPtr  on error, a description of the error that occurred
+ * @return              a sqlite3 operator if successful; NULL otherwise
  */
 static char* reg_strategy_op(reg_strategy strategy, reg_error* errPtr) {
     switch (strategy) {
@@ -94,7 +103,7 @@
         case reg_strategy_regexp:
             return " REGEXP ";
         default:
-            errPtr->code = "registry::invalid-strategy";
+            errPtr->code = "registry::invalid";
             errPtr->description = "invalid matching strategy specified";
             errPtr->free = NULL;
             return NULL;
@@ -103,8 +112,14 @@
 
 /**
  * Converts a `sqlite3_stmt` into a `reg_entry`. The first column of the stmt's
- * row must be the id of an entry; the second either `SQLITE_NUL`L or the
+ * row must be the id of an entry; the second either `SQLITE_NULL` or the
  * address of the entry in memory.
+ *
+ * @param [in] userdata sqlite3 database
+ * @param [out] entry   entry described by `stmt`
+ * @param [in] stmt     `sqlite3_stmt` with appropriate columns
+ * @param [out] errPtr  unused, since this function doesn't fail
+ * @return              true, since this function doesn't fail
  */
 static int reg_stmt_to_entry(void* userdata, void** entry, void* stmt,
         reg_error* errPtr UNUSED) {
@@ -126,6 +141,11 @@
  * table `entries`. These addresses will be retrieved by anything else that
  * needs to get entries, so only one `reg_entry` will exist in memory for any
  * given id. They will be freed when the registry is closed.
+ *
+ * @param [in] db          sqlite3 database to save entries into
+ * @param [in] entries     list of entries to save
+ * @param [in] entry_count number of entries to save
+ * @param [out] errPtr     on error, a description of the error that occurred
  */
 static int reg_save_addresses(sqlite3* db, reg_entry** entries,
         int entry_count, reg_error* errPtr) {
@@ -168,8 +188,6 @@
 }
 
 /**
- * registry::entry create portname version revision variants epoch ?name?
- *
  * Unlike the old registry::new_entry, revision, variants, and epoch are all
  * required. That's OK because there's only one place this function is called,
  * and it's called with all of them there.
@@ -219,6 +237,8 @@
     }
 }
 
+/**
+ */
 reg_entry* reg_entry_open(reg_registry* reg, char* name, char* version,
         char* revision, char* variants, char* epoch, reg_error* errPtr) {
     sqlite3_stmt* stmt;
@@ -265,7 +285,7 @@
 }
 
 /**
- * deletes an entry; still needs to be freed
+ * Deletes and frees an entry.
  */
 int reg_entry_delete(reg_registry* reg, reg_entry* entry, reg_error* errPtr) {
     sqlite3_stmt* stmt;
@@ -284,9 +304,11 @@
                     && (sqlite3_step(stmt) == SQLITE_DONE)) {
                 sqlite3_finalize(stmt);
                 return 1;
+            } else {
+                reg_sqlite_error(reg->db, errPtr, query);
             }
         } else {
-            errPtr->code = "registry::invalid-entry";
+            errPtr->code = "registry::invalid";
             errPtr->description = "an invalid entry was passed";
             errPtr->free = NULL;
         }
@@ -396,7 +418,7 @@
     if (result > 0) {
         if (!reg_save_addresses(reg->db, *entries, result, errPtr)) {
             free(entries);
-            return 0;
+            return -1;
         }
     }
     free(query);
@@ -437,7 +459,7 @@
     if (result > 0) {
         if (!reg_save_addresses(reg->db, *entries, result, errPtr)) {
             free(entries);
-            return 0;
+            return -1;
         }
     }
     sqlite3_free(query);
@@ -473,7 +495,7 @@
     if (result > 0) {
         if (!reg_save_addresses(reg->db, *entries, result, errPtr)) {
             free(entries);
-            return 0;
+            return -1;
         }
     }
     sqlite3_free(query);
@@ -529,20 +551,24 @@
             case SQLITE_ROW:
                 *value = strdup((const char*)sqlite3_column_text(stmt, 0));
                 sqlite3_finalize(stmt);
+                sqlite3_free(query);
                 return 1;
             case SQLITE_DONE:
-                errPtr->code = "registry::invalid-entry";
+                errPtr->code = "registry::invalid";
                 errPtr->description = "an invalid entry was passed";
                 errPtr->free = NULL;
                 sqlite3_finalize(stmt);
+                sqlite3_free(query);
                 return 0;
             default:
                 reg_sqlite_error(reg->db, errPtr, query);
                 sqlite3_finalize(stmt);
+                sqlite3_free(query);
                 return 0;
         }
     } else {
         reg_sqlite_error(reg->db, errPtr, query);
+        sqlite3_free(query);
         return 0;
     }
 }
@@ -560,6 +586,7 @@
         int r = sqlite3_step(stmt);
         switch (r) {
             case SQLITE_DONE:
+                sqlite3_free(query);
                 sqlite3_finalize(stmt);
                 return 1;
             default:
@@ -569,15 +596,18 @@
                         errPtr->description = "a constraint was disobeyed";
                         errPtr->free = NULL;
                         sqlite3_finalize(stmt);
+                        sqlite3_free(query);
                         return 0;
                     default:
                         reg_sqlite_error(reg->db, errPtr, query);
                         sqlite3_finalize(stmt);
+                        sqlite3_free(query);
                         return 0;
                 }
         }
     } else {
         reg_sqlite_error(reg->db, errPtr, query);
+        sqlite3_free(query);
         return 0;
     }
 }
@@ -637,7 +667,7 @@
                 switch (r) {
                     case SQLITE_DONE:
                         if (sqlite3_changes(reg->db) == 0) {
-                            errPtr->code = "registry::not-owned";
+                            errPtr->code = "registry::invalid";
                             errPtr->description = "this entry does not own the "
                                 "given file";
                             errPtr->free = NULL;

Modified: trunk/base/src/cregistry/registry.c
===================================================================
--- trunk/base/src/cregistry/registry.c	2007-08-06 20:13:12 UTC (rev 27526)
+++ trunk/base/src/cregistry/registry.c	2007-08-06 20:13:19 UTC (rev 27527)
@@ -40,6 +40,13 @@
 #include <cregistry/entry.h>
 #include <cregistry/sql.h>
 
+/**
+ * Destroys a `reg_error` object. This should be called on any reg_error when a
+ * registry function returns a failure condition; depending on the function,
+ * failure could be false, negative, or null.
+ *
+ * @param [in] errPtr the error to destroy
+ */
 void reg_error_destruct(reg_error* errPtr) {
     if (errPtr->free) {
         errPtr->free(errPtr->description);
@@ -47,7 +54,13 @@
 }
 
 /**
- * Sets `errPtr` according to the last error in `db`.
+ * Sets `errPtr` according to the last error in `db`. Convenience function for
+ * internal use only. Sets the error code to "registry::sqlite-error" and sets
+ * an appropriate error description (including a query if non-NULL).
+ *
+ * @param [in] db      sqlite3 database connection which had the error
+ * @param [out] errPtr an error to write to
+ * @param [in] query   the query that this error occurred during
  */
 void reg_sqlite_error(sqlite3* db, reg_error* errPtr, char* query) {
     errPtr->code = "registry::sqlite-error";
@@ -100,8 +113,8 @@
         free(reg);
         return 1;
     } else {
-        errPtr->code = "registry::not-closed";
-        errPtr->description = sqlite3_mprintf("error: registry db not closed "
+        errPtr->code = "registry::sqlite-error";
+        errPtr->description = sqlite3_mprintf("registry db not closed "
                 "correctly (%s)\n", sqlite3_errmsg(reg->db));
         errPtr->free = (reg_error_destructor*)sqlite3_free;
         return 0;
@@ -123,7 +136,7 @@
     int needsInit = 0; /* registry doesn't yet exist */
     int canWrite = 1; /* can write to this location */
     if (reg->status & reg_attached) {
-        errPtr->code = "registry::already-attached";
+        errPtr->code = "registry::misuse";
         errPtr->description = "a database is already attached to this registry";
         errPtr->free = NULL;
         return 0;
@@ -141,6 +154,7 @@
         if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
                 && (sqlite3_step(stmt) == SQLITE_DONE)) {
             sqlite3_finalize(stmt);
+            sqlite3_free(query);
             if (!needsInit || (create_tables(reg->db, errPtr))) {
                 reg->status |= reg_attached;
                 return 1;
@@ -148,6 +162,7 @@
         } else {
             reg_sqlite_error(reg->db, errPtr, query);
             sqlite3_finalize(stmt);
+            sqlite3_free(query);
         }
     } else {
         errPtr->code = "registry::cannot-init";
@@ -170,7 +185,7 @@
     sqlite3_stmt* stmt;
     char* query = "DETACH DATABASE registry";
     if (!(reg->status & reg_attached)) {
-        errPtr->code = "registry::not-attached";
+        errPtr->code = "registry::misuse";
         errPtr->description = "no database is attached to this registry";
         errPtr->free = NULL;
         return 0;
@@ -218,9 +233,12 @@
     }
 }
 
+/**
+ * Helper function for `reg_start_read` and `reg_start_write`.
+ */
 static int reg_start(reg_registry* reg, const char* query, reg_error* errPtr) {
     if (reg->status & reg_transacting) {
-        errPtr->code = "registry::cant-start";
+        errPtr->code = "registry::misuse";
         errPtr->description = "couldn't start transaction because a "
             "transaction is already open";
         errPtr->free = NULL;
@@ -238,6 +256,15 @@
     }
 }
 
+/**
+ * Starts a read transaction on registry. This acquires a shared lock on the
+ * database. It must be released with `reg_commit` or `reg_rollback` (it doesn't
+ * actually matter which, since the transaction won't have changed any values).
+ *
+ * @param [in] reg     registry to start transaction on
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
+ */
 int reg_start_read(reg_registry* reg, reg_error* errPtr) {
     if (reg_start(reg, "BEGIN", errPtr)) {
         reg->status |= reg_transacting;
@@ -247,6 +274,14 @@
     }
 }
 
+/**
+ * Starts a write transaction on registry. This acquires an exclusive lock on
+ * the database. It must be released with `reg_commit` or `reg_rollback`.
+ *
+ * @param [in] reg     registry to start transaction on
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
+ */
 int reg_start_write(reg_registry* reg, reg_error* errPtr) {
     if (reg_start(reg, "BEGIN EXCLUSIVE", errPtr)) {
         reg->status |= reg_transacting | reg_can_write;
@@ -256,9 +291,12 @@
     }
 }
 
+/**
+ * Helper function for `reg_commit` and `reg_rollback`.
+ */
 static int reg_end(reg_registry* reg, const char* query, reg_error* errPtr) {
     if (!(reg->status & reg_transacting)) {
-        errPtr->code = "registry::cant-end";
+        errPtr->code = "registry::misuse";
         errPtr->description = "couldn't end transaction because no transaction "
             "is open";
         errPtr->free = NULL;
@@ -276,6 +314,14 @@
     }
 }
 
+/**
+ * Commits the current transaction. All values written since `reg_start_*` was
+ * called will be written to the database.
+ *
+ * @param [in] reg     registry to commit transaction to
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
+ */
 int reg_commit(reg_registry* reg, reg_error* errPtr) {
     if (reg_end(reg, "COMMIT", errPtr)) {
         reg->status &= ~(reg_transacting | reg_can_write);
@@ -285,6 +331,14 @@
     }
 }
 
+/**
+ * Rolls back the current transaction. All values written since `reg_start_*`
+ * was called will be reverted, and no changes will be written to the database.
+ *
+ * @param [in] reg     registry to roll back transaction from
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
+ */
 int reg_rollback(reg_registry* reg, reg_error* errPtr) {
     if (reg_end(reg, "ROLLBACK", errPtr)) {
         reg->status &= ~(reg_transacting | reg_can_write);
@@ -294,11 +348,20 @@
     }
 }
 
+/**
+ * Ensures the registry has a write transaction open. If it doesn't, returns
+ * false and sets an appropriate error. Mainly intended for internal use, though
+ * there's no reason it couldn't be used externally.
+ *
+ * @param [in] reg     registry to check writability of
+ * @param [out] errPtr if not writable, an error describing that situation
+ * @return             true if writable; false if not
+ */
 int reg_test_writable(reg_registry* reg, reg_error* errPtr) {
     if (reg->status & reg_can_write) {
         return 1;
     } else {
-        errPtr->code = "registry::no-write";
+        errPtr->code = "registry::misuse";
         errPtr->description = "a write transaction has not been started";
         errPtr->free = NULL;
         return 0;

Modified: trunk/base/src/cregistry/sql.c
===================================================================
--- trunk/base/src/cregistry/sql.c	2007-08-06 20:13:12 UTC (rev 27526)
+++ trunk/base/src/cregistry/sql.c	2007-08-06 20:13:19 UTC (rev 27527)
@@ -40,10 +40,14 @@
 #include <cregistry/sql.h>
 
 /**
- * Executes a null-terminated list of queries.
+ * Executes a null-terminated list of queries. Pass it a list of queries, it'll
+ * execute them. This is mainly intended for initialization, when you have a
+ * number of standard queries to execute.
  *
- * Pass it a list of queries, it'll execute them. This is mainly intended for
- * initialization, when you have a number of standard queries to execute.
+ * @param [in] db      database to execute queries on
+ * @param [in] queries NULL-terminated list of queries
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
  */
 int do_queries(sqlite3* db, char** queries, reg_error* errPtr) {
     char** query;
@@ -61,13 +65,15 @@
 }
 
 /**
- * REGEXP function for sqlite3.
+ * REGEXP function for sqlite3. Takes two arguments; the first is the value and
+ * the second the pattern. If the pattern is invalid, errors out. Otherwise,
+ * returns true if the value matches the pattern and false otherwise.
  *
- * Takes two arguments; the first is the value and the second the pattern. If
- * the pattern is invalid, errors out. Otherwise, returns true if the value
- * matches the pattern and false otherwise.
+ * This function is made available in sqlite3 as the REGEXP operator.
  *
- * This function is available in sqlite3 as the REGEXP operator.
+ * @param [in] context sqlite3-defined structure
+ * @param [in] argc    number of arguments - always 2 and hence unused
+ * @param [in] argv    0: value to match; 1: pattern to match against
  */
 static void sql_regexp(sqlite3_context* context, int argc UNUSED,
         sqlite3_value** argv) {
@@ -87,15 +93,30 @@
 }
 
 /**
- * NOW function for sqlite3.
+ * NOW function for sqlite3. Takes no arguments. Returns the unix timestamp of
+ * the current time.
  *
- * Takes no arguments. Returns the unix timestamp of now.
+ * @param [in] context sqlite3-defined structure
+ * @param [in] argc    number of arguments - always 2 and hence unused
+ * @param [in] argv    0: value to match; 1: pattern to match against
  */
 static void sql_now(sqlite3_context* context, int argc UNUSED,
         sqlite3_value** argv UNUSED) {
     sqlite3_result_int(context, time(NULL));
 }
 
+/**
+ * RPM version comparison. Shamelessly copied from Pextlib, with some changes to
+ * use string lengths instead of strlen by default. That's necessary to make it
+ * work with sqlite3 collations. It should be shared with Pextlib, rather than
+ * just copied though.
+ *
+ * @param [in] versionA first version string, i.e. "1.4.1"
+ * @param [in] lengthA  length of first version string, or -1 to use strlen
+ * @param [in] versionB second version string, i.e. "1.4.2"
+ * @param [in] lengthA  length of second version string, or -1 to use strlen
+ * @return              -1 if A < B; 0 if A = B; 1 if A > B
+ */
 static int rpm_vercomp (const char *versionA, int lengthA, const char *versionB,
         int lengthB) {
     const char *endA, *endB;
@@ -210,13 +231,16 @@
 }
 
 /**
- * VERSION collation for sqlite3.
+ * VERSION collation for sqlite3. This function collates text according to
+ * pextlib's rpm-vercomp function. This allows direct comparison and sorting of
+ * version columns, such as port.version and port.revision.
  *
- * This function collates text according to pextlib's rpm-vercomp function. This
- * allows direct comparison and sorting of version columns, such as port.version
- * and port.revision.
- *
- * TODO: share rpm-vercomp properly with pextlib. Currently it's copy-pasted in.
+ * @param [in] userdata unused
+ * @param [in] alen     length of first string
+ * @param [in] a        first string
+ * @param [in] blen     length of second string
+ * @param [in] b        second string
+ * @return              -1 if a < b; 0 if a = b; 1 if a > b
  */
 static int sql_version(void* userdata UNUSED, int alen, const void* a, int blen,
         const void* b) {
@@ -224,10 +248,13 @@
 }
 
 /**
- * Creates tables in the registry.
+ * Creates tables in the registry. This function is called upon an uninitialized
+ * database to create the tables needed to record state between invocations of
+ * `port`.
  *
- * This function is called upon an uninitialized database to create the tables
- * needed to record state between invocations of `port`.
+ * @param [in] db      database with an attached registry db
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
  */
 int create_tables(sqlite3* db, reg_error* errPtr) {
     static char* queries[] = {
@@ -264,11 +291,13 @@
 }
 
 /**
- * Initializes database connection.
+ * Initializes database connection. This function creates all the temporary
+ * tables used by the registry. It also registers the user functions and
+ * collations declared here, making them available.
  *
- * This function creates all the temporary tables used by the registry. It also
- * registers the user functions and collations declared in "sql.h", making them
- * available.
+ * @param [in] db      database to initialize
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
  */
 int init_db(sqlite3* db, reg_error* errPtr) {
     static char* queries[] = {

Modified: trunk/base/src/registry2.0/entryobj.c
===================================================================
--- trunk/base/src/registry2.0/entryobj.c	2007-08-06 20:13:12 UTC (rev 27526)
+++ trunk/base/src/registry2.0/entryobj.c	2007-08-06 20:13:19 UTC (rev 27527)
@@ -77,6 +77,7 @@
             if (reg_entry_propget(reg, entry, key, &value, &error)) {
                 Tcl_Obj* result = Tcl_NewStringObj(value, -1);
                 Tcl_SetObjResult(interp, result);
+                free(value);
                 return TCL_OK;
             }
             return registry_failed(interp, &error);

Modified: trunk/base/src/registry2.0/registry.c
===================================================================
--- trunk/base/src/registry2.0/registry.c	2007-08-06 20:13:12 UTC (rev 27526)
+++ trunk/base/src/registry2.0/registry.c	2007-08-06 20:13:19 UTC (rev 27527)
@@ -119,12 +119,11 @@
         }
     }
     if ((reg->status & status) != status) {
+        Tcl_SetErrorCode(interp, "registry::misuse", NULL);
         if (status & reg_can_write) {
-            Tcl_SetErrorCode(interp, "registry::no-write", NULL);
             Tcl_SetResult(interp, "a write transaction has not been started",
                     TCL_STATIC);
         } else {
-            Tcl_SetErrorCode(interp, "registry::not-open", NULL);
             Tcl_SetResult(interp, "registry is not open", TCL_STATIC);
         }
         reg = NULL;

Modified: trunk/base/src/registry2.0/tests/entry.tcl
===================================================================
--- trunk/base/src/registry2.0/tests/entry.tcl	2007-08-06 20:13:12 UTC (rev 27526)
+++ trunk/base/src/registry2.0/tests/entry.tcl	2007-08-06 20:13:19 UTC (rev 27527)
@@ -8,12 +8,17 @@
 	file delete [glob -nocomplain test.db*]
 
     # can't use registry before it's opened
-    test_throws {registry::write {}} registry::not-open
+    test_throws {registry::write {}} registry::misuse
     registry::open test.db
 
     test_throws {registry::entry create vim 7.1.000 0 {multibyte +} 0} \
-        registry::no-write
+        registry::misuse
 
+    # no nested transactions
+    registry::write {
+        test_throws {registry::read {}} registry::misuse
+    }
+
     # write transaction
     registry::write {
 
@@ -77,9 +82,9 @@
 
         # make sure you can't unmap a file you don't own
         test_throws {$zlib unmap [list /opt/local/bin/vim]} \
-            registry::not-owned
+            registry::invalid
         test_throws {$zlib unmap [list /opt/local/bin/emacs]} \
-            registry::not-owned
+            registry::invalid
     }
 
     # delete pcre
@@ -106,7 +111,7 @@
     # close the registry; make sure the registry isn't usable after being
     # closed, then ensure state persists between open sessions
     registry::close
-    test_throws {registry::entry search} registry::not-open
+    test_throws {registry::entry search} registry::misuse
     test {![registry::entry exists $vim3]}
     registry::open test.db
 

Modified: trunk/base/src/registry2.0/util.c
===================================================================
--- trunk/base/src/registry2.0/util.c	2007-08-06 20:13:12 UTC (rev 27526)
+++ trunk/base/src/registry2.0/util.c	2007-08-06 20:13:19 UTC (rev 27527)
@@ -237,7 +237,8 @@
     int length;
     char* value = Tcl_GetStringFromObj(obj, &length);
     *string = malloc((length+1)*sizeof(char));
-    memcpy(*string, value, length+1);
+    memcpy(*string, value, length);
+    (*string)[length] = '\0';
     return 1;
 }
 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20070806/bac97641/attachment.html


More information about the macports-changes mailing list