From 8ab1924ca362f5a32c155c7ec65f6ec5853ed76a Mon Sep 17 00:00:00 2001 From: Marcus Boerger Date: Sat, 22 Feb 2003 17:20:06 +0000 Subject: [PATCH] @Added dba handler inifiles to support ini files. (Marcus) --- ext/dba/config.m4 | 21 +- ext/dba/dba.c | 39 +- ext/dba/dba_inifile.c | 186 ++++++++++ ext/dba/libinifile/inifile.c | 629 +++++++++++++++++++++++++++++++++ ext/dba/libinifile/inifile.h | 67 ++++ ext/dba/php_inifile.h | 12 + ext/dba/tests/dba_inifile.phpt | 29 ++ 7 files changed, 977 insertions(+), 6 deletions(-) create mode 100644 ext/dba/dba_inifile.c create mode 100644 ext/dba/libinifile/inifile.c create mode 100644 ext/dba/libinifile/inifile.h create mode 100644 ext/dba/php_inifile.h create mode 100644 ext/dba/tests/dba_inifile.phpt diff --git a/ext/dba/config.m4 b/ext/dba/config.m4 index b1b9f0d51a..5594bfb725 100644 --- a/ext/dba/config.m4 +++ b/ext/dba/config.m4 @@ -380,6 +380,25 @@ AC_ARG_WITH(cdb, ]) AC_DBA_STD_RESULT(cdb) +AC_DEFUN(PHP_DBA_BUILTIN_INI,[ + PHP_ADD_BUILD_DIR($ext_builddir/libini) + AC_DEFINE(DBA_INIFILE, 1, [ ]) + ini_sources="libinifile/inifile.c" + THIS_RESULT="builtin" +]) + +AC_ARG_WITH(inifile, +[ --with-inifile DBA: Include INI support],[ + if test "$withval" != "no"; then + PHP_DBA_BUILTIN_INI + fi +],[ + if test "$PHP_DBA" != "no"; then + PHP_DBA_BUILTIN_INI + fi +]) +AC_DBA_STD_RESULT(inifile,INI File) + AC_DEFUN(PHP_DBA_BUILTIN_FLATFILE,[ PHP_ADD_BUILD_DIR($ext_builddir/libflatfile) AC_DEFINE(DBA_FLATFILE, 1, [ ]) @@ -406,7 +425,7 @@ AC_MSG_CHECKING(whether to enable DBA interface) if test "$HAVE_DBA" = "1"; then AC_MSG_RESULT(yes) AC_DEFINE(HAVE_DBA, 1, [ ]) - PHP_NEW_EXTENSION(dba, dba.c dba_cdb.c dba_db2.c dba_dbm.c dba_gdbm.c dba_ndbm.c dba_db3.c dba_db4.c dba_flatfile.c $cdb_sources $flat_sources, $ext_shared) + PHP_NEW_EXTENSION(dba, dba.c dba_cdb.c dba_db2.c dba_dbm.c dba_gdbm.c dba_ndbm.c dba_db3.c dba_db4.c dba_flatfile.c dba_inifile.c $cdb_sources $flat_sources $ini_sources, $ext_shared) PHP_SUBST(DBA_SHARED_LIBADD) else AC_MSG_RESULT(no) diff --git a/ext/dba/dba.c b/ext/dba/dba.c index 45f9307d79..77196fd0f6 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -47,6 +47,7 @@ #include "php_db3.h" #include "php_db4.h" #include "php_flatfile.h" +#include "php_inifile.h" /* {{{ dba_functions[] */ @@ -189,6 +190,9 @@ static dba_handler handler[] = { #if DBA_DB4 DBA_HND(db4, DBA_LOCK_ALL) /* No lock in lib */ #endif +#if DBA_INIFILE + DBA_HND(inifile, DBA_STREAM_OPEN|DBA_LOCK_ALL) /* No lock in lib */ +#endif #if DBA_FLATFILE DBA_HND(flatfile, DBA_STREAM_OPEN|DBA_LOCK_ALL) /* No lock in lib */ #endif @@ -212,6 +216,7 @@ static dba_handler handler[] = { #else #define DBA_DEFAULT "" #endif +/* cdb/cdb_make and ini are no option here */ ZEND_BEGIN_MODULE_GLOBALS(dba) char *default_handler; @@ -729,8 +734,29 @@ PHP_FUNCTION(dba_fetch) int len = 0; DBA_ID_GET2_3; - if (ac==3 && strcmp(info->hnd->name, "cdb")) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Handler %s does not support optional skip parameter", info->hnd->name); + if (ac==3) { + if (!strcmp(info->hnd->name, "cdb")) { + if (skip < 0) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Handler %s accepts only skip values greater than or equal to zero, using skip=0", info->hnd->name); + skip = 0; + } + } else if (!strcmp(info->hnd->name, "inifile")) { + /* "-1" is compareable to 0 but allows a non restrictive + * access which is fater. For example 'inifile' uses this + * to allow faster access when the key was already found + * using firstkey/nextkey. However explicitly setting the + * value to 0 ensures the first value. + */ + if (skip < -1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Handler %s accepts only skip value -1 and greater, using skip=0", info->hnd->name); + skip = 0; + } + } else { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Handler %s does not support optional skip parameter, the value will be ignored", info->hnd->name); + skip = 0; + } + } else { + skip = 0; } if((val = info->hnd->fetch(info, VALLEN(key), skip, &len TSRMLS_CC)) != NULL) { if (val && PG(magic_quotes_runtime)) { @@ -773,7 +799,8 @@ PHP_FUNCTION(dba_nextkey) /* }}} */ /* {{{ proto bool dba_delete(string key, int handle) - Deletes the entry associated with key */ + Deletes the entry associated with key + If inifile: remove all other key lines */ PHP_FUNCTION(dba_delete) { DBA_ID_GET2; @@ -787,7 +814,8 @@ PHP_FUNCTION(dba_delete) /* }}} */ /* {{{ proto bool dba_insert(string key, string value, int handle) - Inserts value as key, returns false, if key exists already */ + If not inifile: Insert value as key, return false, if key exists already + If inifile: Add vakue as key (next instance of key) */ PHP_FUNCTION(dba_insert) { php_dba_update(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); @@ -795,7 +823,8 @@ PHP_FUNCTION(dba_insert) /* }}} */ /* {{{ proto bool dba_replace(string key, string value, int handle) - Inserts value as key, replaces key, if key exists already */ + Inserts value as key, replaces key, if key exists already + If inifile: remove all other key lines */ PHP_FUNCTION(dba_replace) { php_dba_update(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); diff --git a/ext/dba/dba_inifile.c b/ext/dba/dba_inifile.c new file mode 100644 index 0000000000..2a12179e78 --- /dev/null +++ b/ext/dba/dba_inifile.c @@ -0,0 +1,186 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +#if DBA_INIFILE +#include "php_inifile.h" + +#include "libinifile/inifile.h" + +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include + +#define INIFILE_DATA \ + inifile *dba = info->dbf + +#define INIFILE_GKEY \ + key_type ini_key = inifile_key_split((char*)key) /* keylen not needed here */ + +#define INIFILE_DONE \ + inifile_key_free(&ini_key) + +DBA_OPEN_FUNC(inifile) +{ + info->dbf = inifile_alloc(info->fp, info->mode == DBA_READER, info->flags&DBA_PERSISTENT TSRMLS_CC); + + return info->dbf ? SUCCESS : FAILURE; +} + +DBA_CLOSE_FUNC(inifile) +{ + INIFILE_DATA; + + inifile_free(dba, info->flags&DBA_PERSISTENT); +} + +DBA_FETCH_FUNC(inifile) +{ + val_type ini_val; + + INIFILE_DATA; + INIFILE_GKEY; + + ini_val = inifile_fetch(dba, &ini_key, skip TSRMLS_CC); + *newlen = ini_val.value ? strlen(ini_val.value) : 0; + INIFILE_DONE; + return ini_val.value; +} + +DBA_UPDATE_FUNC(inifile) +{ + val_type ini_val; + int res; + + INIFILE_DATA; + INIFILE_GKEY; + + ini_val.value = val; + + if (mode == 1) { + res = inifile_append(dba, &ini_key, &ini_val TSRMLS_CC); + } else { + res = inifile_replace(dba, &ini_key, &ini_val TSRMLS_CC); + } + INIFILE_DONE; + switch(res) { + case -1: + php_error_docref1(NULL TSRMLS_CC, key, E_WARNING, "Operation not possible"); + return FAILURE; + default: + case 0: + return SUCCESS; + case 1: + php_error_docref1(NULL TSRMLS_CC, key, E_WARNING, "Key already exists"); + return SUCCESS; + } +} + +DBA_EXISTS_FUNC(inifile) +{ + val_type ini_val; + + INIFILE_DATA; + INIFILE_GKEY; + + ini_val = inifile_fetch(dba, &ini_key, 0 TSRMLS_CC); + INIFILE_DONE; + if (ini_val.value) { + inifile_val_free(&ini_val); + return SUCCESS; + } + return FAILURE; +} + +DBA_DELETE_FUNC(inifile) +{ + INIFILE_DATA; + INIFILE_GKEY; + int res = inifile_delete(dba, &ini_key TSRMLS_CC); + + INIFILE_DONE; + return (res == -1 ? FAILURE : SUCCESS); +} + +DBA_FIRSTKEY_FUNC(inifile) +{ + INIFILE_DATA; + + if (inifile_firstkey(dba TSRMLS_CC)) { + char *result = inifile_key_string(&dba->curr.key); + *newlen = strlen(result); + return result; + } else { + return NULL; + } +} + +DBA_NEXTKEY_FUNC(inifile) +{ + INIFILE_DATA; + + if (!dba->curr.key.group && !dba->curr.key.name) { + return NULL; + } + + if (inifile_nextkey(dba TSRMLS_CC)) { + char *result = inifile_key_string(&dba->curr.key); + *newlen = strlen(result); + return result; + } else { + return NULL; + } +} + +DBA_OPTIMIZE_FUNC(inifile) +{ + /* dummy */ + return SUCCESS; +} + +DBA_SYNC_FUNC(inifile) +{ + /* dummy */ + return SUCCESS; +} + +DBA_INFO_FUNC(inifile) +{ + return estrdup(inifile_version()); +} + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/dba/libinifile/inifile.c b/ext/dba/libinifile/inifile.c new file mode 100644 index 0000000000..76ac982cbf --- /dev/null +++ b/ext/dba/libinifile/inifile.c @@ -0,0 +1,629 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_globals.h" +#include "safe_mode.h" +#include "php_network.h" + +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif + +#include "inifile.h" + +/* ret = -1 means that database was opened for read-only + * ret = 0 success + * ret = 1 key already exists - nothing done + */ + +/* {{{ inifile_version */ +char *inifile_version() +{ + return "1.0, $Revision$"; +} +/* }}} */ + +/* {{{ inifile_free_key */ +void inifile_key_free(key_type *key) +{ + if (key->group) { + efree(key->group); + } + if (key->name) { + efree(key->name); + } + memset(key, 0, sizeof(key_type)); +} +/* }}} */ + +/* {{{ inifile_free_val */ +void inifile_val_free(val_type *val) +{ + if (val->value) { + efree(val->value); + } + memset(val, 0, sizeof(val_type)); +} +/* }}} */ + +/* {{{ inifile_free_val */ +void inifile_line_free(line_type *ln) +{ + inifile_key_free(&ln->key); + inifile_val_free(&ln->val); + ln->pos = 0; +} +/* }}} */ + +/* {{{ inifile_alloc */ +inifile * inifile_alloc(php_stream *fp, int readonly, int persistent TSRMLS_DC) +{ + inifile *dba; + int fd = 0; + + if (!readonly) { + if (php_stream_is(fp, PHP_STREAM_IS_SOCKET)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate sockets"); + return NULL; + } + if (SUCCESS != php_stream_cast(fp, PHP_STREAM_AS_FD, (void*)&fd, 1)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not cast stream"); + return NULL; + } + } + dba = pemalloc(sizeof(inifile), persistent); + memset(dba, 0, sizeof(inifile)); + dba->fp = fp; + dba->fd = fd; + dba->readonly = readonly; + return dba; +} +/* }}} */ + +/* {{{ inifile_free */ +void inifile_free(inifile *dba, int persistent) +{ + if (dba) { + inifile_line_free(&dba->curr); + inifile_line_free(&dba->next); + pefree(dba, persistent); + } +} +/* }}} */ + +/* {{{ inifile_key_split */ +key_type inifile_key_split(const char *group_name) +{ + key_type key; + char *name; + + if (group_name[0] == '[' && (name = strchr(group_name, ']')) != NULL) { + key.group = estrndup(group_name+1, name - (group_name + 1)); + key.name = estrdup(name+1); + } else { + key.group = estrdup(""); + key.name = estrdup(group_name); + } + return key; +} +/* }}} */ + +/* {{{ inifile_key_string */ +char * inifile_key_string(const key_type *key) +{ + if (key->group && *key->group) { + char *result; + spprintf(&result, 0, "[%s]%s", key->group, key->name ? key->name : ""); + return result; + } else if (key->name) { + return estrdup(key->name); + } else { + return NULL; + } +} +/* }}} */ + +/* {{{ etrim */ +static char *etrim(const char *str) +{ + char *val; + size_t l; + + if (!str) { + return NULL; + } + val = (char*)str; + while (strchr(" \t\r\n", *val)) { + val++; + } + l = strlen(val); + while (l && (strchr(" \t\r\n", val[l-1]))) { + l--; + } + return estrndup(val, l); +} +/* }}} */ + +/* {{{ inifile_findkey + */ +static int inifile_read(inifile *dba, line_type *ln TSRMLS_DC) { + char *fline; + char *pos; + + inifile_val_free(&ln->val); + while ((fline = php_stream_gets(dba->fp, NULL, 0)) != NULL) { + if (fline) { + if (fline[0] == '[') { + /* A value name cannot start with '[' + * So either we find a ']' or we found an error + */ + pos = strchr(fline+1, ']'); + if (pos) { + *pos = '\0'; + inifile_key_free(&ln->key); + ln->key.group = etrim(fline+1); + ln->key.name = estrdup(""); + ln->pos = php_stream_tell(dba->fp); + efree(fline); + return 1; + } else { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "The following group was started with '[' but not ended with ']': '%s'", fline+1); + efree(fline); + continue; + } + } else { + pos = strchr(fline, '='); + if (pos) { + *pos = '\0'; + /* keep group or make empty if not existent */ + if (!ln->key.group) { + ln->key.group = estrdup(""); + } + if (ln->key.name) { + efree(ln->key.name); + } + ln->key.name = etrim(fline); + ln->val.value = etrim(pos+1); + ln->pos = php_stream_tell(dba->fp); + efree(fline); + return 1; + } else { + /* simply ignore lines without '=' + * those should be comments + */ + efree(fline); + continue; + } + } + } + } + inifile_line_free(ln); + return 0; +} +/* }}} */ + +/* {{{ inifile_key_cmp */ +/* 0 = EQUAL + * 1 = GROUP-EQUAL,NAME-DIFFERENT + * 2 = DIFFERENT + */ +static int inifile_key_cmp(const key_type *k1, const key_type *k2 TSRMLS_DC) +{ + assert(k1->group && k1->name && k2->group && k2->name); + + if (!strcmp(k1->group, k2->group)) { + if (!strcmp(k1->name, k2->name)) { + return 0; + } else { + return 1; + } + } else { + return 2; + } +} +/* }}} */ + +/* {{{ inifile_fetch + */ +val_type inifile_fetch(inifile *dba, const key_type *key, int skip TSRMLS_DC) { + line_type ln = {{NULL,NULL},{NULL}}; + val_type val; + int res, grp_eq = 0; + + if (skip == -1) { + if (dba->next.key.group && dba->next.key.name && !inifile_key_cmp(&dba->next.key, key TSRMLS_CC)) { + /* we got it already from last fetch */ + val.value = estrdup(dba->next.val.value ? dba->next.val.value : ""); + /* allow faster access by automatically getting next key */ + php_stream_seek(dba->fp, dba->next.pos, SEEK_SET); + ln.key.group = estrdup(dba->next.key.group ? dba->next.key.group : ""); + inifile_read(dba, &ln TSRMLS_CC); + inifile_line_free(&dba->next); + dba->next = ln; + return val; + } else if (dba->curr.key.group && dba->curr.key.name && !inifile_key_cmp(&dba->curr.key, key TSRMLS_CC)) { + /* we got it already from firstkey/lastkey */ + /* + * this optimisation does not work when firstkey/nextkey found + * any instance other than the one instance wanted. + */ + val.value = estrdup(dba->curr.val.value ? dba->curr.val.value : ""); + /* allow faster access by automatically getting next key + * we must use the line pointer 'next' since we cannot change the + * line pointer of firstkey/nextkey + */ + php_stream_seek(dba->fp, dba->curr.pos, SEEK_SET); + ln.key.group = estrdup(dba->curr.key.group ? dba->curr.key.group : ""); + inifile_read(dba, &ln TSRMLS_CC); + inifile_line_free(&dba->next); + dba->next = ln; + return val; + } + } + + /* the slow way: restart and seacrch */ + if (skip != -1 || !dba->next.key.group || !dba->next.key.name || inifile_key_cmp(&dba->next.key, key TSRMLS_CC)) { + /* specific instance or not same key -> restart search */ + php_stream_rewind(dba->fp); + } + inifile_line_free(&dba->next); + if (skip == -1) { + skip = 0; + } + while(inifile_read(dba, &ln TSRMLS_CC)) { + if (!(res=inifile_key_cmp(&ln.key, key TSRMLS_CC))) { + if (!skip) { + val.value = estrdup(ln.val.value ? ln.val.value : ""); + /* allow faster access by automatically getting next key */ + inifile_read(dba, &ln TSRMLS_CC); + inifile_line_free(&dba->next); + dba->next = ln; + return val; + } + skip--; + } else if (res == 1) { + grp_eq = 1; + } else if (grp_eq) { + /* we are leaving group now: that means we cannot find the key */ + break; + } + } + inifile_line_free(&ln); + inifile_line_free(&dba->next); + return ln.val; +} +/* }}} */ + +/* {{{ inifile_firstkey + */ +int inifile_firstkey(inifile *dba TSRMLS_DC) { + inifile_line_free(&dba->curr); + dba->curr.pos = 0; + return inifile_nextkey(dba TSRMLS_CC); +} +/* }}} */ + +/* {{{ inifile_nextkey + */ +int inifile_nextkey(inifile *dba TSRMLS_DC) { + line_type ln = {{NULL,NULL},{NULL}}; + + /*inifile_line_free(&dba->next); ??? */ + php_stream_seek(dba->fp, dba->curr.pos, SEEK_SET); + ln.key.group = estrdup(dba->curr.key.group ? dba->curr.key.group : ""); + inifile_read(dba, &ln TSRMLS_CC); + inifile_line_free(&dba->curr); + dba->curr = ln; + return ln.key.group || ln.key.name; +} +/* }}} */ + +/* {{{ inifile_truncate + */ +static int inifile_truncate(inifile *dba, size_t size TSRMLS_DC) +{ + int res; + + if ((res=ftruncate(dba->fd, size)) != 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error in ftruncate: %d", res); + return FAILURE; + } + php_stream_seek(dba->fp, size, SEEK_SET); + return SUCCESS; +} +/* }}} */ + +/* {{{ inifile_find_group + * if found pos_grp_start points to "[group_name]" + */ +static int inifile_find_group(inifile *dba, const key_type *key, size_t *pos_grp_start TSRMLS_DC) +{ + int ret = FAILURE; + + php_stream_flush(dba->fp); + php_stream_seek(dba->fp, 0, SEEK_SET); + inifile_line_free(&dba->curr); + inifile_line_free(&dba->next); + + if (key->group && strlen(key->group)) { + int res; + line_type ln = {{NULL,NULL},{NULL}}; + + res = 1; + while(inifile_read(dba, &ln TSRMLS_CC)) { + if ((res=inifile_key_cmp(&ln.key, key TSRMLS_CC)) < 2) { +php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found group: %d", *pos_grp_start); + ret = SUCCESS; + break; + } + *pos_grp_start = php_stream_tell(dba->fp); + } + inifile_line_free(&ln); + } else { + *pos_grp_start = 0; + ret = SUCCESS; + } + if (ret == FAILURE) { + *pos_grp_start = php_stream_tell(dba->fp); + } + return ret; +} +/* }}} */ + +/* {{{ inifile_next_group + * only valid after a call to inifile_find_group + * if any next group is found pos_grp_start points to "[group_name]" or whitespace before that + */ +static int inifile_next_group(inifile *dba, const key_type *key, size_t *pos_grp_start TSRMLS_DC) +{ + int ret = FAILURE; + line_type ln = {{NULL,NULL},{NULL}}; + + *pos_grp_start = php_stream_tell(dba->fp); + ln.key.group = estrdup(key->group); + while(inifile_read(dba, &ln TSRMLS_CC)) { + if (inifile_key_cmp(&ln.key, key TSRMLS_CC) == 2) { +php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Found next group: %d", *pos_grp_start); + ret = SUCCESS; + break; + } + *pos_grp_start = php_stream_tell(dba->fp); + } + inifile_line_free(&ln); + return ret; +} +/* }}} */ + +/* {{{ inifile_copy_to + */ +static int inifile_copy_to(inifile *dba, size_t pos_start, size_t pos_end, inifile **ini_copy TSRMLS_DC) +{ + php_stream *fp; + + if (pos_start == pos_end) { + *ini_copy = NULL; + return SUCCESS; + } + if ((fp = php_stream_temp_create(0, 64 * 1024)) == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create temporary stream"); + *ini_copy = NULL; + return FAILURE; + } + + if ((*ini_copy = inifile_alloc(fp, 1, 0 TSRMLS_CC)) == NULL) { + /* writes error */ + return FAILURE; + } + php_stream_seek(dba->fp, pos_start, SEEK_SET); + if (!php_stream_copy_to_stream(dba->fp, fp, pos_end - pos_start)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy group [%d - %d] to temporary stream", pos_start, pos_end); + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + +/* {{{ inifile_filter + * copy from to dba while ignoring key name (group must equal) + */ +static int inifile_filter(inifile *dba, inifile *from, const key_type *key TSRMLS_DC) +{ + size_t pos_start = 0, pos_next = 0, pos_curr; + int ret = SUCCESS; + line_type ln = {{NULL,NULL},{NULL}}; + + php_stream_seek(from->fp, 0, SEEK_SET); + php_stream_seek(dba->fp, 0, SEEK_END); + while(inifile_read(from, &ln TSRMLS_CC)) { + switch(inifile_key_cmp(&ln.key, key TSRMLS_CC)) { + case 0: + pos_curr = php_stream_tell(from->fp); + if (pos_start != pos_next) { + php_stream_seek(from->fp, pos_start, SEEK_SET); + if (!php_stream_copy_to_stream(from->fp, dba->fp, pos_next - pos_start)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy [%d - %d] from temporary stream", pos_next, pos_start); + ret = FAILURE; + } + php_stream_seek(from->fp, pos_curr, SEEK_SET); + } + pos_next = pos_start = pos_curr; + break; + case 1: + pos_next = php_stream_tell(from->fp); + break; + case 2: + /* the function is meant to process only entries from same group */ + assert(0); + break; + } + } + if (pos_start != pos_next) { + php_stream_seek(from->fp, pos_start, SEEK_SET); + if (!php_stream_copy_to_stream(from->fp, dba->fp, pos_next - pos_start)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy [%d - %d] from temporary stream", pos_next, pos_start); + ret = FAILURE; + } + } + inifile_line_free(&ln); + return SUCCESS; +} +/* }}} */ + +/* {{{ inifile_delete_replace_append + */ +static int inifile_delete_replace_append(inifile *dba, const key_type *key, const val_type *value, int append TSRMLS_DC) +{ + size_t pos_grp_start, pos_grp_next; + inifile *ini_tmp = NULL; + php_stream *fp_tmp = NULL; + int ret; + + /* 1) Search group start + * 2) Search next group + * 3) If not append: Copy group to ini_tmp + * 4) Open temp_stream and copy remainder + * 5) Truncate stream + * 6) If not append AND key.name given: Filtered copy back from ini_tmp + * to stream. Otherwise the user wanted to delete the group. + * 7) Append value if given + * 8) Append temporary stream + */ + + assert(!append || (key->name && value)); /* missuse */ + + /* 1 - 3 */ + inifile_find_group(dba, key, &pos_grp_start TSRMLS_CC); + inifile_next_group(dba, key, &pos_grp_next TSRMLS_CC); + if (append) { + ret = SUCCESS; + } else { + ret = inifile_copy_to(dba, pos_grp_start, pos_grp_next, &ini_tmp TSRMLS_CC); + } + + /* 4 */ + if (ret == SUCCESS) { + fp_tmp = php_stream_temp_create(0, 64 * 1024); + if (!fp_tmp) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create temporary stream"); + ret = FAILURE; + } else { + php_stream_seek(dba->fp, 0, SEEK_END); + if (pos_grp_next != php_stream_tell(dba->fp)) { + php_stream_seek(dba->fp, pos_grp_next, SEEK_SET); + if (!php_stream_copy_to_stream(dba->fp, fp_tmp, PHP_STREAM_COPY_ALL)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy remainder to temporary stream"); + ret = FAILURE; + } + } + } + } + + /* 5 */ + if (ret == SUCCESS) { + ret = inifile_truncate(dba, append ? pos_grp_next : pos_grp_start TSRMLS_CC); /* writes error on fail */ + } + + if (ret == SUCCESS) { + if (key->name && strlen(key->name)) { + /* 6 */ + if (!append && ini_tmp) { + ret = inifile_filter(dba, ini_tmp, key TSRMLS_CC); + } + + /* 7 */ + /* important: do not query ret==SUCCESS again: inifile_filter might fail but + * however next operation must be done. + */ + if (value) { + if (pos_grp_start == pos_grp_next && key->group && strlen(key->group)) { + php_stream_printf(dba->fp TSRMLS_CC, "[%s]\n", key->group); + } + php_stream_printf(dba->fp TSRMLS_CC, "%s=%s\n", key->name, value->value ? value->value : ""); + } + } + + /* 8 */ + /* important: do not query ret==SUCCESS again: inifile_filter might fail but + * however next operation must be done. + */ + if (fp_tmp && php_stream_tell(fp_tmp)) { + php_stream_seek(fp_tmp, 0, SEEK_SET); + php_stream_seek(dba->fp, 0, SEEK_END); + if (!php_stream_copy_to_stream(fp_tmp, dba->fp, PHP_STREAM_COPY_ALL)) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not copy from temporary stream - ini file truncated"); + ret = FAILURE; + } + } + } + + if (ini_tmp) { + php_stream_close(ini_tmp->fp); + inifile_free(ini_tmp, 0); + } + if (fp_tmp) { + php_stream_close(fp_tmp); + } + php_stream_flush(dba->fp); + php_stream_seek(dba->fp, 0, SEEK_SET); + + return ret; +} +/* }}} */ + +/* {{{ inifile_delete + */ +int inifile_delete(inifile *dba, const key_type *key TSRMLS_DC) +{ + return inifile_delete_replace_append(dba, key, NULL, 0 TSRMLS_CC); +} +/* }}} */ + +/* {{{ inifile_relace + */ +int inifile_replace(inifile *dba, const key_type *key, const val_type *value TSRMLS_DC) +{ + return inifile_delete_replace_append(dba, key, value, 0 TSRMLS_CC); +} +/* }}} */ + +/* {{{ inifile_append + */ +int inifile_append(inifile *dba, const key_type *key, const val_type *value TSRMLS_DC) +{ + return inifile_delete_replace_append(dba, key, value, 1 TSRMLS_CC); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/dba/libinifile/inifile.h b/ext/dba/libinifile/inifile.h new file mode 100644 index 0000000000..f0c17c369c --- /dev/null +++ b/ext/dba/libinifile/inifile.h @@ -0,0 +1,67 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_LIB_INIFILE_H +#define PHP_LIB_INIFILE_H + +typedef struct { + char *group; + char *name; +} key_type; + +typedef struct { + char *value; +} val_type; + +typedef struct { + key_type key; + val_type val; + size_t pos; +} line_type; + +typedef struct { + char *lockfn; + int lockfd; + php_stream *fp; + int fd; + int readonly; + line_type curr; + line_type next; +} inifile; + +val_type inifile_fetch(inifile *dba, const key_type *key, int skip TSRMLS_DC); +int inifile_firstkey(inifile *dba TSRMLS_DC); +int inifile_nextkey(inifile *dba TSRMLS_DC); +int inifile_delete(inifile *dba, const key_type *key TSRMLS_DC); +int inifile_replace(inifile *dba, const key_type *key, const val_type *val TSRMLS_DC); +int inifile_append(inifile *dba, const key_type *key, const val_type *val TSRMLS_DC); +char *inifile_version(); + +key_type inifile_key_split(const char *group_name); +char * inifile_key_string(const key_type *key); + +void inifile_key_free(key_type *key); +void inifile_val_free(val_type *val); +void inifile_line_free(line_type *ln); + +inifile * inifile_alloc(php_stream *fp, int readonly, int persistent TSRMLS_DC); +void inifile_free(inifile *dba, int persistent); + +#endif diff --git a/ext/dba/php_inifile.h b/ext/dba/php_inifile.h new file mode 100644 index 0000000000..69444df3c6 --- /dev/null +++ b/ext/dba/php_inifile.h @@ -0,0 +1,12 @@ +#ifndef PHP_INIFILE_H +#define PHP_INIFILE_H + +#if DBA_INIFILE + +#include "php_dba.h" + +DBA_FUNCS(inifile); + +#endif + +#endif diff --git a/ext/dba/tests/dba_inifile.phpt b/ext/dba/tests/dba_inifile.phpt new file mode 100644 index 0000000000..5ccccbe238 --- /dev/null +++ b/ext/dba/tests/dba_inifile.phpt @@ -0,0 +1,29 @@ +--TEST-- +DBA INIFILE handler test +--SKIPIF-- + +--FILE-- + +--EXPECT-- +database handler: inifile +3NYNYY +Content String 2 +Content 2 replaced +Read during write: not allowed +Content 2 replaced 2nd time +The 6th value +array(3) { + ["key number 6"]=> + string(13) "The 6th value" + ["key2"]=> + string(27) "Content 2 replaced 2nd time" + ["key5"]=> + string(23) "The last content string" +} \ No newline at end of file -- 2.50.1