mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
452 lines
13 KiB
C
452 lines
13 KiB
C
/* $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;
|
|
}
|