]> granicus.if.org Git - php/commitdiff
@Added dba handler inifiles to support ini files. (Marcus)
authorMarcus Boerger <helly@php.net>
Sat, 22 Feb 2003 17:20:06 +0000 (17:20 +0000)
committerMarcus Boerger <helly@php.net>
Sat, 22 Feb 2003 17:20:06 +0000 (17:20 +0000)
ext/dba/config.m4
ext/dba/dba.c
ext/dba/dba_inifile.c [new file with mode: 0644]
ext/dba/libinifile/inifile.c [new file with mode: 0644]
ext/dba/libinifile/inifile.h [new file with mode: 0644]
ext/dba/php_inifile.h [new file with mode: 0644]
ext/dba/tests/dba_inifile.phpt [new file with mode: 0644]

index b1b9f0d51a0abf026a39a7b05dc5ec5c9af83675..5594bfb7259e4c11f7bdbd3b5a00f0f8550d1ecb 100644 (file)
@@ -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)
index 45f9307d7924b5ffd9bbee67defbc76d48327619..77196fd0f6461a233d54ee32041edca1fa183907 100644 (file)
@@ -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 (file)
index 0000000..2a12179
--- /dev/null
@@ -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 <helly@php.net>                               |
+   +----------------------------------------------------------------------+
+ */
+
+/* $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 <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#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 (file)
index 0000000..76ac982
--- /dev/null
@@ -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 <helly@php.net>                               |
+   +----------------------------------------------------------------------+
+ */
+
+/* $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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#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 (file)
index 0000000..f0c17c3
--- /dev/null
@@ -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 <helly@php.net>                               |
+   +----------------------------------------------------------------------+
+ */
+
+/* $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 (file)
index 0000000..69444df
--- /dev/null
@@ -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 (file)
index 0000000..5ccccbe
--- /dev/null
@@ -0,0 +1,29 @@
+--TEST--
+DBA INIFILE handler test
+--SKIPIF--
+<?php 
+       require_once('skipif.inc');
+       if (!in_array('inifile', dba_handlers())) die('skip INIFILE handler not available');
+?>
+--FILE--
+<?php
+       require_once('test.inc');
+       $handler = 'inifile';
+       require_once('dba_handler.inc');
+?>
+--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