/* * Set a node's data. If it does not exist, create it. * Return a pointer to the node. */ DLLEXP struct pdb_node_t* pdb_set_node(struct pdb* dbptr, char* path, char* key, void* data, int type) { struct pdb_node_types_t* tiptr; struct pdb_node_t* pptr; struct pdb_node_types_t* ptiptr; struct pdb_node_t* nptr; if (!dbptr) return NULL; PDB_MUTEX_LOCK(dbptr); tiptr = pdb_get_type_info(type); if (!tiptr) { PDB_MUTEX_UNLOCK(dbptr); return NULL; } PDB_MUTEX_UNLOCK(dbptr); pptr = pdb_query_node(dbptr, path); if (!pptr) return NULL; PDB_MUTEX_LOCK(dbptr); ptiptr = pdb_get_type_info(pptr->type); nptr = ptiptr->query_cb(pptr, key); if (!nptr) { /* * Node does not exists -- create. */ nptr = tiptr->create_cb(key, pptr, NULL); if (!nptr) { fprintf(stderr, "%s:%s():%i: Error: Unable to create \"%s/%s\" in " "database.\n", __FILE__, __FUNCTION__, __LINE__, path, key); PDB_MUTEX_UNLOCK(dbptr); return NULL; } } /* * Schedule a disk write. */ dbptr->altered = 1; if (!tiptr->set_cb) { /*fprintf(stderr, "%s:%s():%i: Warning: Unable to set data at \"%s/%s\" " "in database; type %i does not support it.\n", __FILE__, __FUNCTION__, __LINE__, path, key, tiptr->bitmask);*/ } else tiptr->set_cb(nptr, data); PDB_MUTEX_UNLOCK(dbptr); return nptr; }
/* * Return a given node from the database. */ DLLEXP struct pdb_node_t* pdb_query_node(struct pdb* dbptr, char* path) { char** tok_arr; struct pdb_node_t* nptr; struct pdb_node_types_t* tiptr; int i = 0; if (!dbptr) return 0; PDB_MUTEX_LOCK(dbptr); if (!strcmp(path, "") || !strcmp(path, "/")) { PDB_MUTEX_UNLOCK(dbptr); return dbptr->data; } tok_arr = token_parse(path, PDB_PATH_DELIM, NULL); nptr = dbptr->data; tiptr = NULL; while (tok_arr[i]) { tiptr = pdb_get_type_info(nptr->type); nptr = tiptr->query_cb(nptr, tok_arr[i]); if (!nptr) break; ++i; } token_free(tok_arr); PDB_MUTEX_UNLOCK(dbptr); return nptr; }
/* * Write a given node to disk. * * Types without an open_token will have their write callbacks called * without a need line prepended. * Types without a close_token will not have the close_token and new line * appended. */ int pdb_standard_write_node(struct pdb* dbptr, FILE* fptr, struct pdb_node_t* nptr, int tabs) { struct pdb_node_types_t* tiptr; int i = 0; int ret; tiptr = pdb_get_type_info(nptr->type); if (!tiptr->write_cb) { char* trace = pdb_trace(nptr); fprintf(stderr, "%s:%s():%i: Warning: Unable to write node \"%s\" to " "disk; operation node supported by node type %i.\n", __FILE__, __FUNCTION__, __LINE__, trace, nptr->type); free(trace); return 1; } /* * Write tabs -- if any (if tabs != -1 [root]). */ if (tabs != -1) for (; i < tabs; i++) fputc('\t', fptr); /* * Write key in double quotes followed by an opening token. */ if (tabs != -1) { fputc('\"', fptr); if (nptr->id) fputs(nptr->id, fptr); fputs("\" ", fptr); if ((tiptr->write_as_block) && (tiptr->open_token)) { fputs(tiptr->open_token, fptr); fputc('\n', fptr); } } /* * Write node to disk. */ ret = tiptr->write_cb(dbptr, fptr, nptr, tabs + 1); /* * Write tabs and tree closing token (if tabs != -1 [root]). */ if ((tabs != -1) && (tiptr->close_token) && (tiptr->write_as_block)) { i = 0; for (; i < tabs; i++) fputc('\t', fptr); fputs(tiptr->close_token, fptr); fputc('\n', fptr); } return ret; }
/* * Standard node loading function to be used for * node load callbacks if desired. */ int pdb_standard_load_node(FILE* fptr, struct pdb_node_t* pptr, char** tok_arr, int* line) { int type = pptr->type; struct pdb_node_types_t* ctiptr = NULL; void* cptr = NULL; char* tok = pdb_get_token(fptr, &type, line); while (tok) { /* * Is the block over? */ if ((type & BLOCK_CLOSE) == BLOCK_CLOSE) { free(tok); return 1; } tok_arr = pdb_token_parse(tok); /* * Create the child node and add to parent. */ ctiptr = pdb_get_type_info(type); if (!ctiptr) { fprintf(stderr, "%s:%s():%i: Error: Unknown child type %i on line " "%i; halting database load.\n", __FILE__, __FUNCTION__, __LINE__, type, *line); free(tok); return 0; } cptr = ctiptr->create_cb(tok_arr[0], pptr, tok_arr); /* * Load the child node (if supported). */ if (ctiptr->load_cb) { if (!ctiptr->load_cb(fptr, cptr, tok_arr, line)) { fprintf(stderr, "%s:%s():%i: Error: An error occured while " "loading the database; halting database load on line %i.\n", __FILE__, __FUNCTION__, __LINE__, *line); token_free(tok_arr); free(tok); return 0; } } /* * Get next token. */ token_free(tok_arr); free(tok); type = pptr->type; tok = pdb_get_token(fptr, &type, line); } return 1; }
/* * Load a file and return a database pointer. * If file is NULL, create an empty database. */ DLLEXP struct pdb* pdb_load(char* file) { struct pdb* dbptr; struct pdb_node_types_t* rntptr; int line = 1; FILE* fptr = NULL; dbptr = (struct pdb*)malloc(sizeof(struct pdb)); if (!dbptr) return NULL; if (file) { fptr = pdb_open_file(file); if (!fptr) return NULL; } /* * Initialize pdb structure. */ dbptr->altered = 0; dbptr->last_write = time(NULL); dbptr->write_interval = PDB_DEFAULT_WRITE_INTERVAL; dbptr->settings = PDB_DEFAULT_SETTINGS; #ifdef PDB_USING_THREADS dbptr->mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(dbptr->mutex, NULL); #endif if (file) { dbptr->file = (char*)malloc(sizeof(char) * (strlen(file) + 1)); strcpy(dbptr->file, file); } else dbptr->file = NULL; rntptr = pdb_get_type_info(ROOT_NODE_TYPE); if (!rntptr) { ERROR("Root node type is invalid."); free(dbptr->file); free(dbptr); pdb_close_file(fptr); return NULL; } dbptr->data = (*rntptr->create_cb)(NULL, NULL, NULL); /* * Load database (if file was specified). */ if (file) { pdb_standard_load_node(fptr, dbptr->data, NULL, &line); pdb_close_file(fptr); } return dbptr; }
/* * Write an integer node to disk. */ int pdb_write_int_node_cb(struct pdb* dbptr, FILE* fptr, struct pdb_node_t* nptr, int tabs) { struct pdb_node_types_t* tiptr; tiptr = pdb_get_type_info(INT_NODE_TYPE); fprintf(fptr, "%s%i%s%c\n", tiptr->open_token, *(int*)nptr->data, tiptr->close_token, PDB_TOKEN_TERM); return 1; }
/* * Standard node creation function to be used for * node creation callbacks if desired. * This will create and set everything but the data pointer. * It will also add the newly created child to the parent structure. */ void* pdb_standard_create_node(char* id, struct pdb_node_t* parent, int type) { struct pdb_node_t* nptr; struct pdb_node_types_t* tiptr; nptr = (struct pdb_node_t*)malloc(sizeof(struct pdb_node_t)); if (!nptr) { ERROR("Unable to allocate memory for node structure."); return NULL; } nptr->data = NULL; nptr->parent = parent; nptr->type = type; nptr->custom_free_cb = NULL; if (id) { nptr->id = (char*)malloc(sizeof(char) * (strlen(id) + 1)); strcpy(nptr->id, id); } else nptr->id = NULL; /* * Add child to parent structure. */ if (parent) { tiptr = pdb_get_type_info(parent->type); if (!tiptr->add_child_cb) { fprintf(stderr, "%s:%s():%i: Error: Unable to add child node to " "parent (type %i); not supported by parent.\n", __FILE__, __FUNCTION__, __LINE__, parent->type); free(nptr->id); free(nptr); return NULL; } else { if (!tiptr->add_child_cb(parent, id, nptr)) { fprintf(stderr, "%s:%s():%i: Error: Unable to add child node " "of type %i to parent node of type %i.\n", __FILE__, __FUNCTION__, __LINE__, tiptr->bitmask, type); free(nptr->id); free(nptr); return NULL; } } } return nptr; }
/* * Delete a node from its parent, but don't free the node. */ void pdb_del_node_from_parent(struct pdb_node_t* nptr) { struct pdb_node_types_t* tiptr; if (!nptr->parent) return; tiptr = pdb_get_type_info(nptr->parent->type); if (!tiptr->del_child_cb) { fprintf(stderr, "%s:%s():%i: Error: Cannot remove '%s' from parent; " "type %i does not support operation.\n", __FILE__, __FUNCTION__, __LINE__, nptr->id, nptr->parent->type); return; } tiptr->del_child_cb(nptr->parent, nptr); }
/* * Callback for loading an integer node. */ int pdb_load_int_node_cb(FILE* fptr, struct pdb_node_t* pptr, char** tok_arr, int* line) { struct pdb_node_types_t* tiptr; int* i = (int*)pptr->data; tiptr = pdb_get_type_info(INT_NODE_TYPE); /* * Read the integer from the file and set it. */ fscanf(fptr, tiptr->open_token, NULL); fscanf(fptr, "%i", i); fscanf(fptr, tiptr->close_token, NULL); return 1; }
/* * Free a given node based on its type and using the corresponding * free callback function. */ int pdb_free_node_cb(void* dptr) { struct pdb_node_t* nptr = dptr; struct pdb_node_types_t* tiptr; int ret = 0; if (nptr->custom_free_cb) { /* * Use the custom free callback rather than the default. */ ret = nptr->custom_free_cb(nptr->data); } else { tiptr = pdb_get_type_info(nptr->type); ret = tiptr->free_cb(nptr); } if (nptr->id) free(nptr->id); free(nptr); return ret; }
/* * Free a node and remove it from the parent. * * Use this function and NOT pdb_node_types_t->free_cb() * if you are deleting a single node from the structure * (e.g. pdb_del rather than pdb_unload). */ DLLEXP int pdb_free_node(struct pdb_node_t* nptr) { struct pdb_node_types_t* tiptr; int ret = 0; if (!nptr) return 0; /* * Only free the container if it is * not of type LINK_NODE_TYPE. */ if (nptr->type != LINK_NODE_TYPE) { /* * Free data container. */ if (nptr->custom_free_cb) { /* * Use the custom free callback rather than the default. */ ret = nptr->custom_free_cb(nptr); } else { tiptr = pdb_get_type_info(nptr->type); ret = tiptr->free_cb(nptr); } } nptr->data = NULL; /* * Remove from parent. */ pdb_del_node_from_parent(nptr); /* * Free node. */ if (nptr->id) free(nptr->id); free(nptr); return ret; }
/* * Callback for loading a hash node. */ int pdb_load_hash_node_cb(FILE* fptr, struct pdb_node_t* pptr, char** tok_arr, int* line) { char b; int i; struct pdb_node_types_t* tiptr = pdb_get_type_info(HASH_NODE_TYPE); /* * Move to the next non-white character. */ while (!feof(fptr)) { FPEEK(&b, fptr); if (!is_whitespace(b)) break; if (b == '\n') *line++; fgetc(fptr); } /* * Skip over the opening token. */ for (i = 0; i < (signed)strlen(tiptr->open_token); ++i) fgetc(fptr); /* * Get the size of the hash table. */ if (!fscanf(fptr, "%i;", &i)) { char* e = va(NULL, "Unable to load hash from file -- no size (line %i).\n", *line); ERROR(e); free(e); return 0; } pptr->data = (void*)hash_create(i); return pdb_standard_load_node(fptr, pptr, tok_arr, line); }
/* * Create a node link from path/key to tnptr. * Node links will be ignored when written to disk. */ DLLEXP int pdb_create_link(struct pdb* dbptr, char* path, char* key, struct pdb_node_t* tnptr) { struct pdb_node_t* nptr; struct pdb_node_types_t* tiptr; int ret; if (!dbptr) return 0; nptr = pdb_query_node(dbptr, path); if (!nptr) return 0; tiptr = pdb_get_type_info(LINK_NODE_TYPE); PDB_MUTEX_LOCK(dbptr); ret = (tiptr->create_cb(key, nptr, (char**)tnptr) ? 1 : 0); PDB_MUTEX_UNLOCK(dbptr); return ret; }
/* * Return the number of children a container has. */ DLLEXP int pdb_count_children(struct pdb* dbptr, char* path) { struct pdb_node_types_t* tiptr; struct pdb_node_t* nptr; int ret; if (!dbptr) return 0; nptr = pdb_query_node(dbptr, path); if (!nptr) return 0; PDB_MUTEX_LOCK(dbptr); tiptr = pdb_get_type_info(nptr->type); if (!tiptr) { /* * Play it safe -- container not empty. */ PDB_MUTEX_UNLOCK(dbptr); return 0; } if (!tiptr->count_children_cb) { /* * Again, playing it safe. */ PDB_MUTEX_UNLOCK(dbptr); return 0; } ret = tiptr->count_children_cb(nptr); PDB_MUTEX_UNLOCK(dbptr); return ret; }