/* * util.c * vim:tw=80:expandtab * * Copyright (c) 2007 Chris Pickel * Copyright (c) 2012 The MacPorts Project * 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 #include /** * 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,out] castcalldata data passed along to the cast function * @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(REG_RESULT_INITIAL_SPACE*sizeof(void*)); int result_count = 0; int result_space = REG_RESULT_INITIAL_SPACE; sqlite3_stmt* stmt = NULL; if (!results) { return -1; } if (!fn) { free(results); return -1; } if (sqlite3_prepare_v2(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; idb, errPtr, query); } free(results); return -1; }