source: trunk/base/src/registry2.0/item.c @ 64294

Last change on this file since 64294 was 64294, checked in by jmr@…, 11 years ago

error checking, sprintf -> snprintf, strcpy -> strncpy

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.5 KB
Line 
1/*
2 * item.c
3 * $Id: item.c 64294 2010-02-28 21:59:12Z jmr@macports.org $
4 *
5 * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#if HAVE_CONFIG_H
30#include <config.h>
31#endif
32
33#include <string.h>
34#include <stdlib.h>
35#include <tcl.h>
36#include <sqlite3.h>
37
38#include "item.h"
39#include "itemobj.h"
40#include "util.h"
41#include "registry.h"
42
43static void delete_item(ClientData clientData) {
44    sqlite_int64 rowid = ((item_t*)clientData)->rowid;
45    sqlite3* db = ((item_t*)clientData)->db;
46    sqlite3_stmt* stmt;
47    sqlite3_prepare(db, "DELETE FROM items WHERE rowid=?", -1, &stmt, NULL);
48    sqlite3_bind_int(stmt, rowid, 1);
49    sqlite3_step(stmt);
50    sqlite3_finalize(stmt);
51    free(clientData);
52}
53
54static item_t* get_item(Tcl_Interp* interp, char* name) {
55    return (item_t*)get_object(interp, name, "item", item_obj_cmd);
56}
57
58static int set_item(Tcl_Interp* interp, char* name, sqlite_int64 rowid) {
59    sqlite3* db = registry_db(interp, 0);
60    item_t* new_item = malloc(sizeof(item_t));
61    new_item->rowid = rowid;
62    new_item->db = db;
63    if (set_object(interp, name, new_item, "item", item_obj_cmd, delete_item)
64                == TCL_OK) {
65        sqlite3_stmt* stmt;
66        /* record the proc name in case we need to return it in a search */
67        if ((sqlite3_prepare(db, "UPDATE items SET proc=? WHERE rowid=?", -1,
68                    &stmt, NULL) == SQLITE_OK)
69                && (sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC)
70                    == SQLITE_OK)
71                && (sqlite3_bind_int64(stmt, 2, rowid) == SQLITE_OK)
72                && (sqlite3_step(stmt) == SQLITE_DONE)) {
73            sqlite3_finalize(stmt);
74            return TCL_OK;
75        }
76        Tcl_DeleteCommand(interp, name);
77        sqlite3_finalize(stmt);
78    }
79    free(new_item);
80    return TCL_ERROR;
81}
82
83/* item create ?name? */
84static int item_create(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
85    sqlite_int64 item;
86    sqlite3* db = registry_db(interp, 0);
87    if (objc > 3) {
88        Tcl_WrongNumArgs(interp, 2, objv, "?name?");
89        return TCL_ERROR;
90    } else if (db == NULL) {
91        return TCL_ERROR;
92    }
93    sqlite3_exec(db, "INSERT INTO items (refcount) VALUES (1)", NULL, NULL,
94            NULL);
95    item = sqlite3_last_insert_rowid(db);
96    if (objc == 3) {
97        /* item create name */
98        char* name = Tcl_GetString(objv[2]);
99        if (set_item(interp, name, item) == TCL_OK) {
100            Tcl_SetObjResult(interp, objv[2]);
101            return TCL_OK;
102        }
103    } else {
104        /* item create */
105        char* name = unique_name(interp, "registry::item");
106        if (set_item(interp, name, item) == TCL_OK) {
107            Tcl_Obj* res = Tcl_NewStringObj(name, -1);
108            Tcl_SetObjResult(interp, res);
109            free(name);
110            return TCL_OK;
111        }
112        free(name);
113    }
114    return TCL_ERROR;
115}
116
117/* item release ?name ...? */
118static int item_release(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
119    int i;
120    for (i=2; i<objc; i++) {
121        char* proc = Tcl_GetString(objv[i]);
122        item_t* item = get_item(interp, proc);
123        if (item == NULL) {
124            return TCL_ERROR;
125        } else {
126            /* decref */
127        }
128    }
129    return TCL_OK;
130}
131
132static const char* searchKeys[] = {
133    "name",
134    "url",
135    "path",
136    "worker",
137    "options",
138    "variants",
139    NULL
140};
141
142/**
143 * item search ?{key value} ...?
144 *
145 * TODO: rip this out and adapt `entry search`
146 */
147static int item_search(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
148    int i, r;
149    sqlite3* db = registry_db(interp, 0);
150    sqlite3_stmt* stmt;
151    Tcl_Obj* result;
152    /* 40 + 20 per clause is safe */
153    int query_size = (20*objc)*sizeof(char);
154    char* query = (char*)malloc(query_size);
155    char* query_start = "SELECT proc FROM items";
156    char* insert;
157    int insert_size = query_size - strlen(query_start);
158    if (db == NULL) {
159        return TCL_ERROR;
160    }
161    strncpy(query, query_start, query_size);
162    insert = query + strlen(query_start);
163    for (i=2; i<objc; i++) {
164        int len;
165        int index;
166        char* key;
167        Tcl_Obj* keyObj;
168        /* ensure each search clause is a 2-element list */
169        if (Tcl_ListObjLength(interp, objv[i], &len) != TCL_OK || len != 2) {
170            free(query);
171            Tcl_AppendResult(interp, "search clause \"", Tcl_GetString(objv[i]),
172                    "\" is not a list with 2 elements", NULL);
173            return TCL_ERROR;
174        }
175        /* this should't fail if Tcl_ListObjLength didn't */
176        Tcl_ListObjIndex(interp, objv[i], 0, &keyObj);
177        /* ensure that a valid search key was used */
178        if (Tcl_GetIndexFromObj(interp, keyObj, searchKeys, "search key", 0,
179                &index) != TCL_OK) {
180            free(query);
181            return TCL_ERROR;
182        }
183        key = Tcl_GetString(keyObj);
184        if (i == 2) {
185            snprintf(insert, insert_size, " WHERE %s=?", key);
186            insert += 9 + strlen(key);
187            insert_size -= 9 + strlen(key);
188        } else {
189            snprintf(insert, insert_size, " AND %s=?", key);
190            insert += 7 + strlen(key);
191            insert_size -= 7 + strlen(key);
192        }
193    }
194    r = sqlite3_prepare(db, query, -1, &stmt, NULL);
195    free(query);
196    for (i=2; i<objc; i++) {
197        char* val;
198        Tcl_Obj* valObj;
199        Tcl_ListObjIndex(interp, objv[i], 1, &valObj);
200        val = Tcl_GetString(valObj);
201        sqlite3_bind_text(stmt, i-1, val, -1, SQLITE_STATIC);
202    }
203    result = Tcl_NewListObj(0, NULL);
204    r = sqlite3_step(stmt);
205    while (r == SQLITE_ROW) {
206        /* avoid signedness warning */
207        const char* proc = sqlite3_column_text(stmt, 0);
208        int len = sqlite3_column_bytes(stmt, 0);
209        Tcl_Obj* procObj = Tcl_NewStringObj(proc, len);
210        Tcl_ListObjAppendElement(interp, result, procObj);
211        r = sqlite3_step(stmt);
212    }
213    Tcl_SetObjResult(interp, result);
214    return TCL_OK;
215}
216
217/* item exists name */
218static int item_exists(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
219    if (objc != 3) {
220        Tcl_WrongNumArgs(interp, 2, objv, "name");
221        return TCL_ERROR;
222    }
223    if (get_item(interp, Tcl_GetString(objv[2])) == NULL) {
224        Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
225    } else {
226        Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));
227    }
228    return TCL_OK;
229}
230
231typedef struct {
232    char* name;
233    int (*function)(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
234} item_cmd_type;
235
236static item_cmd_type item_cmds[] = {
237    /* Global commands */
238    { "create", item_create },
239    { "search", item_search },
240    { "exists", item_exists },
241    /* Instance commands */
242    /*
243    { "retain", item_retain },
244    { "release", item_release },
245    { "name", item_name },
246    { "url", item_url },
247    { "path", item_path },
248    { "worker", item_worker },
249    { "options", item_options },
250    { "variants", item_variants },
251    */
252    { NULL, NULL }
253};
254
255/* item cmd ?arg ...? */
256int item_cmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc,
257        Tcl_Obj* CONST objv[]) {
258    int cmd_index;
259    if (objc < 2) {
260        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?");
261        return TCL_ERROR;
262    }
263    if (Tcl_GetIndexFromObjStruct(interp, objv[1], item_cmds,
264                sizeof(item_cmd_type), "cmd", 0, &cmd_index) == TCL_OK) {
265        item_cmd_type* cmd = &item_cmds[cmd_index];
266        return cmd->function(interp, objc, objv);
267    }
268    return TCL_ERROR;
269}
Note: See TracBrowser for help on using the repository browser.