diff --git a/ext/dba/libinifile/inifile.c b/ext/dba/libinifile/inifile.c index c5467396d4bfe..64536d8e2aef3 100644 --- a/ext/dba/libinifile/inifile.c +++ b/ext/dba/libinifile/inifile.c @@ -14,14 +14,12 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include "php.h" -#include "php_globals.h" +#include "php.h" /* Headers are un utter mess so this needs to be before */ +#include "inifile.h" #include #include @@ -30,21 +28,20 @@ #include #endif -#include "inifile.h" +#include "zend_alloc.h" +#include "php_streams.h" +#include "spprintf.h" /* ret = -1 means that database was opened for read-only * ret = 0 success * ret = 1 key already exists - nothing done */ -/* {{{ inifile_version */ const char *inifile_version(void) { return "1.0, $Id$"; } -/* }}} */ -/* {{{ inifile_free_key */ void inifile_key_free(key_type *key) { if (key->group) { @@ -55,9 +52,7 @@ void inifile_key_free(key_type *key) } memset(key, 0, sizeof(key_type)); } -/* }}} */ -/* {{{ inifile_free_val */ void inifile_val_free(val_type *val) { if (val->value) { @@ -65,19 +60,16 @@ void inifile_val_free(val_type *val) } 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) +inifile * inifile_alloc(php_stream *fp, bool readonly, bool persistent) { inifile *dba; @@ -96,8 +88,7 @@ inifile * inifile_alloc(php_stream *fp, int readonly, int persistent) } /* }}} */ -/* {{{ inifile_free */ -void inifile_free(inifile *dba, int persistent) +void inifile_free(inifile *dba, bool persistent) { if (dba) { inifile_line_free(&dba->curr); @@ -105,7 +96,6 @@ void inifile_free(inifile *dba, int persistent) pefree(dba, persistent); } } -/* }}} */ /* {{{ inifile_key_split */ key_type inifile_key_split(const char *group_name) @@ -160,8 +150,7 @@ static char *etrim(const char *str) } /* }}} */ -/* {{{ inifile_findkey */ -static int inifile_read(inifile *dba, line_type *ln) { +static bool inifile_read(inifile *dba, line_type *ln) { char *fline; char *pos; @@ -180,7 +169,7 @@ static int inifile_read(inifile *dba, line_type *ln) { ln->key.name = estrdup(""); ln->pos = php_stream_tell(dba->fp); efree(fline); - return 1; + return true; } else { efree(fline); continue; @@ -200,7 +189,7 @@ static int inifile_read(inifile *dba, line_type *ln) { ln->val.value = etrim(pos+1); ln->pos = php_stream_tell(dba->fp); efree(fline); - return 1; + return true; } else { /* simply ignore lines without '=' * those should be comments @@ -212,27 +201,27 @@ static int inifile_read(inifile *dba, line_type *ln) { } } inifile_line_free(ln); - return 0; + return false; } -/* }}} */ -/* {{{ 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) +enum inifile_key_cmp_status { + EQUAL, + GROUP_EQUAL_NAME_DIFFERENT, + DIFFERENT +}; + +static enum inifile_key_cmp_status inifile_key_cmp(const key_type *k1, const key_type *k2) { assert(k1->group && k1->name && k2->group && k2->name); if (!strcasecmp(k1->group, k2->group)) { if (!strcasecmp(k1->name, k2->name)) { - return 0; + return EQUAL; } else { - return 1; + return GROUP_EQUAL_NAME_DIFFERENT; } } else { - return 2; + return DIFFERENT; } } /* }}} */ @@ -241,7 +230,6 @@ static int inifile_key_cmp(const key_type *k1, const key_type *k2) val_type inifile_fetch(inifile *dba, const key_type *key, int skip) { line_type ln = {{NULL,NULL},{NULL},0}; val_type val; - int res, grp_eq = 0; if (skip == -1 && dba->next.key.group && dba->next.key.name && !inifile_key_cmp(&dba->next.key, key)) { /* we got position already from last fetch */ @@ -256,8 +244,10 @@ val_type inifile_fetch(inifile *dba, const key_type *key, int skip) { if (skip == -1) { skip = 0; } - while(inifile_read(dba, &ln)) { - if (!(res=inifile_key_cmp(&ln.key, key))) { + bool grp_eq = false; + while (inifile_read(dba, &ln)) { + enum inifile_key_cmp_status cmp_result = inifile_key_cmp(&ln.key, key); + if (cmp_result == EQUAL) { if (!skip) { val.value = estrdup(ln.val.value ? ln.val.value : ""); /* allow faster access by updating key read into next */ @@ -267,8 +257,8 @@ val_type inifile_fetch(inifile *dba, const key_type *key, int skip) { return val; } skip--; - } else if (res == 1) { - grp_eq = 1; + } else if (cmp_result == GROUP_EQUAL_NAME_DIFFERENT) { + grp_eq = true; } else if (grp_eq) { /* we are leaving group now: that means we cannot find the key */ break; @@ -281,7 +271,7 @@ val_type inifile_fetch(inifile *dba, const key_type *key, int skip) { /* }}} */ /* {{{ inifile_firstkey */ -int inifile_firstkey(inifile *dba) { +bool inifile_firstkey(inifile *dba) { inifile_line_free(&dba->curr); dba->curr.pos = 0; return inifile_nextkey(dba); @@ -289,7 +279,7 @@ int inifile_firstkey(inifile *dba) { /* }}} */ /* {{{ inifile_nextkey */ -int inifile_nextkey(inifile *dba) { +bool inifile_nextkey(inifile *dba) { line_type ln = {{NULL,NULL},{NULL},0}; /*inifile_line_free(&dba->next); ??? */ @@ -302,106 +292,90 @@ int inifile_nextkey(inifile *dba) { } /* }}} */ -/* {{{ inifile_truncate */ -static int inifile_truncate(inifile *dba, size_t size) +static bool inifile_truncate(inifile *dba, size_t size) { - int res; + int stream_op_status = php_stream_truncate_set_size(dba->fp, size); - if ((res=php_stream_truncate_set_size(dba->fp, size)) != 0) { - php_error_docref(NULL, E_WARNING, "Error in ftruncate: %d", res); - return FAILURE; + if (stream_op_status != 0) { + php_error_docref(NULL, E_WARNING, "Error in ftruncate: %d", stream_op_status); + return false; } php_stream_seek(dba->fp, size, SEEK_SET); - return SUCCESS; + return true; } -/* }}} */ -/* {{{ 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) +/* Return position where which points to "[group_name]" */ +static size_t inifile_find_group(inifile *dba, const key_type *key) { - int ret = FAILURE; - + /* Rewind to the beginning of the file and free currently hold lines */ 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},0}; + /* If key is not in a group return the position of the start of the file */ + if (!key->group || strlen(key->group) == 0) { + return 0; + } - res = 1; - while(inifile_read(dba, &ln)) { - if ((res=inifile_key_cmp(&ln.key, key)) < 2) { - ret = SUCCESS; - break; - } - *pos_grp_start = php_stream_tell(dba->fp); + size_t group_start_position = 0; + line_type ln = {{NULL,NULL},{NULL},0}; + while (inifile_read(dba, &ln)) { + if (inifile_key_cmp(&ln.key, key) != DIFFERENT) { + break; } - inifile_line_free(&ln); - } else { - *pos_grp_start = 0; - ret = SUCCESS; + group_start_position = php_stream_tell(dba->fp); } - if (ret == FAILURE) { - *pos_grp_start = php_stream_tell(dba->fp); - } - return ret; + inifile_line_free(&ln); + return group_start_position; } -/* }}} */ -/* {{{ inifile_next_group - * only valid after a call to inifile_find_group +/* Must only be called after 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) +static size_t inifile_next_group(inifile *dba, const key_type *key) { - int ret = FAILURE; line_type ln = {{NULL,NULL},{NULL},0}; - *pos_grp_start = php_stream_tell(dba->fp); + size_t start_position = php_stream_tell(dba->fp); ln.key.group = estrdup(key->group); - while(inifile_read(dba, &ln)) { - if (inifile_key_cmp(&ln.key, key) == 2) { - ret = SUCCESS; + while (inifile_read(dba, &ln)) { + if (inifile_key_cmp(&ln.key, key) == DIFFERENT) { break; } - *pos_grp_start = php_stream_tell(dba->fp); + start_position = php_stream_tell(dba->fp); } inifile_line_free(&ln); - return ret; + return start_position; } /* }}} */ -/* {{{ inifile_copy_to */ -static int inifile_copy_to(inifile *dba, size_t pos_start, size_t pos_end, inifile **ini_copy) +static bool inifile_copy_to(inifile *dba, size_t pos_start, size_t pos_end, inifile **ini_copy) { php_stream *fp; if (pos_start == pos_end) { *ini_copy = NULL; - return SUCCESS; + return true; } if ((fp = php_stream_temp_create(0, 64 * 1024)) == NULL) { php_error_docref(NULL, E_WARNING, "Could not create temporary stream"); *ini_copy = NULL; - return FAILURE; + return false; } if ((*ini_copy = inifile_alloc(fp, 1, 0)) == NULL) { /* writes error */ - return FAILURE; + return false; } php_stream_seek(dba->fp, pos_start, SEEK_SET); if (SUCCESS != php_stream_copy_to_stream_ex(dba->fp, fp, pos_end - pos_start, NULL)) { php_error_docref(NULL, E_WARNING, "Could not copy group [%zu - %zu] to temporary stream", pos_start, pos_end); - return FAILURE; + return false; } - return SUCCESS; + return true; } -/* }}} */ /* {{{ inifile_filter * copy from to dba while ignoring key name (group must equal) @@ -414,11 +388,11 @@ static int inifile_filter(inifile *dba, inifile *from, const key_type *key, bool php_stream_seek(from->fp, 0, SEEK_SET); php_stream_seek(dba->fp, 0, SEEK_END); - while(inifile_read(from, &ln)) { - switch(inifile_key_cmp(&ln.key, key)) { - case 0: + while (inifile_read(from, &ln)) { + switch (inifile_key_cmp(&ln.key, key)) { + case EQUAL: if (found) { - *found = (bool) 1; + *found = true; } pos_curr = php_stream_tell(from->fp); if (pos_start != pos_next) { @@ -431,10 +405,10 @@ static int inifile_filter(inifile *dba, inifile *from, const key_type *key, bool } pos_next = pos_start = pos_curr; break; - case 1: + case GROUP_EQUAL_NAME_DIFFERENT: pos_next = php_stream_tell(from->fp); break; - case 2: + case DIFFERENT: /* the function is meant to process only entries from same group */ assert(0); break; @@ -452,13 +426,12 @@ static int inifile_filter(inifile *dba, inifile *from, const key_type *key, bool } /* }}} */ -/* {{{ inifile_delete_replace_append */ -static int inifile_delete_replace_append(inifile *dba, const key_type *key, const val_type *value, int append, bool *found) +/* If value == NULL we are deleting the entry, if is_append is true then we are inserting a new entry */ +static int inifile_delete_replace_append(inifile *dba, const key_type *key, const val_type *value, bool is_append, bool *found) { - size_t pos_grp_start=0, pos_grp_next; inifile *ini_tmp = NULL; php_stream *fp_tmp = NULL; - int ret; + int ret = FAILURE; /* 1) Search group start * 2) Search next group @@ -471,78 +444,76 @@ static int inifile_delete_replace_append(inifile *dba, const key_type *key, cons * 8) Append temporary stream */ - assert(!append || (key->name && value)); /* missuse */ + /* Check missuse of API */ + assert(!is_append || (key->name && value)); /* 1 - 3 */ - inifile_find_group(dba, key, &pos_grp_start); - inifile_next_group(dba, key, &pos_grp_next); - if (append) { - ret = SUCCESS; - } else { - ret = inifile_copy_to(dba, pos_grp_start, pos_grp_next, &ini_tmp); + size_t pos_grp_start = inifile_find_group(dba, key); + size_t pos_grp_next = inifile_next_group(dba, key); + if (!is_append && !inifile_copy_to(dba, pos_grp_start, pos_grp_next, &ini_tmp)) { + goto end; } /* 4 */ - if (ret == SUCCESS) { - fp_tmp = php_stream_temp_create(0, 64 * 1024); - if (!fp_tmp) { - php_error_docref(NULL, E_WARNING, "Could not create temporary stream"); - ret = FAILURE; - } else { - php_stream_seek(dba->fp, 0, SEEK_END); - if (pos_grp_next != (size_t)php_stream_tell(dba->fp)) { - php_stream_seek(dba->fp, pos_grp_next, SEEK_SET); - if (SUCCESS != php_stream_copy_to_stream_ex(dba->fp, fp_tmp, PHP_STREAM_COPY_ALL, NULL)) { - php_error_docref(NULL, E_WARNING, "Could not copy remainder to temporary stream"); - ret = FAILURE; - } - } + fp_tmp = php_stream_temp_create(0, 64 * 1024); + if (!fp_tmp) { + php_error_docref(NULL, E_WARNING, "Could not create temporary stream"); + goto end; + } + + php_stream_seek(dba->fp, 0, SEEK_END); + if (pos_grp_next != (size_t)php_stream_tell(dba->fp)) { + php_stream_seek(dba->fp, pos_grp_next, SEEK_SET); + if (SUCCESS != php_stream_copy_to_stream_ex(dba->fp, fp_tmp, PHP_STREAM_COPY_ALL, NULL)) { + php_error_docref(NULL, E_WARNING, "Could not copy remainder to temporary stream"); + goto end; } } /* 5 */ - if (ret == SUCCESS) { - if (!value || (key->name && strlen(key->name))) { - ret = inifile_truncate(dba, append ? pos_grp_next : pos_grp_start); /* writes error on fail */ + if (!value || (key->name && strlen(key->name))) { + if (!inifile_truncate(dba, is_append ? pos_grp_next : pos_grp_start)) { + /* Warning is already emitted by inifile_truncate() on failure */ + goto end; } } - if (ret == SUCCESS) { - if (key->name && strlen(key->name)) { - /* 6 */ - if (!append && ini_tmp) { - ret = inifile_filter(dba, ini_tmp, key, found); - } - - /* 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, "[%s]\n", key->group); - } - php_stream_printf(dba->fp, "%s=%s\n", key->name, value->value ? value->value : ""); - } + ret = SUCCESS; + if (key->name && strlen(key->name)) { + /* 6 */ + if (!is_append && ini_tmp) { + ret = inifile_filter(dba, ini_tmp, key, found); } - /* 8 */ + /* 7 */ /* 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 (SUCCESS != php_stream_copy_to_stream_ex(fp_tmp, dba->fp, PHP_STREAM_COPY_ALL, NULL)) { - zend_throw_error(NULL, "Could not copy from temporary stream - ini file truncated"); - ret = FAILURE; + if (value) { + if (pos_grp_start == pos_grp_next && key->group && strlen(key->group)) { + php_stream_printf(dba->fp, "[%s]\n", key->group); } + php_stream_printf(dba->fp, "%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 (SUCCESS != php_stream_copy_to_stream_ex(fp_tmp, dba->fp, PHP_STREAM_COPY_ALL, NULL)) { + zend_throw_error(NULL, "Could not copy from temporary stream - ini file truncated"); + ret = FAILURE; + } + } + +end: if (ini_tmp) { php_stream_close(ini_tmp->fp); - inifile_free(ini_tmp, 0); + inifile_free(ini_tmp, /* persistent */ false); } if (fp_tmp) { php_stream_close(fp_tmp); @@ -554,37 +525,27 @@ static int inifile_delete_replace_append(inifile *dba, const key_type *key, cons } /* }}} */ -/* {{{ inifile_delete */ int inifile_delete(inifile *dba, const key_type *key) { - return inifile_delete_replace_append(dba, key, NULL, 0, NULL); + return inifile_delete_replace_append(dba, key, NULL, /* is_append */ false, NULL); } -/* }}} */ -/* {{{ inifile_delete_ex */ int inifile_delete_ex(inifile *dba, const key_type *key, bool *found) { - return inifile_delete_replace_append(dba, key, NULL, 0, found); + return inifile_delete_replace_append(dba, key, NULL, /* is_append */ false, found); } -/* }}} */ -/* {{{ inifile_relace */ int inifile_replace(inifile *dba, const key_type *key, const val_type *value) { - return inifile_delete_replace_append(dba, key, value, 0, NULL); + return inifile_delete_replace_append(dba, key, value, /* is_append */ false, NULL); } -/* }}} */ -/* {{{ inifile_replace_ex */ int inifile_replace_ex(inifile *dba, const key_type *key, const val_type *value, bool *found) { - return inifile_delete_replace_append(dba, key, value, 0, found); + return inifile_delete_replace_append(dba, key, value, /* is_append */ false, found); } -/* }}} */ -/* {{{ inifile_append */ int inifile_append(inifile *dba, const key_type *key, const val_type *value) { - return inifile_delete_replace_append(dba, key, value, 1, NULL); + return inifile_delete_replace_append(dba, key, value, /* is_append */ true, NULL); } -/* }}} */ diff --git a/ext/dba/libinifile/inifile.h b/ext/dba/libinifile/inifile.h index e931c70bcbf6b..f2fcd571d3c76 100644 --- a/ext/dba/libinifile/inifile.h +++ b/ext/dba/libinifile/inifile.h @@ -17,6 +17,9 @@ #ifndef PHP_LIB_INIFILE_H #define PHP_LIB_INIFILE_H +#include +#include + typedef struct { char *group; char *name; @@ -36,14 +39,14 @@ typedef struct { char *lockfn; int lockfd; php_stream *fp; - int readonly; + bool readonly; line_type curr; line_type next; } inifile; val_type inifile_fetch(inifile *dba, const key_type *key, int skip); -int inifile_firstkey(inifile *dba); -int inifile_nextkey(inifile *dba); +bool inifile_firstkey(inifile *dba); +bool inifile_nextkey(inifile *dba); int inifile_delete(inifile *dba, const key_type *key); int inifile_delete_ex(inifile *dba, const key_type *key, bool *found); int inifile_replace(inifile *dba, const key_type *key, const val_type *val); @@ -58,7 +61,7 @@ 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); -void inifile_free(inifile *dba, int persistent); +inifile * inifile_alloc(php_stream *fp, bool readonly, bool persistent); +void inifile_free(inifile *dba, bool persistent); #endif diff --git a/ext/dba/tests/dba_inifile_group_keys.phpt b/ext/dba/tests/dba_inifile_group_keys.phpt new file mode 100644 index 0000000000000..b435c44f68093 --- /dev/null +++ b/ext/dba/tests/dba_inifile_group_keys.phpt @@ -0,0 +1,146 @@ +--TEST-- +DBA check behaviour of array keys (inifile version) +--EXTENSIONS-- +dba +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +Checks on an empty database: +bool(false) +bool(true) +bool(true) +string(12) "Normal group" +bool(true) +Insert various group values: +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +Check INI file content: +bool(true) +Insert an existing value in group1, this should fail +bool(true) +string(12) "Group1Value2" +Check INI file content: +bool(true) +Insert a new value in group1 +bool(true) +string(12) "Group1Value4" +Check INI file content: +bool(true) +Replace value of ['group1', 'name3'] with 'Group1Value3replaced' +bool(true) +string(12) "Group1Value3" +Check INI file content: +bool(true)