wmii/libixp/ramfs.c
2006-01-21 18:38:39 +02:00

298 lines
5.3 KiB
C

/*
* (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ixp.h"
#include <cext.h>
int is_directory(File * f)
{
return !f->size && f->content;
}
File *ixp_create(IXPServer * s, char *path)
{
File *f, *p;
char *buf = strdup(path);
char *tok, *tok_ptr;
if (!path)
return 0;
/* cannot create a directory with empty name */
if (buf[strlen(buf) - 1] == '/')
return 0;
tok = strtok_r(buf, "/", &tok_ptr);
p = s->root;
f = p->content;
/* first determine the path as it exists already */
while (f && tok) {
if (!strcmp(f->name, tok)) {
tok = strtok_r(0, "/", &tok_ptr);
p = f;
if (tok && is_directory(f))
f = f->content;
else
break;
continue;
}
f = f->next;
}
if (p->size && tok) {
free(buf);
return 0; /* cannot create subdirectory, if file has
* content */
}
/* only create missing parts, if file is directory */
while (tok) {
f = (File *) cext_emallocz(sizeof(File));
f->name = strdup(tok);
f->parent = p;
if (p->content) {
p = p->content;
while (p->next) {
p = p->next;
}
p->next = f;
} else {
p->content = f;
}
p = f;
tok = strtok_r(0, "/", &tok_ptr);
}
free(buf);
return f;
}
static int comp_file_name(const void *f1, const void *f2)
{
File *f = (File *) f1;
File *p = (File *) f2;
return strcmp(*(char **) f->name, *(char **) p->name);
}
static char *xls(File * f)
{
File *p;
char *result = 0;
size_t size = 1; /* for \n */
int num = 0, i;
File **tmp;
for (p = f; p; p = p->next)
num++;
tmp = cext_emallocz(sizeof(File *) * num);
i = 0;
for (p = f; p; p = p->next) {
size += strlen(p->name) + 1;
if (is_directory(p))
size++;
tmp[i++] = p;
}
qsort(tmp, num, sizeof(File *), comp_file_name);
result = cext_emallocz(size);
for (i = 0; i < num; i++) {
strncat(result, tmp[i]->name, size);
if (is_directory(tmp[i]))
strncat(result, "/\n", size);
else
strncat(result, "\n", size);
}
free(tmp);
return result;
}
File *ixp_open(IXPServer * s, char *path)
{
File *f;
f = ixp_walk(s, path);
if (!f) {
set_error(s, "file does not exist");
return nil;
}
return f;
}
void ixp_close(IXPServer * s, int fd)
{
File *f = fd_to_file(s, fd);
if (!f)
set_error(s, "invalid file descriptor");
}
size_t
ixp_read(IXPServer * s, int fd, size_t offset, void *out_buf,
size_t out_buf_len)
{
File *f = fd_to_file(s, fd);
void *result = 0;
size_t len = 0, res_len = 0;
if (!f) {
set_error(s, "invalid file descriptor");
return 0;
}
/* callback */
if (f->before_read)
f->before_read(s, f);
if (is_directory(f)) {
result = xls(f->content);
res_len = strlen(result);
} else if (f->size) {
result = f->content;
res_len = f->size;
}
if (offset > res_len) {
set_error(s, "invalid offset when reading file");
if (is_directory(f))
free(result);
return 0;
}
if (result) {
len = res_len - offset;
if (len > out_buf_len)
len = out_buf_len;
memcpy(out_buf, (char *) result + offset, len);
if (is_directory(f))
free(result);
}
return len;
}
void
ixp_write(IXPServer * s, int fd, size_t offset, void *content,
size_t in_len)
{
File *f = fd_to_file(s, fd);
if (!f) {
set_error(s, "invalid file descriptor");
return;
}
if (is_directory(f)) {
/* we cannot write to directories */
set_error(s, "cannot write to a directory");
return;
}
if (in_len) {
/* offset 0 flushes the file */
if (!offset || (offset + in_len > f->size)) {
f->content = realloc(f->content, offset + in_len + 1);
f->size = offset + in_len;
}
memcpy((char *) f->content + offset, content, in_len);
/* internal EOF character */
((char *) f->content)[f->size] = 0;
} else if (!offset) {
/* blank file */
if (f->content)
free(f->content);
f->content = 0;
f->size = 0;
}
/* callback */
if (f->after_write)
f->after_write(s, f);
}
static void xremove(IXPServer * s, File * f)
{
if (!f)
return;
if (f->next) {
xremove(s, f->next);
if (s->errstr)
return;
}
if (!f->bind && is_directory(f)) {
xremove(s, f->content);
if (s->errstr)
return;
}
if (f->content && f->size) {
free(f->content);
}
if (f != s->root) {
if (f->name) {
free(f->name);
}
free(f);
}
}
void ixp_remove_file(IXPServer * s, File * f)
{
File *p, *n;
set_error(s, 0);
if (!f) {
set_error(s, "file does not exist");
return;
}
/* detach */
p = f->parent;
n = f->next;
f->next = 0;
if (p) {
if (p->content == f)
p->content = n;
else {
p = p->content;
while (p && (p->next != f))
p = p->next;
if (p)
p->next = n;
}
}
/* remove now */
xremove(s, f);
}
void ixp_remove(IXPServer * s, char *path)
{
ixp_remove_file(s, ixp_walk(s, path));
}
File *ixp_walk(IXPServer * s, char *path)
{
File *f = 0;
File *n;
char *buf;
char *tok, *tok_ptr;
if (!path) {
return 0;
}
buf = strdup(path);
tok = strtok_r(buf, "/", &tok_ptr);
f = s->root->content;
if (!tok && buf[0] == '/') {
f = s->root;
}
while (f && tok) {
n = f->next;
if (!strcmp(f->name, tok)) {
tok = strtok_r(0, "/", &tok_ptr);
if (tok && f->size)
return 0;
if (!tok)
break;
f = f->content;
continue;
}
f = n;
}
if (f && (path[strlen(path) - 1] == '/') && !is_directory(f))
f = 0;
free(buf);
return f;
}