blob: 947c8f1a4cd66b3cc9c89903503ad1cc84d426d1 [file] [log] [blame]
/* $Id: tmesh-util.c,v 1.2 2003/10/25 17:08:02 fredette Exp $ */
/* tmesh/tmesh-util.c - the tme shell utility functions: */
/*
* Copyright (c) 2003 Matt Fredette
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Matt Fredette.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <tme/common.h>
_TME_RCSID("$Id: tmesh-util.c,v 1.2 2003/10/25 17:08:02 fredette Exp $");
/* includes: */
#include "tmesh-impl.h"
/* macros: */
/* this looks up a pathname: */
int
_tmesh_fs_lookup(struct tmesh *tmesh,
char **_pathname,
struct tmesh_fs_dirent **_dir,
struct tmesh_fs_dirent **_entry,
char **_output,
int flags)
{
struct tmesh_fs_dirent *dir, *entry;
const char *pathname, *part_start, *part_end;
unsigned int part_length;
char c;
int rc;
/* get the pathname: */
pathname = *_pathname;
*_pathname = NULL;
/* initialize dir and entry. if the pathname begins with a '/',
start at the root directory, otherwise start in the current
directory: */
if (*pathname == '/') {
dir = tmesh->tmesh_root;
for (; *pathname == '/'; pathname++);
}
else {
dir = tmesh->tmesh_cwd;
}
entry = (*pathname == '\0' ? dir : NULL);
/* look up the parts of this pathname: */
part_start = part_end = pathname;
for (;;) {
c = *part_end;
/* forward slashes and NULs delimit pathname parts: */
if (c == '/'
|| c == '\0') {
/* if this pathname part has some length, look it up: */
if (part_end > part_start) {
part_length = part_end - part_start;
/* if there is a delayed descent, descend now: */
if (entry != NULL) {
dir = entry;
}
/* check all entries in this directory. remember that the
directory entries form a circular linked list: */
entry = dir;
for (;;) {
/* check this entry. if it matches, we're done: */
if (!memcmp(entry->tmesh_fs_dirent_name,
part_start,
part_length)
&& entry->tmesh_fs_dirent_name[part_length] == '\0') {
break;
}
/* if this was the last entry in the directory, stop: */
entry = entry->tmesh_fs_dirent_next;
if (entry == dir) {
entry = NULL;
break;
}
}
/* if we couldn't find a matching entry: */
if (entry == NULL) {
/* assume we're returning an error: */
rc = ENOENT;
/* if this was the last pathname part, and failure on the
last pathname part is OK, don't return an error: */
if (c == '\0'
&& (flags & TMESH_SEARCH_LAST_PART_OK)) {
*_pathname = (char *) part_start;
rc = TME_OK;
}
/* return what we know: */
*_dir = dir;
*_entry = entry;
if (rc != TME_OK) {
tme_output_append_raw(_output, part_start, part_length);
}
return (rc);
}
}
/* we did find a matching entry: */
assert(entry != NULL);
/* if the delimiter is a NUL, return success: */
if (c == '\0') {
*_dir = dir;
*_entry = entry;
return (TME_OK);
}
/* otherwise, the delimiter must be a slash: */
assert(c == '/');
/* if the entry is not a directory, return an error: */
if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_DIR) {
*_dir = dir;
*_entry = entry;
return (ENOTDIR);
}
/* start on another part: */
part_start = part_end = part_end + 1;
}
/* otherwise, this is not a delimiter, so just continue on: */
else {
part_end++;
}
}
/* NOTREACHED */
}
/* this creates a new directory entry and links it into a directory: */
struct tmesh_fs_dirent *
_tmesh_fs_link(struct tmesh_fs_dirent *parent, char *name, int type, void *value)
{
struct tmesh_fs_dirent *entry;
assert(parent->tmesh_fs_dirent_type == TMESH_FS_DIRENT_DIR);
/* create the new entry: */
entry = tme_new(struct tmesh_fs_dirent, 1);
entry->tmesh_fs_dirent_type = type;
entry->tmesh_fs_dirent_name = name;
entry->tmesh_fs_dirent_value = value;
/* link in the new entry: */
entry->tmesh_fs_dirent_next = parent;
entry->tmesh_fs_dirent_prev = parent->tmesh_fs_dirent_prev;
entry->tmesh_fs_dirent_next->tmesh_fs_dirent_prev = &entry->tmesh_fs_dirent_next;
*entry->tmesh_fs_dirent_prev = entry;
return (entry);
}
/* this unlinks a directory entry: */
void
_tmesh_fs_unlink(struct tmesh_fs_dirent *entry)
{
*entry->tmesh_fs_dirent_prev = entry->tmesh_fs_dirent_next;
entry->tmesh_fs_dirent_next->tmesh_fs_dirent_prev = entry->tmesh_fs_dirent_prev;
}
/* this creates a new directory: */
struct tmesh_fs_dirent *
_tmesh_fs_mkdir(struct tmesh_fs_dirent *parent, char *name)
{
struct tmesh_fs_dirent *dir;
/* create the "." directory entry: */
dir = tme_new(struct tmesh_fs_dirent, 1);
dir->tmesh_fs_dirent_next = dir;
dir->tmesh_fs_dirent_prev = &dir->tmesh_fs_dirent_next;
dir->tmesh_fs_dirent_type = TMESH_FS_DIRENT_DIR;
dir->tmesh_fs_dirent_name = tme_strdup(".");
dir->tmesh_fs_dirent_value = dir;
/* if we have a parent directory, create our entry in that directory,
otherwise, this is the root directory, so we're our own parent: */
if (parent != NULL) {
_tmesh_fs_link(parent, name, TMESH_FS_DIRENT_DIR, dir);
}
else {
parent = dir;
}
/* make the ".." directory entry: */
_tmesh_fs_link(dir, tme_strdup(".."), TMESH_FS_DIRENT_DIR, parent);
/* return the new directory: */
return (dir);
}
/* this recursively recovers a pathname: */
static unsigned int
_tmesh_fs_pathname(struct tmesh_fs_dirent *parent, int type, void *value,
char **_output,
struct tmesh_fs_dirent *parent_stop)
{
struct tmesh_fs_dirent *entry_2dots, *parent_parent, *entry;
unsigned int pathname_length;
/* get this parent's parent directory: */
entry_2dots = parent->tmesh_fs_dirent_next;
assert(parent->tmesh_fs_dirent_type == TMESH_FS_DIRENT_DIR);
assert(!strcmp(parent->tmesh_fs_dirent_name, "."));
assert(entry_2dots->tmesh_fs_dirent_type == TMESH_FS_DIRENT_DIR);
assert(!strcmp(entry_2dots->tmesh_fs_dirent_name, ".."));
parent_parent = entry_2dots->tmesh_fs_dirent_value;
/* if we've reached a common parent directory, this is a base case: */
if (parent == parent_stop) {
pathname_length = 0;
}
/* otherwise, if this parent is its own parent, this parent is the
root directory, which is always a base case: */
else if (parent_parent == parent) {
tme_output_append(_output, "/");
pathname_length = 1;
/* if the value we're looking for also happens to be the root
directory, we have nothing more to do: */
if (value == parent) {
assert(type == TMESH_FS_DIRENT_DIR);
return (pathname_length);
}
}
/* otherwise, recurse into the parent parent: */
else {
pathname_length =
_tmesh_fs_pathname(parent_parent, TMESH_FS_DIRENT_DIR, parent,
_output, parent_stop);
}
/* find the entry in the parent directory pointing to this value.
remember that the directory entries form a circular linked list: */
entry = parent;
for (;;) {
/* check this entry. if it matches, we're done: */
if (entry->tmesh_fs_dirent_value == value) {
assert(entry->tmesh_fs_dirent_type == type);
break;
}
/* if this was the last entry in the directory, the system
is inconsistent: */
entry = entry->tmesh_fs_dirent_next;
assert(entry != parent);
}
/* add in this name: */
if (pathname_length > 1) {
tme_output_append(_output, "/");
pathname_length++;
}
tme_output_append(_output, "%s", entry->tmesh_fs_dirent_name);
return (pathname_length + strlen(entry->tmesh_fs_dirent_name));
}
/* this recovers the pathname of a directory: */
void
_tmesh_fs_pathname_dir(struct tmesh_fs_dirent *dir,
char **_output,
struct tmesh_fs_dirent *parent_stop)
{
struct tmesh_fs_dirent *entry_2dots, *parent;
/* get this directory's parent directory: */
entry_2dots = dir->tmesh_fs_dirent_next;
assert(dir->tmesh_fs_dirent_type == TMESH_FS_DIRENT_DIR);
assert(!strcmp(dir->tmesh_fs_dirent_name, "."));
assert(entry_2dots->tmesh_fs_dirent_type == TMESH_FS_DIRENT_DIR);
assert(!strcmp(entry_2dots->tmesh_fs_dirent_name, ".."));
parent = entry_2dots->tmesh_fs_dirent_value;
/* output this directory's pathname: */
_tmesh_fs_pathname(parent, TMESH_FS_DIRENT_DIR, dir,
_output, parent_stop);
}
/* this recovers the pathname of an element: */
void
_tmesh_fs_pathname_element(struct tmesh_fs_element *element,
char **_output,
struct tmesh_fs_dirent *parent_stop)
{
_tmesh_fs_pathname(element->tmesh_fs_element_parent,
TMESH_FS_DIRENT_ELEMENT, element,
_output, parent_stop);
}
/* this allocates garbage-collectable memory: */
void *
_tmesh_gc_malloc(struct tmesh *tmesh, unsigned int size)
{
struct tmesh_gc_record *mem;
/* allocate the memory: */
mem = tme_new(struct tmesh_gc_record, 1);
mem->tmesh_gc_record_mem = tme_malloc(size);
/* add this memory to our list: */
mem->tmesh_gc_record_next = tmesh->tmesh_gc_record;
if (mem->tmesh_gc_record_next != NULL) {
mem->tmesh_gc_record_next->tmesh_gc_record_prev = &mem->tmesh_gc_record_next;
}
mem->tmesh_gc_record_prev = &tmesh->tmesh_gc_record;
tmesh->tmesh_gc_record = mem;
/* return the real buffer: */
return (mem->tmesh_gc_record_mem);
}
/* this reallocates garbage-collectable memory: */
void *
_tmesh_gc_realloc(struct tmesh *tmesh, void *p, unsigned int size)
{
struct tmesh_gc_record *mem;
/* recover the memory structure: */
for (mem = tmesh->tmesh_gc_record;
mem != NULL;
mem = mem->tmesh_gc_record_next) {
if (mem->tmesh_gc_record_mem == p) {
break;
}
}
assert(mem != NULL);
/* reallocate the memory: */
mem->tmesh_gc_record_mem = tme_realloc(p, size);
/* return the real buffer: */
return (mem->tmesh_gc_record_mem);
}
/* this frees or releases garbage-collectable memory: */
static void
__tmesh_gc_free(struct tmesh *tmesh, void *p, int release)
{
struct tmesh_gc_record *mem;
/* recover the memory structure: */
for (mem = tmesh->tmesh_gc_record;
mem != NULL;
mem = mem->tmesh_gc_record_next) {
if (mem->tmesh_gc_record_mem == p) {
break;
}
}
assert(mem != NULL);
/* remove this memory from our list: */
*mem->tmesh_gc_record_prev = mem->tmesh_gc_record_next;
if (mem->tmesh_gc_record_next != NULL) {
mem->tmesh_gc_record_next->tmesh_gc_record_prev = mem->tmesh_gc_record_prev;
}
/* free this memory if we're not releasing it: */
if (!release) {
tme_free(mem->tmesh_gc_record_mem);
}
tme_free(mem);
}
/* this frees garbage-collectable memory: */
void
_tmesh_gc_free(struct tmesh *tmesh, void *p)
{
__tmesh_gc_free(tmesh, p, FALSE);
}
/* this releases garbage-collectable memory: */
void
_tmesh_gc_release(struct tmesh *tmesh, void *p)
{
__tmesh_gc_free(tmesh, p, TRUE);
}
/* this releases an argv: */
void
_tmesh_gc_release_argv(struct tmesh *tmesh, struct tmesh_parser_argv *argv)
{
unsigned int argc;
char **args;
argc = argv->tmesh_parser_argv_argc;
args = argv->tmesh_parser_argv_argv;
assert(argc > 0);
_tmesh_gc_release(tmesh, args);
for (; argc-- > 0; ) {
_tmesh_gc_release(tmesh, *(args++));
}
}
/* this garbage-collects: */
void
_tmesh_gc_gc(struct tmesh *tmesh)
{
struct tmesh_gc_record *mem, *mem_next;
/* while we have memory to collect: */
for (mem = tmesh->tmesh_gc_record;
mem != NULL;
mem = mem_next) {
mem_next = mem->tmesh_gc_record_next;
tme_free(mem->tmesh_gc_record_mem);
tme_free(mem);
}
/* we don't have any memory allocated: */
tmesh->tmesh_gc_record = NULL;
}