Merge from ^/branches/gsoc11-rev-upgrade

git-svn-id: https://svn.macports.org/repository/macports/trunk/base@88376 d073be05-634f-4543-b044-5fe20cf6d1d6
This commit is contained in:
Clemens Lang
2011-12-30 00:17:49 +00:00
parent c4f3ca6622
commit e47aa992a5
42 changed files with 6717 additions and 541 deletions
Vendored
+47 -1
View File
@@ -679,6 +679,7 @@ CFLAGS_QUICHEEATERS
TAR_CMD
TAR_Q
SED_EXT
SWIG
OPEN
XAR
PAX
@@ -844,6 +845,7 @@ LZMA
XZ
XAR
OPEN
SWIG
OBJCPP
CPP'
ac_subdirs_all='src/thread2.6'
@@ -1530,6 +1532,7 @@ Some influential environment variables:
XZ path to xz command
XAR path to xar command
OPEN path to open command
SWIG path to swig command
OBJCPP Objective C preprocessor
CPP C preprocessor
@@ -5784,6 +5787,47 @@ $as_echo "no" >&6; }
fi
# Extract the first word of "swig", so it can be a program name with args.
set dummy swig; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_SWIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
ac_cv_path_SWIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_path_SWIG" && ac_cv_path_SWIG="$PATH:/usr/local/bin"
;;
esac
fi
SWIG=$ac_cv_path_SWIG
if test -n "$SWIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5
$as_echo "$SWIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Define some precious variables allowing user to override PATH for some programs
@@ -5804,6 +5848,7 @@ fi
# Extract the first word of "sed", so it can be a program name with args.
set dummy sed; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -9740,7 +9785,7 @@ prefix=$oldprefix
# Output
ac_config_files="$ac_config_files Doxyfile Makefile Mk/macports.autoconf.mk doc/base.mtree doc/prefix.mtree doc/macosx.mtree doc/macports.conf doc/pubkeys.conf portmgr/freebsd/Makefile src/Makefile src/macports1.0/macports_autoconf.tcl src/port1.0/port_autoconf.tcl src/registry2.0/registry_autoconf.tcl src/programs/Makefile src/macports1.0/macports_fastload.tcl setupenv.bash"
ac_config_files="$ac_config_files Doxyfile Makefile Mk/macports.autoconf.mk doc/base.mtree doc/prefix.mtree doc/macosx.mtree doc/macports.conf doc/pubkeys.conf portmgr/freebsd/Makefile src/Makefile src/machista1.0/Makefile src/macports1.0/macports_autoconf.tcl src/port1.0/port_autoconf.tcl src/registry2.0/registry_autoconf.tcl src/programs/Makefile src/macports1.0/macports_fastload.tcl setupenv.bash"
ac_config_files="$ac_config_files src/pkg_mkindex.sh"
@@ -10460,6 +10505,7 @@ do
"doc/pubkeys.conf") CONFIG_FILES="$CONFIG_FILES doc/pubkeys.conf" ;;
"portmgr/freebsd/Makefile") CONFIG_FILES="$CONFIG_FILES portmgr/freebsd/Makefile" ;;
"src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
"src/machista1.0/Makefile") CONFIG_FILES="$CONFIG_FILES src/machista1.0/Makefile" ;;
"src/macports1.0/macports_autoconf.tcl") CONFIG_FILES="$CONFIG_FILES src/macports1.0/macports_autoconf.tcl" ;;
"src/port1.0/port_autoconf.tcl") CONFIG_FILES="$CONFIG_FILES src/port1.0/port_autoconf.tcl" ;;
"src/registry2.0/registry_autoconf.tcl") CONFIG_FILES="$CONFIG_FILES src/registry2.0/registry_autoconf.tcl" ;;
+3
View File
@@ -126,6 +126,7 @@ AC_PATH_PROG(MKBOM, [mkbom], [])
AC_PATH_PROG(PAX, [pax], [])
AC_PATH_PROG(XAR, [xar], [])
AC_PATH_PROG(OPEN, [open], [])
AC_PATH_PROG(SWIG, [swig], [$PATH:/usr/local/bin])
# Define some precious variables allowing user to override PATH for some programs
AC_ARG_VAR(MTREE, [path to mtree command])
@@ -144,6 +145,7 @@ AC_ARG_VAR(LZMA, [path to lzma command])
AC_ARG_VAR(XZ, [path to xz command])
AC_ARG_VAR(XAR, [path to xar command])
AC_ARG_VAR(OPEN, [path to open command])
AC_ARG_VAR(SWIG, [path to swig command])
MP_SED_EXTENDED_REGEXP
MP_TAR_FAST_READ
@@ -368,6 +370,7 @@ AC_CONFIG_FILES([
doc/pubkeys.conf
portmgr/freebsd/Makefile
src/Makefile
src/machista1.0/Makefile
src/macports1.0/macports_autoconf.tcl
src/port1.0/port_autoconf.tcl
src/registry2.0/registry_autoconf.tcl
+18
View File
@@ -492,6 +492,24 @@ finally the global variants specified in variants.conf, if any. Note that
upgrade will not normally rebuild a port only to change the selected
variants; you can either specify --enforce-variants, or deactivate the port and reinstall it
with different variants.
.Pp
After the upgrade MacPorts will automatically run \fBrev-upgrade\fP to check for
broken ports that need to be rebuilt. If there are known problems with
\fBrev-upgrade\fP or other reasons why you would want to avoid running this
step, you can disable it by running \fBport upgrade\fP with the
\fB--no-rev-upgrade\fP switch:
.Pp
.Dl "port upgrade --no-rev-upgrade outdated"
.Ss rev-upgrade
Manually check for broken binaries and rebuild ports containing broken binaries.
\fBrev-upgrade\fP is usually automatically run after each upgrade, unless you
specify the \fB--no-rev-upgrade\fP option.
.Pp
\fBrev-upgrade\fP can run more checks against a special loadcommand in Mach-O
binaries that should always be referencing the file itself. This check is most
helpful for maintainers to check whether their ports have been built correctly.
It is disabled by default and can be enabled by passing \fB--id-loadcmd-check\fP
to \fBrev-upgrade\fP.
.Ss clean
Clean the files used for building
.Ar portname .
+2 -1
View File
@@ -5,7 +5,8 @@ TCLPKG= @OUR_INCLUDED_PACKAGES@ \
package1.0 \
pextlib1.0 \
registry2.0 \
darwintracelib1.0
darwintracelib1.0 \
machista1.0
SUBDIR= ${TCLPKG} port programs
all::
+1 -1
View File
@@ -1,6 +1,6 @@
# $Id$
OBJS = registry.o entry.o sql.o vercomp.o
OBJS = registry.o entry.o sql.o vercomp.o util.o file.o
STLIB_NAME = cregistry.a
RANLIB = ranlib
+10 -154
View File
@@ -34,10 +34,11 @@
#include "entry.h"
#include "registry.h"
#include "sql.h"
#include "util.h"
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>
/*
* TODO: possibly, allow reg_entry_search to take different matching strategies
@@ -53,9 +54,6 @@
* 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
@@ -68,85 +66,6 @@
* deactivated/uninstalled.
*/
/**
* 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.
*
* @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`
*/
static int reg_strcat(char** dst, size_t* dst_len, size_t* dst_space, char* src) {
size_t src_len = strlen(src);
size_t result_len = *dst_len + src_len;
if (result_len > *dst_space) {
char* new_dst;
*dst_space *= 2;
if (*dst_space < result_len) {
*dst_space = result_len;
}
new_dst = realloc(*dst, *dst_space * sizeof(char) + 1);
if (!new_dst)
return 0;
else
*dst = new_dst;
}
memcpy(*dst + *dst_len, src, src_len+1);
*dst_len = result_len;
return 1;
}
/**
* 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.
*
* @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 int reg_listcat(void*** dst, int* dst_len, int* dst_space, void* src) {
if (*dst_len == *dst_space) {
void** new_dst;
*dst_space *= 2;
new_dst = realloc(*dst, *dst_space * sizeof(void*));
if (!new_dst)
return 0;
else
*dst = new_dst;
}
(*dst)[*dst_len] = src;
(*dst_len)++;
return 1;
}
/**
* Returns an expression to use for the given strategy. This should be passed as
* the `fmt` argument of `sqlite3_mprintf`, with the key and value following.
*
* @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 expression if success; NULL if failure
*/
static char* reg_strategy_op(reg_strategy strategy, reg_error* errPtr) {
switch (strategy) {
case reg_strategy_exact:
return "%q = '%q'";
case reg_strategy_glob:
return "%q GLOB '%q'";
case reg_strategy_regexp:
return "REGEXP(%q, '%q')";
default:
errPtr->code = REG_INVALID;
errPtr->description = "invalid matching strategy specified";
errPtr->free = NULL;
return NULL;
}
}
/**
* 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_NULL` or the
@@ -159,7 +78,7 @@ static char* reg_strategy_op(reg_strategy strategy, reg_error* errPtr) {
* @return true if success; false if failure
*/
static int reg_stmt_to_entry(void* userdata, void** entry, void* stmt,
reg_error* errPtr UNUSED) {
void* calldata UNUSED, reg_error* errPtr UNUSED) {
int is_new;
reg_registry* reg = (reg_registry*)userdata;
sqlite_int64 id = sqlite3_column_int64(stmt, 0);
@@ -262,6 +181,7 @@ reg_entry* reg_entry_open(reg_registry* reg, char* name, char* version,
char* revision, char* variants, char* epoch, reg_error* errPtr) {
sqlite3_stmt* stmt = NULL;
reg_entry* entry = NULL;
int lower_bound = 0;
char* query;
if (strlen(epoch) > 0) {
query = "SELECT id FROM registry.ports WHERE name=? AND version=? "
@@ -286,7 +206,7 @@ reg_entry* reg_entry_open(reg_registry* reg, char* name, char* version,
r = sqlite3_step(stmt);
switch (r) {
case SQLITE_ROW:
reg_stmt_to_entry(reg, (void**)&entry, stmt, errPtr);
reg_stmt_to_entry(reg, (void**)&entry, stmt, &lower_bound, errPtr);
break;
case SQLITE_DONE:
errPtr->code = REG_NOT_FOUND;
@@ -415,72 +335,6 @@ void reg_entry_free(reg_entry* entry) {
free(entry);
}
/**
* Convenience method for returning all objects of a given type from the
* registry.
*
* @param [in] reg registry to select objects from
* @param [in] query the select query to execute
* @param [in] query_len length of the query (or -1 for automatic)
* @param [out] objects the objects selected
* @param [in] fn a function to convert sqlite3_stmts to the desired type
* @param [in] del a function to delete the desired type of object
* @param [out] errPtr on error, a description of the error that occurred
* @return the number of objects if success; negative if failure
*/
static int reg_all_objects(reg_registry* reg, char* query, int query_len,
void*** objects, cast_function* fn, free_function* del,
reg_error* errPtr) {
void** results = malloc(10*sizeof(void*));
int result_count = 0;
int result_space = 10;
sqlite3_stmt* stmt = NULL;
if (!results || !fn) {
return -1;
}
if (sqlite3_prepare(reg->db, query, query_len, &stmt, NULL) == SQLITE_OK) {
int r;
reg_entry* entry;
do {
r = sqlite3_step(stmt);
switch (r) {
case SQLITE_ROW:
if (fn(reg, (void**)&entry, stmt, errPtr)) {
if (!reg_listcat(&results, &result_count, &result_space, entry)) {
r = SQLITE_ERROR;
}
} else {
r = SQLITE_ERROR;
}
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);
if (r == SQLITE_DONE) {
*objects = results;
return result_count;
} else if (del) {
int i;
for (i=0; i<result_count; i++) {
del(NULL, results[i]);
}
}
} else {
if (stmt) {
sqlite3_finalize(stmt);
}
reg_sqlite_error(reg->db, errPtr, query);
}
free(results);
return -1;
}
/**
* Type-safe version of `reg_all_objects` for `reg_entry`.
*
@@ -493,8 +347,9 @@ static int reg_all_objects(reg_registry* reg, char* query, int query_len,
*/
static int reg_all_entries(reg_registry* reg, char* query, int query_len,
reg_entry*** objects, reg_error* errPtr) {
int lower_bound = 0;
return reg_all_objects(reg, query, query_len, (void***)objects,
reg_stmt_to_entry, NULL, errPtr);
reg_stmt_to_entry, &lower_bound, NULL, errPtr);
}
/**
@@ -640,6 +495,7 @@ int reg_entry_owner(reg_registry* reg, char* path, reg_entry** entry,
int result = 0;
sqlite3_stmt* stmt = NULL;
char* query = "SELECT id FROM registry.files WHERE actual_path=? AND active";
int lower_bound = 0;
if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
&& (sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC)
== SQLITE_OK)) {
@@ -649,7 +505,7 @@ int reg_entry_owner(reg_registry* reg, char* path, reg_entry** entry,
switch (r) {
case SQLITE_ROW:
result = reg_stmt_to_entry(reg, (void**)entry, stmt,
errPtr);
&lower_bound, errPtr);
break;
case SQLITE_DONE:
*entry = NULL;
-6
View File
@@ -36,12 +36,6 @@
#include <sqlite3.h>
typedef enum {
reg_strategy_exact = 1,
reg_strategy_glob = 2,
reg_strategy_regexp = 3
} reg_strategy;
typedef struct {
sqlite_int64 id; /* rowid in database */
reg_registry* reg; /* associated registry */
+353
View File
@@ -0,0 +1,353 @@
/*
* file.c
* vim:tw=80:expandtab
* $Id$
*
* Copyright (c) 2011 Clemens Lang <cal@macports.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "file.h"
#include "util.h"
#include "registry.h"
#include "sql.h"
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>
/**
* Converts a `sqlite3_stmt` into a `reg_file`. The first column of the stmt's
* row must be the id of a file; the second column must be the path of a file;
* the third either `SQLITE_NULL` or the address of the entry in memory.
*
* @param [in] userdata sqlite3 database
* @param [out] file file described by `stmt`
* @param [in] stmt `sqlite3_stmt` with appropriate columns
* @param [out] errPtr unused
* @return true if success; false if failure
*/
static int reg_stmt_to_file(void* userdata, void** file, void* stmt,
void* calldata UNUSED, reg_error* errPtr UNUSED) {
int is_new;
reg_registry* reg = (reg_registry*)userdata;
reg_file_pk key;
Tcl_HashEntry* hash;
char* hashkey;
key.id = sqlite3_column_int64(stmt, 0);
key.path = strdup((const char*) sqlite3_column_text(stmt, 1));
if (!key.path) {
return 0;
}
hashkey = sqlite3_mprintf("%lld:%s", key.id, key.path);
if (!hashkey) {
free(key.path);
return 0;
}
hash = Tcl_CreateHashEntry(&reg->open_files,
hashkey, &is_new);
sqlite3_free(hashkey);
if (is_new) {
reg_file* f = malloc(sizeof(reg_file));
if (!f) {
free(key.path);
return 0;
}
f->reg = reg;
f->key = key;
f->proc = NULL;
*file = f;
Tcl_SetHashValue(hash, f);
} else {
free(key.path);
*file = Tcl_GetHashValue(hash);
}
return 1;
}
/**
* Opens an existing file in the registry.
*
* @param [in] reg registry to open entry in
* @param [in] id port id in the dabatase
* @param [in] path file path in the database
* @param [out] errPtr on error, a description of the error that occures
* @return the file if success, NULL if failure
*/
reg_file* reg_file_open(reg_registry* reg, char* id, char* name,
reg_error* errPtr) {
sqlite3_stmt* stmt = NULL;
reg_file* file = NULL;
char* query = "SELECT id, path FROM registry.files WHERE id=? AND path=?";
int lower_bound = 0;
if ((sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
&& (sqlite3_bind_text(stmt, 1, id, -1, SQLITE_STATIC)
== SQLITE_OK)
&& (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC)
== SQLITE_OK)) {
int r;
do {
r = sqlite3_step(stmt);
switch (r) {
case SQLITE_ROW:
reg_stmt_to_file(reg, (void**)&file, stmt, &lower_bound,
errPtr);
break;
case SQLITE_DONE:
errPtr->code = REG_NOT_FOUND;
errPtr->description = sqlite3_mprintf("no matching file found for: "
"id=%s, name=%s", id, name);
errPtr->free = (reg_error_destructor*) sqlite3_free;
break;
case SQLITE_BUSY:
continue;
default:
reg_sqlite_error(reg->db, errPtr, query);
break;
}
} while (r == SQLITE_BUSY);
} else {
reg_sqlite_error(reg->db, errPtr, query);
}
if (stmt) {
sqlite3_finalize(stmt);
}
return file;
}
/**
* Type-safe version of `reg_all_objects` for `reg_file`.
*
* @param [in] reg registry to select entries from
* @param [in] query the select query to execute
* @param [in] query_len length of the query (or -1 for automatic)
* @param [out] objects the files selected
* @param [out] errPtr on error, a description of the error that occurred
* @return the number of entries if success; negative if failure
*/
static int reg_all_files(reg_registry* reg, char* query, int query_len,
reg_file*** objects, reg_error* errPtr) {
int lower_bound = 0;
return reg_all_objects(reg, query, query_len, (void***)objects,
reg_stmt_to_file, &lower_bound, NULL, errPtr);
}
/**
* Searches the registry for files for which each key's value is equal to the
* given value. To find all files, pass a key_count of 0.
*
* Bad keys should cause sqlite3 errors but not permit SQL injection attacks.
* Pass it good keys anyway.
*
* @param [in] reg registry to search in
* @param [in] keys a list of keys to search by
* @param [in] vals a list of values to search by, matching keys
* @param [in] strats a list of strategies to use when searching
* @param [in] key_count the number of key/value pairs passed
* @param [out] files a list of matching files
* @param [out] errPtr on error, a description of the error that occurred
* @return the number of entries if success; false if failure
*/
int reg_file_search(reg_registry* reg, char** keys, char** vals, int* strats,
int key_count, reg_file*** files, reg_error* errPtr) {
int i;
char* kwd = " WHERE ";
char* query;
size_t query_len, query_space;
int result;
/* build the query */
query = strdup("SELECT id, path FROM registry.files");
if (!query) {
return -1;
}
query_len = query_space = strlen(query);
for (i = 0; i < key_count; i++) {
char* op;
char* cond;
/* get the strategy */
if ((op = reg_strategy_op(strats[i], errPtr)) == NULL) {
free(query);
return -1;
}
cond = sqlite3_mprintf(op, keys[i], vals[i]);
if (!cond || !reg_strcat(&query, &query_len, &query_space, kwd)
|| !reg_strcat(&query, &query_len, &query_space, cond)) {
free(query);
return -1;
}
sqlite3_free(cond);
kwd = " AND ";
}
/* do the query */
result = reg_all_files(reg, query, -1, files, errPtr);
free(query);
return result;
}
/**
* Gets a named property of a file. That property can be set using
* `reg_file_propset`. The property named must be one that exists in the table
* and must not be one with internal meaning such as `id`.
*
* @param [in] file file to get property from
* @param [in] key property to get
* @param [out] value the value of the property
* @param [out] errPtr on error, a description of the error that occurred
* @return true if success; false if failure
*/
int reg_file_propget(reg_file* file, char* key, char** value,
reg_error* errPtr) {
reg_registry* reg = file->reg;
int result = 0;
sqlite3_stmt* stmt = NULL;
char* query;
const char *text;
query = sqlite3_mprintf("SELECT %q FROM registry.files WHERE id=%lld "
"AND path='%q'", key, file->key.id, file->key.path);
if (sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
int r;
do {
r = sqlite3_step(stmt);
switch (r) {
case SQLITE_ROW:
text = (const char*)sqlite3_column_text(stmt, 0);
if (text) {
*value = strdup(text);
result = 1;
} else {
reg_sqlite_error(reg->db, errPtr, query);
}
break;
case SQLITE_DONE:
errPtr->code = REG_INVALID;
errPtr->description = "an invalid file was passed";
errPtr->free = NULL;
break;
case SQLITE_BUSY:
continue;
default:
reg_sqlite_error(reg->db, errPtr, query);
break;
}
} while (r == SQLITE_BUSY);
} else {
reg_sqlite_error(reg->db, errPtr, query);
}
if (stmt) {
sqlite3_finalize(stmt);
}
sqlite3_free(query);
return result;
}
/**
* Sets a named property of a file. That property can be later retrieved using
* `reg_file_propget`. The property named must be one that exists in the table
* and must not be one with internal meaning such as `id`.
*
* @param [in] file file to set property for
* @param [in] key property to set
* @param [in] value the desired value of the property
* @param [out] errPtr on error, a description of the error that occurred
* @return true if success; false if failure
*/
int reg_file_propset(reg_file* file, char* key, char* value,
reg_error* errPtr) {
reg_registry* reg = file->reg;
int result = 0;
sqlite3_stmt* stmt = NULL;
char* query;
query = sqlite3_mprintf("UPDATE registry.files SET %q = '%q' WHERE id=%lld "
"AND path='%q'", key, value, file->key.id, file->key.path);
if (sqlite3_prepare(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
int r;
do {
r = sqlite3_step(stmt);
switch (r) {
case SQLITE_DONE:
result = 1;
break;
case SQLITE_BUSY:
continue;
default:
if (sqlite3_reset(stmt) == SQLITE_CONSTRAINT) {
errPtr->code = REG_CONSTRAINT;
errPtr->description = "a constraint was disobeyed";
errPtr->free = NULL;
} else {
reg_sqlite_error(reg->db, errPtr, query);
}
break;
}
} while (r == SQLITE_BUSY);
} else {
reg_sqlite_error(reg->db, errPtr, query);
}
if (stmt) {
sqlite3_finalize(stmt);
}
sqlite3_free(query);
return result;
}
/**
* Fetches a list of all open files
*
* @param [in] reg registry to fetch files from
* @param [out] files a list of open files
* @return the number of open entries, -1 on error
*/
int reg_all_open_files(reg_registry* reg, reg_file*** files) {
reg_file* file;
int file_count = 0;
int file_space = 10;
Tcl_HashEntry* hash;
Tcl_HashSearch search;
*files = malloc(10 * sizeof(void*));
if (!*files) {
return -1;
}
for (hash = Tcl_FirstHashEntry(&reg->open_files, &search); hash != NULL;
hash = Tcl_NextHashEntry(&search)) {
file = Tcl_GetHashValue(hash);
if (!reg_listcat((void***)files, &file_count, &file_space, file)) {
free(*files);
return -1;
}
}
return file_count;
}
+66
View File
@@ -0,0 +1,66 @@
/*
* file.h
* vim:tw=80:expandtab
* $Id$
*
* Copyright (c) 2011 Clemens Lang <cal@macports.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _CFILE_H
#define _CFILE_H
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "registry.h"
#include <sqlite3.h>
typedef struct {
/* rowid and path form the primary key */
/* unfortunately, we don't have a surrogate key in this db */
sqlite_int64 id; /* rowid in the database */
char* path; /* path in the database */
} reg_file_pk;
typedef struct {
reg_file_pk key;
reg_registry* reg; /* associated registry */
char* proc; /* name of Tcl proc, if using Tcl */
} reg_file;
reg_file* reg_file_open(reg_registry* reg, char* id, char* path,
reg_error* errPtr);
int reg_file_search(reg_registry* reg, char** keys, char** vals, int* strats,
int key_count, reg_file*** files, reg_error* errPtr);
int reg_file_propget(reg_file* file, char* key, char** value,
reg_error* errPtr);
int reg_file_propset(reg_file* file, char* key, char* value,
reg_error* errPtr);
int reg_all_open_files(reg_registry* reg, reg_file*** files);
#endif /* _CFILE_H */
+17
View File
@@ -1,6 +1,7 @@
/*
* registry.c
* $Id$
* vim:expandtab:tw=80
*
* Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
* All rights reserved.
@@ -31,6 +32,7 @@
#endif
#include "entry.h"
#include "file.h"
#include "sql.h"
#include <stdio.h>
@@ -214,6 +216,8 @@ int reg_attach(reg_registry* reg, const char* path, reg_error* errPtr) {
if (initialized || (create_tables(reg->db, errPtr))) {
Tcl_InitHashTable(&reg->open_entries,
sizeof(sqlite_int64)/sizeof(int));
Tcl_InitHashTable(&reg->open_files,
TCL_STRING_KEYS);
reg->status |= reg_attached;
result = 1;
}
@@ -224,6 +228,10 @@ int reg_attach(reg_registry* reg, const char* path, reg_error* errPtr) {
reg_sqlite_error(reg->db, errPtr, query);
}
} while (r == SQLITE_BUSY);
if (result) {
result &= update_db(reg->db, errPtr);
}
} else {
reg_sqlite_error(reg->db, errPtr, query);
}
@@ -275,6 +283,15 @@ int reg_detach(reg_registry* reg, reg_error* errPtr) {
free(entry);
}
Tcl_DeleteHashTable(&reg->open_entries);
for (curr = Tcl_FirstHashEntry(&reg->open_files, &search);
curr != NULL; curr = Tcl_NextHashEntry(&search)) {
reg_file* file = Tcl_GetHashValue(curr);
free(file->proc);
free(file->key.path);
free(file);
}
Tcl_DeleteHashTable(&reg->open_files);
reg->status &= ~reg_attached;
result = 1;
break;
+3 -1
View File
@@ -1,5 +1,6 @@
/*
* registry.h
* vim:tw=80:expandtab
* $Id$
*
* Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
@@ -56,7 +57,7 @@ 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);
void* calldata, reg_error* errPtr);
typedef void (free_function)(void* userdata, void* item);
enum {
@@ -70,6 +71,7 @@ typedef struct {
sqlite3* db;
int status;
Tcl_HashTable open_entries;
Tcl_HashTable open_files;
} reg_registry;
int reg_open(reg_registry** regPtr, reg_error* errPtr);
+56 -3
View File
@@ -34,8 +34,9 @@
#include "sql.h"
#include "vercomp.h"
#include <tcl.h>
#include <sqlite3.h>
#include <string.h>
#include <tcl.h>
#include <time.h>
/**
@@ -108,7 +109,7 @@ int create_tables(sqlite3* db, reg_error* errPtr) {
/* metadata table */
"CREATE TABLE registry.metadata (key UNIQUE, value)",
"INSERT INTO registry.metadata (key, value) VALUES ('version', 1.000)",
"INSERT INTO registry.metadata (key, value) VALUES ('version', 1.100)",
"INSERT INTO registry.metadata (key, value) VALUES ('created', strftime('%s', 'now'))",
/* ports table */
@@ -130,11 +131,12 @@ int create_tables(sqlite3* db, reg_error* errPtr) {
/* file map */
"CREATE TABLE registry.files (id INTEGER, path TEXT, actual_path TEXT, "
"active INT, mtime DATETIME, md5sum TEXT, editable INT, "
"active INT, mtime DATETIME, md5sum TEXT, editable INT, binary BOOL, "
"FOREIGN KEY(id) REFERENCES ports(id))",
"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)",
"CREATE INDEX registry.file_binary ON files(binary)",
/* dependency map */
"CREATE TABLE registry.dependencies (id INTEGER, name TEXT, variants TEXT, "
@@ -147,6 +149,57 @@ int create_tables(sqlite3* db, reg_error* errPtr) {
return do_queries(db, queries, errPtr);
}
/**
* Updates the database if necessary. This function queries the current database version
* from the metadata table and executes SQL to update the schema to newer versions if needed.
* After that, this function updates the database version number
*
* @param [in] db database to update
* @param [out] errPtr on error, a description of the error that occurred
* @return true if success; false if failure
*/
int update_db(sqlite3* db, reg_error* errPtr) {
const char* version;
char* query = "SELECT value FROM registry.metadata WHERE key = 'version'";
sqlite3_stmt *stmt = NULL;
if ((sqlite3_prepare(db, query, -1, &stmt, NULL) != SQLITE_OK)
|| (sqlite3_step(stmt) != SQLITE_ROW)) {
goto reg_err_out;
}
if (NULL == (version = (const char *)sqlite3_column_text(stmt, 0))) {
goto reg_err_out;
}
/* can't call rpm_vercomp directly, because it is static, but can call sql_version */
if (sql_version(NULL, strlen(version), version, strlen("1.1"), "1.1") < 0) {
/* conversion necessary, add binary field and index to files table */
static char* version_1_1_queries[] = {
"BEGIN",
"ALTER TABLE registry.files ADD COLUMN binary BOOL",
"CREATE INDEX registry.file_binary ON files(binary)",
"UPDATE registry.metadata SET value = '1.100' WHERE key = 'version'",
"COMMIT"
};
if (!do_queries(db, version_1_1_queries, errPtr)) {
goto err_out;
}
/* TODO: Walk the file tree and set the binary field */
}
sqlite3_finalize(stmt);
return 1;
reg_err_out:
reg_sqlite_error(db, errPtr, query);
err_out:
sqlite3_finalize(stmt);
return 0;
}
/**
* Initializes database connection. This function creates all the temporary
* tables used by the registry. It also registers the user functions and
+1
View File
@@ -38,5 +38,6 @@
int create_tables(sqlite3* db, reg_error* errPtr);
int init_db(sqlite3* db, reg_error* errPtr);
int update_db(sqlite3* db, reg_error* errPtr);
#endif /* _SQL_H */
+182
View File
@@ -0,0 +1,182 @@
/*
* util.c
* vim:tw=80:expandtab
* $Id$
*
* Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "util.h"
#include <stdlib.h>
#include <string.h>
/**
* 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.
*
* @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`
*/
int reg_strcat(char** dst, size_t* dst_len, size_t* dst_space, char* src) {
size_t src_len = strlen(src);
size_t result_len = *dst_len + src_len;
if (result_len > *dst_space) {
char* new_dst;
*dst_space *= 2;
if (*dst_space < result_len) {
*dst_space = result_len;
}
new_dst = realloc(*dst, *dst_space * sizeof(char) + 1);
if (!new_dst)
return 0;
else
*dst = new_dst;
}
memcpy(*dst + *dst_len, src, src_len+1);
*dst_len = result_len;
return 1;
}
/**
* 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.
*
* @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`
*/
int reg_listcat(void*** dst, int* dst_len, int* dst_space, void* src) {
if (*dst_len == *dst_space) {
void** new_dst;
*dst_space *= 2;
new_dst = realloc(*dst, *dst_space * sizeof(void*));
if (!new_dst)
return 0;
else
*dst = new_dst;
}
(*dst)[*dst_len] = src;
(*dst_len)++;
return 1;
}
/**
* Returns an expression to use for the given strategy. This should be passed as
* the `fmt` argument of `sqlite3_mprintf`, with the key and value following.
*
* @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 expression if success; NULL if failure
*/
char* reg_strategy_op(reg_strategy strategy, reg_error* errPtr) {
switch (strategy) {
case reg_strategy_exact:
return "%q = '%q'";
case reg_strategy_glob:
return "%q GLOB '%q'";
case reg_strategy_regexp:
return "REGEXP(%q, '%q')";
case reg_strategy_null:
return "%q IS NULL";
default:
errPtr->code = REG_INVALID;
errPtr->description = "invalid matching strategy specified";
errPtr->free = NULL;
return NULL;
}
}
/**
* Convenience method for returning all objects of a given type from the
* registry.
*
* @param [in] reg registry to select objects from
* @param [in] query the select query to execute
* @param [in] query_len length of the query (or -1 for automatic)
* @param [out] objects the objects selected
* @param [in] fn a function to convert sqlite3_stmts to the desired type
* @param [in] del a function to delete the desired type of object
* @param [out] errPtr on error, a description of the error that occurred
* @return the number of objects if success; negative if failure
*/
int reg_all_objects(reg_registry* reg, char* query, int query_len,
void*** objects, cast_function* fn, void* castcalldata,
free_function* del, reg_error* errPtr) {
void** results = malloc(10*sizeof(void*));
int result_count = 0;
int result_space = 10;
sqlite3_stmt* stmt = NULL;
if (!results || !fn) {
return -1;
}
if (sqlite3_prepare(reg->db, query, query_len, &stmt, NULL) == SQLITE_OK) {
int r;
void* row;
do {
r = sqlite3_step(stmt);
switch (r) {
case SQLITE_ROW:
if (fn(reg, &row, stmt, castcalldata, errPtr)) {
if (!reg_listcat(&results, &result_count, &result_space, row)) {
r = SQLITE_ERROR;
}
} else {
r = SQLITE_ERROR;
}
break;
case SQLITE_DONE:
break;
case SQLITE_BUSY:
continue;
default:
reg_sqlite_error(reg->db, errPtr, query);
break;
}
} while (r == SQLITE_ROW || r == SQLITE_BUSY);
sqlite3_finalize(stmt);
if (r == SQLITE_DONE) {
*objects = results;
return result_count;
} else if (del) {
int i;
for (i=0; i<result_count; i++) {
del(NULL, results[i]);
}
}
} else {
if (stmt) {
sqlite3_finalize(stmt);
}
reg_sqlite_error(reg->db, errPtr, query);
}
free(results);
return -1;
}
+53
View File
@@ -0,0 +1,53 @@
/*
* util.h
* vim:tw=80:expandtab
* $Id$
*
* Copyright (c) 2011 Clemens Lang <cal@macports.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _CUTIL_H
#define _CUTIL_H
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "registry.h"
typedef enum {
reg_strategy_exact = 1,
reg_strategy_glob = 2,
reg_strategy_regexp = 3,
reg_strategy_null = 4
} reg_strategy;
int reg_strcat(char** dst, size_t* dst_len, size_t* dst_space, char* src);
int reg_listcat(void*** dst, int* dst_len, int* dst_space, void* src);
int reg_all_objects(reg_registry* reg, char* query, int query_len,
void*** objects, cast_function* fn, void* castcalldata,
free_function* del, reg_error* errPtr);
char* reg_strategy_op(reg_strategy strategy, reg_error* errPtr);
#endif /* _CUTIL_H */
+53
View File
@@ -0,0 +1,53 @@
OBJS= libmachista.o hashmap.o machista_wrap.o
SHLIB_NAME= machista${SHLIB_SUFFIX}
INSTALLDIR= ${DESTDIR}${datadir}/macports/Tcl/machista1.0
SWIG = @SWIG@
SWIG_FLAGS = -tcl8 -pkgversion 1.0 -namespace
SWIG_LDFLAGS = -ltcl
SWIG_IFACE = machista.i
SWIG_SRCS = ${SWIG_IFACE:%.i=%_wrap.c}
SWIG_OBJS = ${SWIG_SRCS:%.c=%.o}
TESTS = ./tests/libmachista-test
include ../../Mk/macports.autoconf.mk
include ../../Mk/macports.tea.mk
CFLAGS+= -fPIC -std=c99 -pedantic
${SWIG_SRCS}:: ${SWIG_IFACE}
ifdef SWIG
${SWIG} ${SWIG_FLAGS} $<
else
@echo "Building ${SWIG_SRCS} requires swig, which you apparently did not have installed when configuring MacPorts." >&2
@echo "Please install swig and re-run configure" >&2
@echo "I recommend installing SWIG 1.x in /opt/swig, because as of this writing SWIG 2.x-generated code does not compile warning-free" >&2
@echo "To use this SWIG, run configure as follows:" >&2
@echo " SWIG=/opt/swig/bin/swig ./configure --your-usual-config-flags" >&2
@echo "If you checked out from svn you can also run svn revert in base/src/machista1.0 to get rid of this error" >&2
@exit 1
endif
${SWIG_OBJS}:: ${SWIG_SRCS}
${PKG_INDEX}:: ${SWIG_SHLIB}
$(SILENT) ../pkg_mkindex.sh $< || ( rm -rf $@ && exit 1 )
clean::
rm -f ${SWIG_OBJS} ${PKG_INDEX}
ifdef SWIG
rm -f ${SWIG_SRCS}
else
@echo "Not cleaning SWIG-generated sources, because you have configured MacPorts without SWIG." >&2
endif
rm -f ${TESTS}
rm -rf ${TESTS:%=%.dSYM}
test:: ${TESTS}
${TESTS}
tests/libmachista-test: tests/libmachista-test.c libmachista.h libmachista.o hashmap.o
$(CC) $(CFLAGS) -D_POSIX_SOURCE -o $@ -I. $< libmachista.o hashmap.o
+295
View File
@@ -0,0 +1,295 @@
/* vim:expandtab:tw=80:ts=2:sts=2:sw=2
*/
/*-
* Copyright (c) 2011 Christoph Erhardt. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY CHRISTOPH ERHARDT ``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 CHRISTOPH ERHARDT 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.
*/
/*-
* Modified by Clemens Lang to accept values of arbitrary type.
*/
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#include "hashmap.h"
#include <limits.h>
#include <stdint.h>
#include <string.h>
static const size_t INITIAL_CAPACITY = 16; /* Must be a power of 2 */
static const size_t MAXIMUM_CAPACITY = (1U << 31);
static const float LOAD_FACTOR = 0.75;
typedef struct HashMapEntry {
char *key;
const void *value;
struct HashMapEntry *next;
uint32_t hash;
} HashMapEntry;
struct HashMap {
HashMapEntry **table;
size_t capacity;
size_t size;
size_t threshold;
void (*freeFunc)(const void *);
};
static void setTable(HashMap *map, HashMapEntry **table, size_t capacity) {
map->table = table;
map->capacity = capacity;
map->threshold = (size_t) (capacity * LOAD_FACTOR);
}
static uint32_t doHash(const char key[]) {
size_t length;
size_t i;
uint32_t h = 0;
if (key == NULL)
return 0;
length = strlen(key);
for (i = 0; i < length; ++i) {
h = (31 * h) + key[i];
}
h ^= (h >> 20) ^ (h >> 12);
return h ^ (h >> 7) ^ (h >> 4);
}
static size_t indexFor(uint32_t hash, size_t length) {
return hash & (length - 1);
}
static int isHit(HashMapEntry *e, const char key[], uint32_t hash) {
return (e->hash == hash
&& (e->key == key || (key != NULL && strcmp(e->key, key) == 0)));
}
static void copyOrFree(void (*freeFunc)(const void *),
const void *value, const void **valPtr) {
if (valPtr != NULL)
*valPtr = value;
else
freeFunc(value);
}
static int updateValue(HashMap *map, HashMapEntry *e, const void *newVal,
const void **oldValPtr) {
copyOrFree(map->freeFunc, e->value, oldValPtr);
e->value = newVal;
return 1;
}
/* Creates a hash map. */
HashMap *hashMapCreate(void (*freeFunc)(const void *)) {
HashMapEntry **table;
HashMap *map = malloc(sizeof(*map));
if (map == NULL)
return NULL;
table = calloc(INITIAL_CAPACITY, sizeof(*map->table));
if (table == NULL) {
free(map);
return NULL;
}
setTable(map, table, INITIAL_CAPACITY);
map->size = 0;
map->freeFunc = freeFunc;
return map;
}
/* Inserts a key-value pair into a hash map. */
int hashMapPut(HashMap *map, const char key[], const void * const value,
const void **oldValPtr) {
HashMapEntry *e;
size_t newCapacity;
HashMapEntry **newTable;
size_t i;
/* If an entry with the same key exists, update it */
uint32_t hash = doHash(key);
size_t index = indexFor(hash, map->capacity);
for (e = map->table[index]; e != NULL; e = e->next) {
if (isHit(e, key, hash) == 0)
continue;
return updateValue(map, e, value, oldValPtr);
}
/* Create a new entry */
e = calloc(1, sizeof(HashMapEntry)); /* Must be zeroed */
if (e == NULL)
return 0;
/* Copy key and value into the entry */
if (key != NULL) {
e->key = strdup(key);
if (e->key == NULL) {
free(e);
return 0;
}
}
if (updateValue(map, e, value, oldValPtr) == 0) {
free(e->key);
free(e);
return 0;
}
/* Insert entry into the table */
e->hash = hash;
e->next = map->table[index];
map->table[index] = e;
if (map->size++ < map->threshold)
return 1;
/* If the size exceeds the threshold, double the table's capacity */
newCapacity = 2 * map->capacity;
if (map->capacity == MAXIMUM_CAPACITY) {
map->threshold = UINT_MAX;
return 1;
}
newTable = calloc(newCapacity, sizeof(*newTable));
if (newTable == NULL)
return 0;
/* Copy entries from the old table into the new one */
for (i = 0; i < map->capacity; ++i) {
HashMapEntry *next;
for (e = map->table[i]; e != NULL; e = next) {
index = indexFor(e->hash, newCapacity);
next = e->next;
e->next = newTable[index];
newTable[index] = e;
}
}
/* Release the old table and set the new one */
free(map->table);
setTable(map, newTable, newCapacity);
return 1;
}
/* Performs a hash map lookup. */
const void *hashMapGet(HashMap *map, const char key[]) {
HashMapEntry *e;
uint32_t hash = doHash(key);
size_t index = indexFor(hash, map->capacity);
for (e = map->table[index]; e != NULL; e = e->next) {
if (isHit(e, key, hash))
return e->value;
}
return NULL;
}
/* Checks whether a hash map contains an entry with a certain key. */
int hashMapContainsKey(HashMap *map, const char key[]) {
HashMapEntry *e;
uint32_t hash = doHash(key);
size_t index = indexFor(hash, map->capacity);
for (e = map->table[index]; e != NULL; e = e->next) {
if (isHit(e, key, hash))
return 1;
}
return 0;
}
/* Removes a key-value pair from a hash map. */
void hashMapRemove(HashMap *map, const char key[], const void **valPtr) {
uint32_t hash = doHash(key);
size_t index = indexFor(hash, map->capacity);
HashMapEntry *prev = map->table[index];
HashMapEntry *e = prev;
while (e != NULL) {
HashMapEntry *next = e->next;
if (isHit(e, key, hash)) {
map->size--;
if (prev == e)
map->table[index] = next;
else
prev->next = next;
break;
}
prev = e;
e = next;
}
if (e == NULL) {
copyOrFree(map->freeFunc, NULL, valPtr);
return;
}
free(e->key);
copyOrFree(map->freeFunc, e->value, valPtr);
free(e);
}
/* Returns the number of elements stored in a hash map. */
size_t hashMapSize(const HashMap *map) {
return map->size;
}
/* Checks whether a hash map is empty. */
int hashMapIsEmpty(const HashMap *map) {
return (map->size == 0);
}
/* Removes all entries from a hash map. */
void hashMapClear(HashMap *map) {
size_t i;
for (i = 0; i < map->capacity; ++i) {
HashMapEntry *e;
HashMapEntry *next;
for (e = map->table[i]; e != NULL; e = next) {
free(e->key);
map->freeFunc(e->value);
next = e->next;
free(e);
}
map->table[i] = NULL;
}
}
/* Destroys a hash map. */
void hashMapDestroy(HashMap *map) {
if (map == NULL)
return;
hashMapClear(map);
free(map->table);
free(map);
}
+135
View File
@@ -0,0 +1,135 @@
/* vim:tw=80:expandtab
*/
/**
* @file hashmap.h
* @brief A hash map implementation in C.
* @author Christoph Erhardt <erhardt@cs.fau.de>
*/
/*-
* Copyright (c) 2011 Christoph Erhardt. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY CHRISTOPH ERHARDT ``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 CHRISTOPH ERHARDT 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.
*/
/*-
* Modified by Clemens Lang to accept values of arbitrary type.
*/
#ifndef HASHMAP_H
#define HASHMAP_H
#include <stdlib.h>
/** Hash map type. */
typedef struct HashMap HashMap;
/**
* @brief Creates a hash map.
*
* The keys and values managed in the map can be arbitrary C strings.
* @param freeFunc Function to call in order to free a stored value
* @return Pointer to the newly created hash map, or @c NULL on error.
*/
HashMap *hashMapCreate(void (*freeFunc)(const void *));
/**
* @brief Inserts a key-value pair into a hash map.
*
* Both key and value are copied internally, so the caller can reuse the
* original variables.
* If oldValPtr is @c NULL, the previously stored value corresponding to the key
* is freed. Otherwise it is written into @c *valPtr and the caller is
* responsible for freeing it.
* @param map Hash map.
* @param key Key.
* @param value Value.
* @param oldValPtr Output parameter receiving the previously stored value
* corresponding to the key (@c NULL if no mapping existed
* before).
* @return Nonzero on success, 0 on error.
*/
int hashMapPut(HashMap *map, const char key[], const void * const value,
const void **oldValPtr);
/**
* @brief Performs a hash map lookup.
*
* The returned value must not be freed or otherwise manipulated by the caller.
* @param map Hash map.
* @param key Key.
* @return Value corresponding to the key on success, @c NULL if no matching
* entry was found.
*/
const void *hashMapGet(HashMap *map, const char key[]);
/**
* @brief Checks whether a hash map contains an entry with a certain key.
* @param map Hash map.
* @param key Key.
* @return Nonzero if the map contains an entry with the given key, 0 if it does
* not.
*/
int hashMapContainsKey(HashMap *map, const char key[]);
/**
* @brief Removes a key-value pair from a hash map and frees the stored key.
*
* If @c valPtr is @c NULL, the internally stored value corresponding to the key
* is freed. Otherwise it is written into @c *valPtr and the caller is
* responsible for freeing it.
* @param map Hash map.
* @param key Key.
* @param valPtr Output parameter receiving the internally stored value
* corresponding to the key.
*/
void hashMapRemove(HashMap *map, const char key[], const void **valPtr);
/**
* @brief Returns the number of elements stored in a hash map.
* @param map Hash map.
* @return Number of elements stored in the map.
*/
size_t hashMapSize(const HashMap *map);
/**
* @brief Checks whether a hash map is empty.
* @param map Hash map.
* @return Nonzero if the map contains no entries, 0 otherwise.
*/
int hashMapIsEmpty(const HashMap *map);
/**
* @brief Removes all entries from a hash map.
* @param map Hash map.
*/
void hashMapClear(HashMap *map);
/**
* @brief Destroys a hash map.
* @param map Hash map to be destroyed.
*/
void hashMapDestroy(HashMap *map);
#endif /* HASHMAP_H */
File diff suppressed because it is too large Load Diff
+144
View File
@@ -0,0 +1,144 @@
/*
* -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=c:et:sw=4:ts=4:sts=4:tw=100
* libmachista.h
* $Id$
*
* Copyright (c) 2011 The MacPorts Project
* Copyright (c) 2011 Clemens Lang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE 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.
*/
#ifndef __LIBMACHISTA_H__
#define __LIBMACHISTA_H__
/*
* This is a library to parse Mach-O files in single architecture _and_ universal variant and return
* a list of architectures and their load commands and properties
* The name a pun: machista is the spanish translation of "macho".
*/
#include <mach-o/arch.h>
#include <inttypes.h>
#define MACHO_SUCCESS (0x00)
#define MACHO_EFILE (0x01)
#define MACHO_EMMAP (0x02)
#define MACHO_EMEM (0x04)
#define MACHO_ERANGE (0x08)
#define MACHO_EMAGIC (0x10)
/* Blind structure; this essentially contains the hash map used to cache
* entries, but users should not have to look into this structure. struct
* macho_handle is defined in libmachista.c */
typedef struct macho_handle macho_handle_t;
/** Structure describing a load command within a Mach-O file */
typedef struct macho_loadcmd {
char *mlt_install_name; /* install name of the library to be loaded by this load command */
uint32_t mlt_type; /* type of the load command; see mach-o/loader.h for possible
values */
uint32_t mlt_comp_version; /* compatibility version of the file to be loaded by this
command (at build time of this file) */
uint32_t mlt_version; /* version of the library to be loaded by this command (at build
time of this file) */
struct macho_loadcmd *next; /* pointer to the next entry in the linked list of
macho_loadcmd_t's (NULL if there's no further element) */
} macho_loadcmd_t;
/** Stucture describing an architecture within a Mach-O file */
typedef struct macho_arch {
char *mat_install_name; /* install name of the library or NULL if none */
char *mat_rpath; /* rpath of the binary of NULL if none */
cpu_type_t mat_arch; /* cpu_type_t describing the CPU this part of the binary is
intended for */
uint32_t mat_comp_version; /* compatibility version of this part of the binary */
uint32_t mat_version; /* current version of this part of the binary */
macho_loadcmd_t *mat_loadcmds; /* array of macho_loadcmd_t's describing the different load
commands */
struct macho_arch *next; /* pointer to the next entry in the linked list of
macho_arch_t's (NULL if there's no further element) */
} macho_arch_t;
/** Structure describing a Mach-O file */
typedef struct macho {
macho_arch_t *mt_archs; /* linked list of macho_arch_t's describing the different
architectures */
} macho_t;
/**
* Creates and returns a macho_handle_t to be passed to subsequent calls to macho_parse_file. No
* assumptions should be made about the contents of a macho_handle_t; it is declared to be a blind
* structure.
*
* Returns either a pointer to a valid macho_handle_t or NULL on failure. errno will be set on
* failure. The resources associated with a macho_handle_t must be freed by passing it to
* macho_destroy_handle.
*/
macho_handle_t *macho_create_handle(void);
/**
* Frees resources associated with a macho_handle_t and invalidates all results returned by
* macho_parse_file called with this handle.
*/
void macho_destroy_handle(macho_handle_t *handle);
/**
* Formats a dylib version number given by an uint32_t into a human-readable format and returns a
* Pointer to the beginning of that string. The result is either a valid pointer or NULL on error
* (in which case the errno is set to indicate the error). The pointer must be free()'d after use.
*/
char *macho_format_dylib_version(uint32_t version);
/**
* Returns a readable version of any cpu_type_t constant. Returns a valid pointer to the first
* character in a 0-terminated string or NULL on error. The pointer must not be free()'d after use.
*/
const char *macho_get_arch_name(cpu_type_t cputype);
/**
* Parses the Mach-O file indicated by filepath and writes a pointer to a macho_t describing the
* Mach-O file into the location idicated by res. Returns MACHO_SUCCESS on success or any of the
* following error codes on error:
*
* code description errno set?
* MACHO_EFILE error stat()'ing, opening or reading the file yes
* MACHO_EMMAP error mmap()'ing the file yes
* MACHO_EMEM error allocating memory yes
* MACHO_ERANGE unexpected end of file no
* MACHO_EMAGIC unknown magic number/not a Mach-O file no
*
* On error, the contents of res are undefined and should not be used. The memory associated with
* the result *res will be free()'d and should thus not be used after calling macho_destroy_handle
* on the macho_handle_t used for the call. *res should also never be modified or otherwise
* free()'d.
*/
int macho_parse_file(macho_handle_t *handle, const char *filepath, const macho_t **res);
/**
* Returns a string representation of the MACHO_* error code constants
*/
const char *macho_strerror(int err);
#endif

Some files were not shown because too many files have changed in this diff Show More