/* -*- Mode: C -*-
======================================================================
FILE: icalfileset.c
CREATOR: eric 23 December 1999
$Id$
$Locker$
(C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
This program is free software; you can redistribute it and/or modify
it under the terms of either:
The LGPL as published by the Free Software Foundation, version
2.1, available at: http://www.fsf.org/copyleft/lesser.html
Or:
The Mozilla Public License Version 1.0. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
The Original Code is eric. The Initial Developer of the Original
Code is Eric Busboom
======================================================================*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "icalfileset.h"
#include "icalgauge.h"
#include <errno.h>
#include <sys/stat.h> /* for stat */
#include <unistd.h> /* for stat, getpid */
#include <stdlib.h>
#include <string.h>
#include <fcntl.h> /* for fcntl */
#include <unistd.h> /* for fcntl */
#include "icalfilesetimpl.h"
extern int errno;
int icalfileset_lock(icalfileset *cluster);
int icalfileset_unlock(icalfileset *cluster);
icalerrorenum icalfileset_read_file(icalfileset* cluster, mode_t mode);
int icalfileset_filesize(icalfileset* cluster);
icalerrorenum icalfileset_create_cluster(const char *path);
icalfileset* icalfileset_new_impl()
{
struct icalfileset_impl* impl;
if ( ( impl = (struct icalfileset_impl*)
malloc(sizeof(struct icalfileset_impl))) == 0) {
icalerror_set_errno(ICAL_NEWFAILED_ERROR);
errno = ENOMEM;
return 0;
}
memset(impl,0,sizeof(struct icalfileset_impl));
strcpy(impl->id,ICALFILESET_ID);
return impl;
}
icalfileset* icalfileset_new(const char* path)
{
return icalfileset_new_open(path, O_RDWR|O_CREAT, 0664);
}
icalfileset* icalfileset_new_open(const char* path, int flags, mode_t mode)
{
struct icalfileset_impl *impl = icalfileset_new_impl();
struct icaltimetype tt;
off_t cluster_file_size;
memset(&tt,0,sizeof(struct icaltimetype));
icalerror_clear_errno();
icalerror_check_arg_rz( (path!=0), "path");
if (impl == 0){
return 0;
}
impl->path = strdup(path);
cluster_file_size = icalfileset_filesize(impl);
if(cluster_file_size < 0){
icalfileset_free(impl);
return 0;
}
impl->fd = open(impl->path,flags, mode);
if (impl->fd < 0){
icalerror_set_errno(ICAL_FILE_ERROR);
icalfileset_free(impl);
return 0;
}
icalfileset_lock(impl);
if(cluster_file_size > 0 ){
icalerrorenum error;
if((error = icalfileset_read_file(impl,mode))!= ICAL_NO_ERROR){
icalfileset_free(impl);
return 0;
}
}
if(impl->cluster == 0){
impl->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
}
return impl;
}
char* icalfileset_read_from_file(char *s, size_t size, void *d)
{
char* p = s;
int fd = (int)d;
/* Simulate fgets -- read single characters and stop at '\n' */
for(p=s; p<s+size-1;p++){
if(read(fd,p,1) != 1 || *p=='\n'){
p++;
break;
}
}
*p = '\0';
if(*s == 0){
return 0;
} else {
return s;
}
}
icalerrorenum icalfileset_read_file(icalfileset* cluster,mode_t mode)
{
icalparser *parser;
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
parser = icalparser_new();
icalparser_set_gen_data(parser,(void*)impl->fd);
impl->cluster = icalparser_parse(parser,icalfileset_read_from_file);
icalparser_free(parser);
if (impl->cluster == 0 || icalerrno != ICAL_NO_ERROR){
icalerror_set_errno(ICAL_PARSE_ERROR);
return ICAL_PARSE_ERROR;
}
if (icalcomponent_isa(impl->cluster) != ICAL_XROOT_COMPONENT){
/* The parser got a single component, so it did not put it in
an XROOT. */
icalcomponent *cl = impl->cluster;
impl->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
icalcomponent_add_component(impl->cluster,cl);
}
return ICAL_NO_ERROR;
}
int icalfileset_filesize(icalfileset* cluster)
{
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
int cluster_file_size;
struct stat sbuf;
if (stat(impl->path,&sbuf) != 0){
/* A file by the given name does not exist, or there was
another error */
cluster_file_size = 0;
if (errno == ENOENT) {
/* It was because the file does not exist */
return 0;
} else {
/* It was because of another error */
icalerror_set_errno(ICAL_FILE_ERROR);
return -1;
}
} else {
/* A file by the given name exists, but is it a regular file? */
if (!S_ISREG(sbuf.st_mode)){
/* Nope, not a regular file */
icalerror_set_errno(ICAL_FILE_ERROR);
return -1;
} else {
/* Lets assume that it is a file of the right type */
return sbuf.st_size;
}
}
/*return -1; not reached*/
}
void icalfileset_free(icalfileset* cluster)
{
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
icalerror_check_arg_rv((cluster!=0),"cluster");
if (impl->cluster != 0){
icalfileset_commit(cluster);
icalcomponent_free(impl->cluster);
impl->cluster=0;
}
if(impl->fd > 0){
icalfileset_unlock(impl);
close(impl->fd);
impl->fd = -1;
}
if(impl->path != 0){
free(impl->path);
impl->path = 0;
}
free(impl);
}
const char* icalfileset_path(icalfileset* cluster)
{
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
icalerror_check_arg_rz((cluster!=0),"cluster");
return impl->path;
}
int icalfileset_lock(icalfileset *cluster)
{
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
struct flock lock;
int rtrn;
icalerror_check_arg_rz((impl->fd>0),"impl->fd");
errno = 0;
lock.l_type = F_WRLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
lock.l_start = 0; /* byte offset relative to l_whence */
lock.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = 0; /* #bytes (0 means to EOF) */
rtrn = fcntl(impl->fd, F_SETLKW, &lock);
return rtrn;
}
int icalfileset_unlock(icalfileset *cluster)
{
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
struct flock lock;
icalerror_check_arg_rz((impl->fd>0),"impl->fd");
lock.l_type = F_WRLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
lock.l_start = 0; /* byte offset relative to l_whence */
lock.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = 0; /* #bytes (0 means to EOF) */
return (fcntl(impl->fd, F_UNLCK, &lock));
}
#ifdef ICAL_SAFESAVES
int icalfileset_safe_saves=1;
#else
int icalfileset_safe_saves=0;
#endif
icalerrorenum icalfileset_commit(icalfileset* cluster)
{
char tmp[ICAL_PATH_MAX];
char *str;
icalcomponent *c;
off_t write_size=0;
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
icalerror_check_arg_re((impl!=0),"cluster",ICAL_BADARG_ERROR);
icalerror_check_arg_re((impl->fd>0),"impl->fd is invalid",
ICAL_INTERNAL_ERROR) ;
if (impl->changed == 0 ){
return ICAL_NO_ERROR;
}
if(icalfileset_safe_saves == 1){
snprintf(tmp,ICAL_PATH_MAX,"cp %s %s.bak",impl->path,impl->path);
if(system(tmp) < 0){
icalerror_set_errno(ICAL_FILE_ERROR);
return ICAL_FILE_ERROR;
}
}
if(lseek(impl->fd,SEEK_SET,0) < 0){
icalerror_set_errno(ICAL_FILE_ERROR);
return ICAL_FILE_ERROR;
}
for(c = icalcomponent_get_first_component(impl->cluster,ICAL_ANY_COMPONENT);
c != 0;
c = icalcomponent_get_next_component(impl->cluster,ICAL_ANY_COMPONENT)){
int sz;
str = icalcomponent_as_ical_string(c);
sz=write(impl->fd,str,strlen(str));
if ( sz != strlen(str)){
perror("write");
icalerror_set_errno(ICAL_FILE_ERROR);
return ICAL_FILE_ERROR;
}
write_size += sz;
}
impl->changed = 0;
if(ftruncate(impl->fd,write_size) < 0){
return ICAL_FILE_ERROR;
}
return ICAL_NO_ERROR;
}
void icalfileset_mark(icalfileset* cluster){
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
icalerror_check_arg_rv((impl!=0),"cluster");
impl->changed = 1;
}
icalcomponent* icalfileset_get_component(icalfileset* cluster){
struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
icalerror_check_arg_re((impl!=0),"cluster",ICAL_BADARG_ERROR);
return impl->cluster;
}
/* manipulate the components in the cluster */
icalerrorenum icalfileset_add_component(icalfileset *cluster,
icalcomponent* child)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
icalerror_check_arg_re((cluster!=0),"cluster", ICAL_BADARG_ERROR);
icalerror_check_arg_re((child!=0),"child",ICAL_BADARG_ERROR);
icalcomponent_add_component(impl->cluster,child);
icalfileset_mark(cluster);
return ICAL_NO_ERROR;
}
icalerrorenum icalfileset_remove_component(icalfileset *cluster,
icalcomponent* child)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
icalerror_check_arg_re((cluster!=0),"cluster",ICAL_BADARG_ERROR);
icalerror_check_arg_re((child!=0),"child",ICAL_BADARG_ERROR);
icalcomponent_remove_component(impl->cluster,child);
icalfileset_mark(cluster);
return ICAL_NO_ERROR;
}
int icalfileset_count_components(icalfileset *cluster,
icalcomponent_kind kind)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
if(cluster == 0){
icalerror_set_errno(ICAL_BADARG_ERROR);
return -1;
}
return icalcomponent_count_components(impl->cluster,kind);
}
icalerrorenum icalfileset_select(icalfileset* set, icalgauge* gauge)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)set;
icalerror_check_arg_re(gauge!=0,"guage",ICAL_BADARG_ERROR);
impl->gauge = gauge;
return ICAL_NO_ERROR;
}
void icalfileset_clear(icalfileset* gauge)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)gauge;
impl->gauge = 0;
}
icalcomponent* icalfileset_fetch(icalfileset* store,const char* uid)
{
icalcompiter i;
struct icalfileset_impl* impl = (struct icalfileset_impl*)store;
for(i = icalcomponent_begin_component(impl->cluster,ICAL_ANY_COMPONENT);
icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){
icalcomponent *this = icalcompiter_deref(&i);
icalcomponent *inner = icalcomponent_get_first_real_component(this);
icalcomponent *p;
const char *this_uid;
if(inner != 0){
p = icalcomponent_get_first_property(inner,ICAL_UID_PROPERTY);
this_uid = icalproperty_get_uid(p);
if(this_uid==0){
icalerror_warn("icalfileset_fetch found a component with no UID");
continue;
}
if (strcmp(uid,this_uid)==0){
return this;
}
}
}
return 0;
}
int icalfileset_has_uid(icalfileset* store,const char* uid)
{
assert(0); /* HACK, not implemented */
return 0;
}
/******* support routines for icalfileset_fetch_match *********/
struct icalfileset_id{
char* uid;
char* recurrence_id;
int sequence;
};
void icalfileset_id_free(struct icalfileset_id *id)
{
if(id->recurrence_id != 0){
free(id->recurrence_id);
}
if(id->uid != 0){
free(id->uid);
}
}
struct icalfileset_id icalfileset_get_id(icalcomponent* comp)
{
icalcomponent *inner;
struct icalfileset_id id;
icalproperty *p;
inner = icalcomponent_get_first_real_component(comp);
p = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY);
assert(p!= 0);
id.uid = strdup(icalproperty_get_uid(p));
p = icalcomponent_get_first_property(inner, ICAL_SEQUENCE_PROPERTY);
if(p == 0) {
id.sequence = 0;
} else {
id.sequence = icalproperty_get_sequence(p);
}
p = icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY);
if (p == 0){
id.recurrence_id = 0;
} else {
icalvalue *v;
v = icalproperty_get_value(p);
id.recurrence_id = strdup(icalvalue_as_ical_string(v));
assert(id.recurrence_id != 0);
}
return id;
}
/* Find the component that is related to the given
component. Currently, it just matches based on UID and
RECURRENCE-ID */
icalcomponent* icalfileset_fetch_match(icalfileset* set, icalcomponent *comp)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)set;
icalcompiter i;
struct icalfileset_id comp_id, match_id;
comp_id = icalfileset_get_id(comp);
for(i = icalcomponent_begin_component(impl->cluster,ICAL_ANY_COMPONENT);
icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){
icalcomponent *match = icalcompiter_deref(&i);
match_id = icalfileset_get_id(match);
if(strcmp(comp_id.uid, match_id.uid) == 0 &&
( comp_id.recurrence_id ==0 ||
strcmp(comp_id.recurrence_id, match_id.recurrence_id) ==0 )){
/* HACK. What to do with SEQUENCE? */
icalfileset_id_free(&match_id);
icalfileset_id_free(&comp_id);
return match;
}
icalfileset_id_free(&match_id);
}
icalfileset_id_free(&comp_id);
return 0;
}
icalerrorenum icalfileset_modify(icalfileset* store, icalcomponent *old,
icalcomponent *new)
{
assert(0); /* HACK, not implemented */
return ICAL_NO_ERROR;
}
/* Iterate through components */
icalcomponent* icalfileset_get_current_component (icalfileset* cluster)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
icalerror_check_arg_rz((cluster!=0),"cluster");
return icalcomponent_get_current_component(impl->cluster);
}
icalcomponent* icalfileset_get_first_component(icalfileset* cluster)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
icalcomponent *c=0;
icalerror_check_arg_rz((cluster!=0),"cluster");
do {
if (c == 0){
c = icalcomponent_get_first_component(impl->cluster,
ICAL_ANY_COMPONENT);
} else {
c = icalcomponent_get_next_component(impl->cluster,
ICAL_ANY_COMPONENT);
}
if(c != 0 && (impl->gauge == 0 ||
icalgauge_compare(impl->gauge,c) == 1)){
return c;
}
} while(c != 0);
return 0;
}
icalcomponent* icalfileset_get_next_component(icalfileset* cluster)
{
struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
icalcomponent *c;
icalerror_check_arg_rz((cluster!=0),"cluster");
do {
c = icalcomponent_get_next_component(impl->cluster,
ICAL_ANY_COMPONENT);
if(c != 0 && (impl->gauge == 0 ||
icalgauge_compare(impl->gauge,c) == 1)){
return c;
}
} while(c != 0);
return 0;
}