source: branches/gsoc11-statistics/base/src/cregistry/file.c @ 140222

Last change on this file since 140222 was 105085, checked in by snc@…, 7 years ago

merge from trunk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.8 KB
Line 
1/*
2 * file.c
3 * vim:tw=80:expandtab
4 * $Id: file.c 105085 2013-04-09 18:46:31Z snc@macports.org $
5 *
6 * Copyright (c) 2011 Clemens Lang <cal@macports.org>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#if HAVE_CONFIG_H
31#include <config.h>
32#endif
33
34#include "file.h"
35#include "util.h"
36#include "registry.h"
37#include "sql.h"
38
39#include <sqlite3.h>
40#include <stdlib.h>
41#include <string.h>
42
43/**
44 * Converts a `sqlite3_stmt` into a `reg_file`. The first column of the stmt's
45 * row must be the id of a file; the second column must be the path of a file;
46 * the third either `SQLITE_NULL` or the address of the entry in memory.
47 *
48 * @param [in] userdata sqlite3 database
49 * @param [out] file    file described by `stmt`
50 * @param [in] stmt     `sqlite3_stmt` with appropriate columns
51 * @param [out] errPtr  unused
52 * @return              true if success; false if failure
53 */
54static int reg_stmt_to_file(void* userdata, void** file, void* stmt,
55        void* calldata UNUSED, reg_error* errPtr UNUSED) {
56    int is_new;
57    reg_registry* reg = (reg_registry*)userdata;
58    reg_file_pk key;
59    Tcl_HashEntry* hash;
60    char* hashkey;
61
62    key.id = sqlite3_column_int64(stmt, 0);
63    key.path = strdup((const char*) sqlite3_column_text(stmt, 1));
64    if (!key.path) {
65        return 0;
66    }
67
68    hashkey = sqlite3_mprintf("%lld:%s", key.id, key.path);
69    if (!hashkey) {
70        free(key.path);
71        return 0;
72    }
73    hash = Tcl_CreateHashEntry(&reg->open_files,
74            hashkey, &is_new);
75    sqlite3_free(hashkey);
76
77    if (is_new) {
78        reg_file* f = malloc(sizeof(reg_file));
79        if (!f) {
80            free(key.path);
81            return 0;
82        }
83        f->reg = reg;
84        f->key = key;
85        f->proc = NULL;
86        *file = f;
87        Tcl_SetHashValue(hash, f);
88    } else {
89        free(key.path);
90        *file = Tcl_GetHashValue(hash);
91    }
92    return 1;
93}
94
95/**
96 * Opens an existing file in the registry.
97 *
98 * @param [in] reg      registry to open entry in
99 * @param [in] id       port id in the dabatase
100 * @param [in] path     file path in the database
101 * @param [out] errPtr  on error, a description of the error that occures
102 * @return              the file if success, NULL if failure
103 */
104reg_file* reg_file_open(reg_registry* reg, char* id, char* name,
105        reg_error* errPtr) {
106    sqlite3_stmt* stmt = NULL;
107    reg_file* file = NULL;
108    char* query = "SELECT id, path FROM registry.files WHERE id=? AND path=?";
109    int lower_bound = 0;
110
111    if ((sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
112            && (sqlite3_bind_text(stmt, 1, id, -1, SQLITE_STATIC)
113                == SQLITE_OK)
114            && (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC)
115                == SQLITE_OK)) {
116        int r;
117        do {
118            r = sqlite3_step(stmt);
119            switch (r) {
120                case SQLITE_ROW:
121                    reg_stmt_to_file(reg, (void**)&file, stmt, &lower_bound,
122                            errPtr);
123                    break;
124                case SQLITE_DONE:
125                    errPtr->code = REG_NOT_FOUND;
126                    errPtr->description = sqlite3_mprintf("no matching file found for: "
127                            "id=%s, name=%s", id, name);
128                    errPtr->free = (reg_error_destructor*) sqlite3_free;
129                    break;
130                case SQLITE_BUSY:
131                    continue;
132                default:
133                    reg_sqlite_error(reg->db, errPtr, query);
134                    break;
135            }
136        } while (r == SQLITE_BUSY);
137    } else {
138        reg_sqlite_error(reg->db, errPtr, query);
139    }
140    if (stmt) {
141        sqlite3_finalize(stmt);
142    }
143    return file;
144}
145
146/**
147 * Type-safe version of `reg_all_objects` for `reg_file`.
148 *
149 * @param [in] reg       registry to select entries from
150 * @param [in] query     the select query to execute
151 * @param [in] query_len length of the query (or -1 for automatic)
152 * @param [out] objects  the files selected
153 * @param [out] errPtr   on error, a description of the error that occurred
154 * @return               the number of entries if success; negative if failure
155 */
156static int reg_all_files(reg_registry* reg, char* query, int query_len,
157        reg_file*** objects, reg_error* errPtr) {
158    int lower_bound = 0;
159    return reg_all_objects(reg, query, query_len, (void***)objects,
160            reg_stmt_to_file, &lower_bound, NULL, errPtr);
161}
162
163/**
164 * Searches the registry for files for which each key's value is equal to the
165 * given value. To find all files, pass a key_count of 0.
166 *
167 * Bad keys should cause sqlite3 errors but not permit SQL injection attacks.
168 * Pass it good keys anyway.
169 *
170 * @param [in] reg       registry to search in
171 * @param [in] keys      a list of keys to search by
172 * @param [in] vals      a list of values to search by, matching keys
173 * @param [in] strats    a list of strategies to use when searching
174 * @param [in] key_count the number of key/value pairs passed
175 * @param [out] files    a list of matching files
176 * @param [out] errPtr   on error, a description of the error that occurred
177 * @return               the number of entries if success; false if failure
178 */
179int reg_file_search(reg_registry* reg, char** keys, char** vals, int* strats,
180        int key_count, reg_file*** files, reg_error* errPtr) {
181    int i;
182    char* kwd = " WHERE ";
183    char* query;
184    size_t query_len, query_space;
185    int result;
186
187    /* build the query */
188    query = strdup("SELECT id, path FROM registry.files");
189    if (!query) {
190        return -1;
191    }
192    query_len = query_space = strlen(query);
193
194    for (i = 0; i < key_count; i++) {
195        char* op;
196        char* cond;
197
198        /* get the strategy */
199        if ((op = reg_strategy_op(strats[i], errPtr)) == NULL) {
200            free(query);
201            return -1;
202        }
203
204        cond = sqlite3_mprintf(op, keys[i], vals[i]);
205        if (!cond || !reg_strcat(&query, &query_len, &query_space, kwd)
206            || !reg_strcat(&query, &query_len, &query_space, cond)) {
207            free(query);
208            return -1;
209        }
210        sqlite3_free(cond);
211        kwd = " AND ";
212    }
213
214    /* do the query */
215    result = reg_all_files(reg, query, -1, files, errPtr);
216    free(query);
217    return result;
218}
219
220/**
221 * Gets a named property of a file. That property can be set using
222 * `reg_file_propset`. The property named must be one that exists in the table
223 * and must not be one with internal meaning such as `id`.
224 *
225 * @param [in] file    file to get property from
226 * @param [in] key     property to get
227 * @param [out] value  the value of the property
228 * @param [out] errPtr on error, a description of the error that occurred
229 * @return             true if success; false if failure
230 */
231int reg_file_propget(reg_file* file, char* key, char** value,
232        reg_error* errPtr) {
233    reg_registry* reg = file->reg;
234    int result = 0;
235    sqlite3_stmt* stmt = NULL;
236    char* query;
237    const char *text;
238    query = sqlite3_mprintf("SELECT %q FROM registry.files WHERE id=%lld "
239            "AND path='%q'", key, file->key.id, file->key.path);
240    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
241        int r;
242        do {
243            r = sqlite3_step(stmt);
244            switch (r) {
245                case SQLITE_ROW:
246                    text = (const char*)sqlite3_column_text(stmt, 0);
247                    if (text) {
248                        *value = strdup(text);
249                        result = 1;
250                    } else {
251                        reg_sqlite_error(reg->db, errPtr, query);
252                    }
253                    break;
254                case SQLITE_DONE:
255                    errPtr->code = REG_INVALID;
256                    errPtr->description = "an invalid file was passed";
257                    errPtr->free = NULL;
258                    break;
259                case SQLITE_BUSY:
260                    continue;
261                default:
262                    reg_sqlite_error(reg->db, errPtr, query);
263                    break;
264            }
265        } while (r == SQLITE_BUSY);
266    } else {
267        reg_sqlite_error(reg->db, errPtr, query);
268    }
269    if (stmt) {
270        sqlite3_finalize(stmt);
271    }
272    sqlite3_free(query);
273    return result;
274}
275
276/**
277 * Sets a named property of a file. That property can be later retrieved using
278 * `reg_file_propget`. The property named must be one that exists in the table
279 * and must not be one with internal meaning such as `id`.
280 *
281 * @param [in] file    file to set property for
282 * @param [in] key     property to set
283 * @param [in] value   the desired value of the property
284 * @param [out] errPtr on error, a description of the error that occurred
285 * @return             true if success; false if failure
286 */
287int reg_file_propset(reg_file* file, char* key, char* value,
288        reg_error* errPtr) {
289    reg_registry* reg = file->reg;
290    int result = 0;
291    sqlite3_stmt* stmt = NULL;
292    char* query;
293    query = sqlite3_mprintf("UPDATE registry.files SET %q = '%q' WHERE id=%lld "
294            "AND path='%q'", key, value, file->key.id, file->key.path);
295    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
296        int r;
297        do {
298            r = sqlite3_step(stmt);
299            switch (r) {
300                case SQLITE_DONE:
301                    result = 1;
302                    break;
303                case SQLITE_BUSY:
304                    continue;
305                default:
306                    if (sqlite3_reset(stmt) == SQLITE_CONSTRAINT) {
307                        errPtr->code = REG_CONSTRAINT;
308                        errPtr->description = "a constraint was disobeyed";
309                        errPtr->free = NULL;
310                    } else {
311                        reg_sqlite_error(reg->db, errPtr, query);
312                    }
313                    break;
314            }
315        } while (r == SQLITE_BUSY);
316    } else {
317        reg_sqlite_error(reg->db, errPtr, query);
318    }
319    if (stmt) {
320        sqlite3_finalize(stmt);
321    }
322    sqlite3_free(query);
323    return result;
324}
325
326/**
327 * Fetches a list of all open files
328 *
329 * @param [in] reg      registry to fetch files from
330 * @param [out] files   a list of open files
331 * @return              the number of open entries, -1 on error
332 */
333int reg_all_open_files(reg_registry* reg, reg_file*** files) {
334    reg_file* file;
335    int file_count = 0;
336    int file_space = 10;
337    Tcl_HashEntry* hash;
338    Tcl_HashSearch search;
339    *files = malloc(10 * sizeof(void*));
340    if (!*files) {
341        return -1;
342    }
343    for (hash = Tcl_FirstHashEntry(&reg->open_files, &search); hash != NULL;
344            hash = Tcl_NextHashEntry(&search)) {
345        file = Tcl_GetHashValue(hash);
346        if (!reg_listcat((void***)files, &file_count, &file_space, file)) {
347            free(*files);
348            return -1;
349        }
350    }
351    return file_count;
352}
353
Note: See TracBrowser for help on using the repository browser.