You've already forked macports-base
mirror of
https://github.com/macports/macports-base.git
synced 2026-07-12 18:18:43 -07:00
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:
@@ -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" ;;
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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(®->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(®->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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
@@ -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(®->open_entries,
|
||||
sizeof(sqlite_int64)/sizeof(int));
|
||||
Tcl_InitHashTable(®->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(®->open_entries);
|
||||
for (curr = Tcl_FirstHashEntry(®->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(®->open_files);
|
||||
reg->status &= ~reg_attached;
|
||||
result = 1;
|
||||
break;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user