helly Thu May 29 10:16:25 2003 EDT Added files: (Branch: PHP_4_3) /php4/ext/dba dba_inifile.c php_inifile.h /php4/ext/dba/libinifile .cvsignore inifile.c inifile.h /php4/ext/dba/tests dba_inifile.phpt
Modified files: /php4/ext/dba config.m4 dba.c Log: MFH: ini file support
Index: php4/ext/dba/config.m4 diff -u php4/ext/dba/config.m4:1.29.2.19 php4/ext/dba/config.m4:1.29.2.20 --- php4/ext/dba/config.m4:1.29.2.19 Tue May 20 20:35:29 2003 +++ php4/ext/dba/config.m4 Thu May 29 10:16:25 2003 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4,v 1.29.2.19 2003/05/21 00:35:29 helly Exp $ +dnl $Id: config.m4,v 1.29.2.20 2003/05/29 14:16:25 helly Exp $ dnl dnl Suppose we need FlatFile if no support or only CDB is used. @@ -390,6 +390,24 @@ ]) AC_DBA_STD_RESULT(cdb) +AC_DEFUN(PHP_DBA_BUILTIN_INI,[ + 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" -o "$HAVE_DBA" = "1"; then + PHP_DBA_BUILTIN_INI + fi +]) +AC_DBA_STD_RESULT(inifile,INI File) + AC_DEFUN(PHP_DBA_BUILTIN_FLATFILE,[ AC_DEFINE(DBA_FLATFILE, 1, [ ]) flat_sources="libflatfile/flatfile.c" @@ -415,7 +433,8 @@ 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_ADD_BUILD_DIR($ext_builddir/libinifile) PHP_ADD_BUILD_DIR($ext_builddir/libcdb) PHP_ADD_BUILD_DIR($ext_builddir/libflatfile) PHP_SUBST(DBA_SHARED_LIBADD) Index: php4/ext/dba/dba.c diff -u php4/ext/dba/dba.c:1.61.2.15 php4/ext/dba/dba.c:1.61.2.16 --- php4/ext/dba/dba.c:1.61.2.15 Mon May 19 18:53:35 2003 +++ php4/ext/dba/dba.c Thu May 29 10:16:25 2003 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dba.c,v 1.61.2.15 2003/05/19 22:53:35 helly Exp $ */ +/* $Id: dba.c,v 1.61.2.16 2003/05/29 14:16:25 helly Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -28,6 +28,7 @@ #if HAVE_DBA #include "ext/standard/flock_compat.h" +#include "php_ini.h" #include <stdio.h> #include <fcntl.h> #ifdef HAVE_SYS_FILE_H @@ -46,6 +47,7 @@ #include "php_db3.h" #include "php_db4.h" #include "php_flatfile.h" +#include "php_inifile.h" /* {{{ dba_functions[] */ @@ -105,15 +107,56 @@ WRONG_PARAM_COUNT; \ } +/* {{{ php_dba_myke_key */ +static size_t php_dba_make_key(zval **key, char **key_str, char **key_free TSRMLS_DC) +{ + if (Z_TYPE_PP(key) == IS_ARRAY) { + zval **group, **name; + HashPosition pos; + size_t len; + + if (zend_hash_num_elements(Z_ARRVAL_PP(key)) != 2) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key does not have exactly two elements: (key, name)"); + return -1; + } + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(key), &pos); + zend_hash_get_current_data_ex(Z_ARRVAL_PP(key), (void **) &group, &pos); + zend_hash_move_forward_ex(Z_ARRVAL_PP(key), &pos); + zend_hash_get_current_data_ex(Z_ARRVAL_PP(key), (void **) &name, &pos); + convert_to_string_ex(group); + convert_to_string_ex(name); + if (Z_STRLEN_PP(group) == 0) { + *key_str = Z_STRVAL_PP(name); + *key_free = NULL; + return Z_STRLEN_PP(name); + } + len = spprintf(key_str, 0, "[%s]%s", Z_STRVAL_PP(group), Z_STRVAL_PP(name)); + *key_free = *key_str; + return len; + } else { + convert_to_string_ex(key); + *key_str = Z_STRVAL_PP(key); + *key_free = NULL; + return Z_STRLEN_PP(key); + } +} +/* }}} */ + #define DBA_GET2 \ zval **key; \ + char *key_str, *key_free; \ + size_t key_len; \ if(ac != 2 || zend_get_parameters_ex(ac, &key, &id) != SUCCESS) { \ WRONG_PARAM_COUNT; \ } \ - convert_to_string_ex(key) + if ((key_len = php_dba_make_key(key, &key_str, &key_free TSRMLS_CC)) == 0) {\ + RETURN_FALSE; \ + } #define DBA_GET2_3 \ zval **key; \ + char *key_str, *key_free; \ + size_t key_len; \ zval **tmp; \ int skip = 0; \ switch(ac) { \ @@ -132,7 +175,21 @@ default: \ WRONG_PARAM_COUNT; \ } \ - convert_to_string_ex(key) + if ((key_len = php_dba_make_key(key, &key_str, &key_free TSRMLS_CC)) == 0) {\ + RETURN_FALSE; \ + } + +#define DBA_GET3 \ + zval **key, **val; \ + char *key_str, *key_free; \ + size_t key_len; \ + if(ac != 3 || zend_get_parameters_ex(ac, &key, &val, &id) != SUCCESS) { \ + WRONG_PARAM_COUNT; \ + } \ + convert_to_string_ex(val); \ + if ((key_len = php_dba_make_key(key, &key_str, &key_free TSRMLS_CC)) == 0) {\ + RETURN_FALSE; \ + } #define DBA_ID_GET \ ZEND_FETCH_RESOURCE2(info, dba_info *, id, -1, "DBA identifier", le_db, le_pdb); @@ -140,7 +197,10 @@ #define DBA_ID_GET1 DBA_ID_PARS; DBA_GET1; DBA_ID_GET #define DBA_ID_GET2 DBA_ID_PARS; DBA_GET2; DBA_ID_GET #define DBA_ID_GET2_3 DBA_ID_PARS; DBA_GET2_3; DBA_ID_GET +#define DBA_ID_GET3 DBA_ID_PARS; DBA_GET3; DBA_ID_GET +#define DBA_ID_DONE \ + if (key_free) efree(key_free) /* a DBA handler must have specific routines */ #define DBA_NAMED_HND(alias, name, flags) \ @@ -188,14 +248,68 @@ #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 { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; +#if DBA_FLATFILE +#define DBA_DEFAULT "flatfile" +#elif DBA_DB4 +#define DBA_DEFAULT "db4" +#elif DBA_DB3 +#define DBA_DEFAULT "db3" +#elif DBA_DB2 +#define DBA_DEFAULT "db2" +#elif DBA_GDBM +#define DBA_DEFAULT "gdbm" +#elif DBA_NBBM +#define DBA_DEFAULT "ndbm" +#elif DBA_DBM +#define DBA_DEFAULT "dbm" +#else +#define DBA_DEFAULT "" +#endif +/* cdb/cdb_make and ini are no option here */ + +ZEND_BEGIN_MODULE_GLOBALS(dba) + char *default_handler; + dba_handler *default_hptr; +ZEND_END_MODULE_GLOBALS(dba) + +ZEND_DECLARE_MODULE_GLOBALS(dba) + +#ifdef ZTS +#define DBA_G(v) TSRMG(dba_globals_id, zend_dba_globals *, v) +#else +#define DBA_G(v) (dba_globals.v) +#endif + static int le_db; static int le_pdb; + +/* {{{ dba_fetch_resource +PHPAPI void dba_fetch_resource(dba_info **pinfo, zval **id TSRMLS_DC) +{ + dba_info *info; + DBA_ID_FETCH + *pinfo = info; +} +*/ +/* }}} */ + +/* {{{ dba_get_handler +PHPAPI dba_handler *dba_get_handler(const char* handler_name) +{ + dba_handler *hptr; + for (hptr = handler; hptr->name && strcasecmp(hptr->name, handler_name); hptr++); + return hptr; +} +*/ /* }}} */ /* {{{ dba_close @@ -253,10 +367,47 @@ } /* }}} */ +/* {{{ PHP_INI + */ +ZEND_API ZEND_INI_MH(OnUpdateDefaultHandler) +{ + dba_handler *hptr; + + if (!strlen(new_value)) { + DBA_G(default_hptr) = NULL; + return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + } + + for (hptr = handler; hptr->name && strcasecmp(hptr->name, new_value); hptr++); + + if (!hptr->name) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such handler: %s", new_value); + return FAILURE; + } + DBA_G(default_hptr) = hptr; + return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); +} + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("dba.default_handler", DBA_DEFAULT, PHP_INI_ALL, OnUpdateDefaultHandler, default_handler, zend_dba_globals, dba_globals) +PHP_INI_END() +/* }}} */ + +/* {{{ php_dba_init_globals + */ +static void php_dba_init_globals(zend_dba_globals *dba_globals) +{ + dba_globals->default_handler = ""; + dba_globals->default_hptr = NULL; +} +/* }}} */ + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(dba) { + ZEND_INIT_MODULE_GLOBALS(dba, php_dba_init_globals, NULL); + REGISTER_INI_ENTRIES(); le_db = zend_register_list_destructors_ex(dba_close_rsrc, NULL, "dba", module_number); le_pdb = zend_register_list_destructors_ex(dba_close_pe_rsrc, dba_close_rsrc, "dba persistent", module_number); return SUCCESS; @@ -267,6 +418,7 @@ */ PHP_MSHUTDOWN_FUNCTION(dba) { + UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ @@ -302,17 +454,9 @@ */ static void php_dba_update(INTERNAL_FUNCTION_PARAMETERS, int mode) { - DBA_ID_PARS; - zval **val, **key; char *v; int len; - - if(ac != 3 || zend_get_parameters_ex(ac, &key, &val, &id) != SUCCESS) { - WRONG_PARAM_COUNT; - } - convert_to_string_ex(key); - convert_to_string_ex(val); - DBA_ID_GET; + DBA_ID_GET3; DBA_WRITE_CHECK; @@ -320,15 +464,20 @@ len = Z_STRLEN_PP(val); v = estrndup(Z_STRVAL_PP(val), len); php_stripslashes(v, &len TSRMLS_CC); - if(info->hnd->update(info, VALLEN(key), v, len, mode TSRMLS_CC) == SUCCESS) { + if(info->hnd->update(info, key_str, key_len, v, len, mode TSRMLS_CC) == SUCCESS) { efree(v); + DBA_ID_DONE; RETURN_TRUE; } efree(v); } else { - if(info->hnd->update(info, VALLEN(key), VALLEN(val), mode TSRMLS_CC) == SUCCESS) + if(info->hnd->update(info, key_str, key_len, VALLEN(val), mode TSRMLS_CC) == SUCCESS) + { + DBA_ID_DONE; RETURN_TRUE; + } } + DBA_ID_DONE; RETURN_FALSE; } /* }}} */ @@ -377,7 +526,7 @@ char mode[4], *pmode, *lock_file_mode = NULL; int persistent_flag = persistent ? STREAM_OPEN_PERSISTENT : 0; - if(ac < 3) { + if(ac < 2) { WRONG_PARAM_COUNT; } @@ -398,7 +547,8 @@ list_entry *le; /* calculate hash */ - key = emalloc(keylen); + key = safe_emalloc(keylen, 1, 1); + key[keylen] = '\0'; keylen = 0; for(i = 0; i < ac; i++) { @@ -421,7 +571,16 @@ } } - for (hptr = handler; hptr->name && strcasecmp(hptr->name, Z_STRVAL_PP(args[2])); hptr++); + if (ac==2) { + hptr = DBA_G(default_hptr); + if (!hptr) { + php_error_docref2(NULL TSRMLS_CC, Z_STRVAL_PP(args[0]), Z_STRVAL_PP(args[1]), E_WARNING, "No default handler selected"); + FREENOW; + RETURN_FALSE; + } + } else { + for (hptr = handler; hptr->name && strcasecmp(hptr->name, Z_STRVAL_PP(args[2])); hptr++); + } if (!hptr->name) { php_error_docref2(NULL TSRMLS_CC, Z_STRVAL_PP(args[0]), Z_STRVAL_PP(args[1]), E_WARNING, "No such handler: %s", Z_STRVAL_PP(args[2])); @@ -633,7 +792,7 @@ /* }}} */ #undef FREENOW -/* {{{ proto int dba_popen(string path, string mode, string handlername [, string ...]) +/* {{{ proto int dba_popen(string path, string mode [, string handlername, string ...]) Opens path using the specified handler in mode persistently */ PHP_FUNCTION(dba_popen) { @@ -641,7 +800,7 @@ } /* }}} */ -/* {{{ proto int dba_open(string path, string mode, string handlername [, string ...]) +/* {{{ proto int dba_open(string path, string mode [, string handlername, string ...]) Opens path using the specified handler in mode*/ PHP_FUNCTION(dba_open) { @@ -665,9 +824,11 @@ { DBA_ID_GET2; - if(info->hnd->exists(info, VALLEN(key) TSRMLS_CC) == SUCCESS) { + if(info->hnd->exists(info, key_str, key_len TSRMLS_CC) == SUCCESS) { + DBA_ID_DONE; RETURN_TRUE; } + DBA_ID_DONE; RETURN_FALSE; } /* }}} */ @@ -680,15 +841,38 @@ 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 = info->hnd->fetch(info, key_str, key_len, skip, &len TSRMLS_CC)) != NULL) { if (val && PG(magic_quotes_runtime)) { val = php_addslashes(val, len, &len, 1 TSRMLS_CC); } + DBA_ID_DONE; RETURN_STRINGL(val, len, 0); } + DBA_ID_DONE; RETURN_FALSE; } /* }}} */ @@ -724,21 +908,27 @@ /* }}} */ /* {{{ 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; DBA_WRITE_CHECK; - if(info->hnd->delete(info, VALLEN(key) TSRMLS_CC) == SUCCESS) + if(info->hnd->delete(info, key_str, key_len TSRMLS_CC) == SUCCESS) + { + DBA_ID_DONE; RETURN_TRUE; + } + DBA_ID_DONE; RETURN_FALSE; } /* }}} */ /* {{{ 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); @@ -746,7 +936,8 @@ /* }}} */ /* {{{ 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); Index: php4/ext/dba/dba_inifile.c +++ php4/ext/dba/dba_inifile.c /* +----------------------------------------------------------------------+ | 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 | | [EMAIL PROTECTED] so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Marcus Boerger <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ /* $Id: dba_inifile.c,v 1.1 2003/02/22 17:20:05 helly Exp $ */ #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 */ Index: php4/ext/dba/php_inifile.h +++ php4/ext/dba/php_inifile.h #ifndef PHP_INIFILE_H #define PHP_INIFILE_H #if DBA_INIFILE #include "php_dba.h" DBA_FUNCS(inifile); #endif #endif Index: php4/ext/dba/libinifile/.cvsignore +++ php4/ext/dba/libinifile/.cvsignore *.lo *.la deps *.plg *.opt *.ncb Release Release_inline Debug Release_TS Release_TSDbg Release_TS_inline Debug_TS Index: php4/ext/dba/libinifile/inifile.c +++ php4/ext/dba/libinifile/inifile.c /* +----------------------------------------------------------------------+ | 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 | | [EMAIL PROTECTED] so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Marcus Boerger <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ /* $Id: inifile.c,v 1.1 2003/02/22 17:20:05 helly Exp $ */ #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: 1.1 $"; } /* }}} */ /* {{{ 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 */ Index: php4/ext/dba/libinifile/inifile.h +++ php4/ext/dba/libinifile/inifile.h /* +----------------------------------------------------------------------+ | 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 | | [EMAIL PROTECTED] so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Marcus Boerger <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ /* $Id: inifile.h,v 1.1 2003/02/22 17:20:05 helly Exp $ */ #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 Index: php4/ext/dba/tests/dba_inifile.phpt +++ php4/ext/dba/tests/dba_inifile.phpt --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" }
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php