Annotation of embedaddon/php/ext/dba/libinifile/inifile.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2010 The PHP Group |
! 6: +----------------------------------------------------------------------+
! 7: | This source file is subject to version 3.01 of the PHP license, |
! 8: | that is bundled with this package in the file LICENSE, and is |
! 9: | available through the world-wide-web at the following url: |
! 10: | http://www.php.net/license/3_01.txt |
! 11: | If you did not receive a copy of the PHP license and are unable to |
! 12: | obtain it through the world-wide-web, please send a note to |
! 13: | license@php.net so we can mail you a copy immediately. |
! 14: +----------------------------------------------------------------------+
! 15: | Author: Marcus Boerger <helly@php.net> |
! 16: +----------------------------------------------------------------------+
! 17: */
! 18:
! 19: /* $Id: inifile.c 293036 2010-01-03 09:23:27Z sebastian $ */
! 20:
! 21: #ifdef HAVE_CONFIG_H
! 22: #include "config.h"
! 23: #endif
! 24:
! 25: #include "php.h"
! 26: #include "php_globals.h"
! 27: #include "safe_mode.h"
! 28:
! 29: #include <stdlib.h>
! 30: #include <string.h>
! 31: #include <errno.h>
! 32: #if HAVE_UNISTD_H
! 33: #include <unistd.h>
! 34: #endif
! 35:
! 36: #include "inifile.h"
! 37:
! 38: /* ret = -1 means that database was opened for read-only
! 39: * ret = 0 success
! 40: * ret = 1 key already exists - nothing done
! 41: */
! 42:
! 43: /* {{{ inifile_version */
! 44: char *inifile_version()
! 45: {
! 46: return "1.0, $Revision: 293036 $";
! 47: }
! 48: /* }}} */
! 49:
! 50: /* {{{ inifile_free_key */
! 51: void inifile_key_free(key_type *key)
! 52: {
! 53: if (key->group) {
! 54: efree(key->group);
! 55: }
! 56: if (key->name) {
! 57: efree(key->name);
! 58: }
! 59: memset(key, 0, sizeof(key_type));
! 60: }
! 61: /* }}} */
! 62:
! 63: /* {{{ inifile_free_val */
! 64: void inifile_val_free(val_type *val)
! 65: {
! 66: if (val->value) {
! 67: efree(val->value);
! 68: }
! 69: memset(val, 0, sizeof(val_type));
! 70: }
! 71: /* }}} */
! 72:
! 73: /* {{{ inifile_free_val */
! 74: void inifile_line_free(line_type *ln)
! 75: {
! 76: inifile_key_free(&ln->key);
! 77: inifile_val_free(&ln->val);
! 78: ln->pos = 0;
! 79: }
! 80: /* }}} */
! 81:
! 82: /* {{{ inifile_alloc */
! 83: inifile * inifile_alloc(php_stream *fp, int readonly, int persistent TSRMLS_DC)
! 84: {
! 85: inifile *dba;
! 86:
! 87: if (!readonly) {
! 88: if (!php_stream_truncate_supported(fp)) {
! 89: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate this stream");
! 90: return NULL;
! 91: }
! 92: }
! 93:
! 94: dba = pemalloc(sizeof(inifile), persistent);
! 95: memset(dba, 0, sizeof(inifile));
! 96: dba->fp = fp;
! 97: dba->readonly = readonly;
! 98: return dba;
! 99: }
! 100: /* }}} */
! 101:
! 102: /* {{{ inifile_free */
! 103: void inifile_free(inifile *dba, int persistent)
! 104: {
! 105: if (dba) {
! 106: inifile_line_free(&dba->curr);
! 107: inifile_line_free(&dba->next);
! 108: pefree(dba, persistent);
! 109: }
! 110: }
! 111: /* }}} */
! 112:
! 113: /* {{{ inifile_key_split */
! 114: key_type inifile_key_split(const char *group_name)
! 115: {
! 116: key_type key;
! 117: char *name;
! 118:
! 119: if (group_name[0] == '[' && (name = strchr(group_name, ']')) != NULL) {
! 120: key.group = estrndup(group_name+1, name - (group_name + 1));
! 121: key.name = estrdup(name+1);
! 122: } else {
! 123: key.group = estrdup("");
! 124: key.name = estrdup(group_name);
! 125: }
! 126: return key;
! 127: }
! 128: /* }}} */
! 129:
! 130: /* {{{ inifile_key_string */
! 131: char * inifile_key_string(const key_type *key)
! 132: {
! 133: if (key->group && *key->group) {
! 134: char *result;
! 135: spprintf(&result, 0, "[%s]%s", key->group, key->name ? key->name : "");
! 136: return result;
! 137: } else if (key->name) {
! 138: return estrdup(key->name);
! 139: } else {
! 140: return NULL;
! 141: }
! 142: }
! 143: /* }}} */
! 144:
! 145: /* {{{ etrim */
! 146: static char *etrim(const char *str)
! 147: {
! 148: char *val;
! 149: size_t l;
! 150:
! 151: if (!str) {
! 152: return NULL;
! 153: }
! 154: val = (char*)str;
! 155: while (*val && strchr(" \t\r\n", *val)) {
! 156: val++;
! 157: }
! 158: l = strlen(val);
! 159: while (l && (strchr(" \t\r\n", val[l-1]))) {
! 160: l--;
! 161: }
! 162: return estrndup(val, l);
! 163: }
! 164: /* }}} */
! 165:
! 166: /* {{{ inifile_findkey
! 167: */
! 168: static int inifile_read(inifile *dba, line_type *ln TSRMLS_DC) {
! 169: char *fline;
! 170: char *pos;
! 171:
! 172: inifile_val_free(&ln->val);
! 173: while ((fline = php_stream_gets(dba->fp, NULL, 0)) != NULL) {
! 174: if (fline) {
! 175: if (fline[0] == '[') {
! 176: /* A value name cannot start with '['
! 177: * So either we find a ']' or we found an error
! 178: */
! 179: pos = strchr(fline+1, ']');
! 180: if (pos) {
! 181: *pos = '\0';
! 182: inifile_key_free(&ln->key);
! 183: ln->key.group = etrim(fline+1);
! 184: ln->key.name = estrdup("");
! 185: ln->pos = php_stream_tell(dba->fp);
! 186: efree(fline);
! 187: return 1;
! 188: } else {
! 189: efree(fline);
! 190: continue;
! 191: }
! 192: } else {
! 193: pos = strchr(fline, '=');
! 194: if (pos) {
! 195: *pos = '\0';
! 196: /* keep group or make empty if not existent */
! 197: if (!ln->key.group) {
! 198: ln->key.group = estrdup("");
! 199: }
! 200: if (ln->key.name) {
! 201: efree(ln->key.name);
! 202: }
! 203: ln->key.name = etrim(fline);
! 204: ln->val.value = etrim(pos+1);
! 205: ln->pos = php_stream_tell(dba->fp);
! 206: efree(fline);
! 207: return 1;
! 208: } else {
! 209: /* simply ignore lines without '='
! 210: * those should be comments
! 211: */
! 212: efree(fline);
! 213: continue;
! 214: }
! 215: }
! 216: }
! 217: }
! 218: inifile_line_free(ln);
! 219: return 0;
! 220: }
! 221: /* }}} */
! 222:
! 223: /* {{{ inifile_key_cmp */
! 224: /* 0 = EQUAL
! 225: * 1 = GROUP-EQUAL,NAME-DIFFERENT
! 226: * 2 = DIFFERENT
! 227: */
! 228: static int inifile_key_cmp(const key_type *k1, const key_type *k2 TSRMLS_DC)
! 229: {
! 230: assert(k1->group && k1->name && k2->group && k2->name);
! 231:
! 232: if (!strcasecmp(k1->group, k2->group)) {
! 233: if (!strcasecmp(k1->name, k2->name)) {
! 234: return 0;
! 235: } else {
! 236: return 1;
! 237: }
! 238: } else {
! 239: return 2;
! 240: }
! 241: }
! 242: /* }}} */
! 243:
! 244: /* {{{ inifile_fetch
! 245: */
! 246: val_type inifile_fetch(inifile *dba, const key_type *key, int skip TSRMLS_DC) {
! 247: line_type ln = {{NULL,NULL},{NULL}};
! 248: val_type val;
! 249: int res, grp_eq = 0;
! 250:
! 251: if (skip == -1 && dba->next.key.group && dba->next.key.name && !inifile_key_cmp(&dba->next.key, key TSRMLS_CC)) {
! 252: /* we got position already from last fetch */
! 253: php_stream_seek(dba->fp, dba->next.pos, SEEK_SET);
! 254: } else {
! 255: /* specific instance or not same key -> restart search */
! 256: /* the slow way: restart and seacrch */
! 257: php_stream_rewind(dba->fp);
! 258: inifile_line_free(&dba->next);
! 259: }
! 260: if (skip == -1) {
! 261: skip = 0;
! 262: }
! 263: while(inifile_read(dba, &ln TSRMLS_CC)) {
! 264: if (!(res=inifile_key_cmp(&ln.key, key TSRMLS_CC))) {
! 265: if (!skip) {
! 266: val.value = estrdup(ln.val.value ? ln.val.value : "");
! 267: /* allow faster access by updating key read into next */
! 268: inifile_line_free(&dba->next);
! 269: dba->next = ln;
! 270: dba->next.pos = php_stream_tell(dba->fp);
! 271: return val;
! 272: }
! 273: skip--;
! 274: } else if (res == 1) {
! 275: grp_eq = 1;
! 276: } else if (grp_eq) {
! 277: /* we are leaving group now: that means we cannot find the key */
! 278: break;
! 279: }
! 280: }
! 281: inifile_line_free(&ln);
! 282: dba->next.pos = php_stream_tell(dba->fp);
! 283: return ln.val;
! 284: }
! 285: /* }}} */
! 286:
! 287: /* {{{ inifile_firstkey
! 288: */
! 289: int inifile_firstkey(inifile *dba TSRMLS_DC) {
! 290: inifile_line_free(&dba->curr);
! 291: dba->curr.pos = 0;
! 292: return inifile_nextkey(dba TSRMLS_CC);
! 293: }
! 294: /* }}} */
! 295:
! 296: /* {{{ inifile_nextkey
! 297: */
! 298: int inifile_nextkey(inifile *dba TSRMLS_DC) {
! 299: line_type ln = {{NULL,NULL},{NULL}};
! 300:
! 301: /*inifile_line_free(&dba->next); ??? */
! 302: php_stream_seek(dba->fp, dba->curr.pos, SEEK_SET);
! 303: ln.key.group = estrdup(dba->curr.key.group ? dba->curr.key.group : "");
! 304: inifile_read(dba, &ln TSRMLS_CC);
! 305: inifile_line_free(&dba->curr);
! 306: dba->curr = ln;
! 307: return ln.key.group || ln.key.name;
! 308: }
! 309: /* }}} */
! 310:
! 311: /* {{{ inifile_truncate
! 312: */
! 313: static int inifile_truncate(inifile *dba, size_t size TSRMLS_DC)
! 314: {
! 315: int res;
! 316:
! 317: if ((res=php_stream_truncate_set_size(dba->fp, size)) != 0) {
! 318: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error in ftruncate: %d", res);
! 319: return FAILURE;
! 320: }
! 321: php_stream_seek(dba->fp, size, SEEK_SET);
! 322: return SUCCESS;
! 323: }
! 324: /* }}} */
! 325:
! 326: /* {{{ inifile_find_group
! 327: * if found pos_grp_start points to "[group_name]"
! 328: */
! 329: static int inifile_find_group(inifile *dba, const key_type *key, size_t *pos_grp_start TSRMLS_DC)
! 330: {
! 331: int ret = FAILURE;
! 332:
! 333: php_stream_flush(dba->fp);
! 334: php_stream_seek(dba->fp, 0, SEEK_SET);
! 335: inifile_line_free(&dba->curr);
! 336: inifile_line_free(&dba->next);
! 337:
! 338: if (key->group && strlen(key->group)) {
! 339: int res;
! 340: line_type ln = {{NULL,NULL},{NULL}};
! 341:
! 342: res = 1;
! 343: while(inifile_read(dba, &ln TSRMLS_CC)) {
! 344: if ((res=inifile_key_cmp(&ln.key, key TSRMLS_CC)) < 2) {
! 345: ret = SUCCESS;
! 346: break;
! 347: }
! 348: *pos_grp_start = php_stream_tell(dba->fp);
! 349: }
! 350: inifile_line_free(&ln);
! 351: } else {
! 352: *pos_grp_start = 0;
! 353: ret = SUCCESS;
! 354: }
! 355: if (ret == FAILURE) {
! 356: *pos_grp_start = php_stream_tell(dba->fp);
! 357: }
! 358: return ret;
! 359: }
! 360: /* }}} */
! 361:
! 362: /* {{{ inifile_next_group
! 363: * only valid after a call to inifile_find_group
! 364: * if any next group is found pos_grp_start points to "[group_name]" or whitespace before that
! 365: */
! 366: static int inifile_next_group(inifile *dba, const key_type *key, size_t *pos_grp_start TSRMLS_DC)
! 367: {
! 368: int ret = FAILURE;
! 369: line_type ln = {{NULL,NULL},{NULL}};
! 370:
! 371: *pos_grp_start = php_stream_tell(dba->fp);
! 372: ln.key.group = estrdup(key->group);
! 373: while(inifile_read(dba, &ln TSRMLS_CC)) {
! 374: if (inifile_key_cmp(&ln.key, key TSRMLS_CC) == 2) {
! 375: ret = SUCCESS;
! 376: break;
! 377: }
! 378: *pos_grp_start = php_stream_tell(dba->fp);
! 379: }
! 380: inifile_line_free(&ln);
! 381: return ret;
! 382: }
! 383: /* }}} */
! 384:
! 385: /* {{{ inifile_copy_to
! 386: */
! 387: static int inifile_copy_to(inifile *dba, size_t pos_start, size_t pos_end, inifile **ini_copy TSRMLS_DC)
! 388: {
! 389: php_stream *fp;
! 390:
! 391: if (pos_start == pos_end) {
! 392: *ini_copy = NULL;
! 393: return SUCCESS;
! 394: }
! 395: if ((fp = php_stream_temp_create(0, 64 * 1024)) == NULL) {
! 396: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create temporary stream");
! 397: *ini_copy = NULL;
! 398: return FAILURE;
! 399: }
! 400:
! 401: if ((*ini_copy = inifile_alloc(fp, 1, 0 TSRMLS_CC)) == NULL) {
! 402: /* writes error */
! 403: return FAILURE;
! 404: }
! 405: php_stream_seek(dba->fp, pos_start, SEEK_SET);
! 406: if (!php_stream_copy_to_stream(dba->fp, fp, pos_end - pos_start)) {
! 407: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy group [%zu - %zu] to temporary stream", pos_start, pos_end);
! 408: return FAILURE;
! 409: }
! 410: return SUCCESS;
! 411: }
! 412: /* }}} */
! 413:
! 414: /* {{{ inifile_filter
! 415: * copy from to dba while ignoring key name (group must equal)
! 416: */
! 417: static int inifile_filter(inifile *dba, inifile *from, const key_type *key TSRMLS_DC)
! 418: {
! 419: size_t pos_start = 0, pos_next = 0, pos_curr;
! 420: int ret = SUCCESS;
! 421: line_type ln = {{NULL,NULL},{NULL}};
! 422:
! 423: php_stream_seek(from->fp, 0, SEEK_SET);
! 424: php_stream_seek(dba->fp, 0, SEEK_END);
! 425: while(inifile_read(from, &ln TSRMLS_CC)) {
! 426: switch(inifile_key_cmp(&ln.key, key TSRMLS_CC)) {
! 427: case 0:
! 428: pos_curr = php_stream_tell(from->fp);
! 429: if (pos_start != pos_next) {
! 430: php_stream_seek(from->fp, pos_start, SEEK_SET);
! 431: if (!php_stream_copy_to_stream(from->fp, dba->fp, pos_next - pos_start)) {
! 432: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy [%zu - %zu] from temporary stream", pos_next, pos_start);
! 433: ret = FAILURE;
! 434: }
! 435: php_stream_seek(from->fp, pos_curr, SEEK_SET);
! 436: }
! 437: pos_next = pos_start = pos_curr;
! 438: break;
! 439: case 1:
! 440: pos_next = php_stream_tell(from->fp);
! 441: break;
! 442: case 2:
! 443: /* the function is meant to process only entries from same group */
! 444: assert(0);
! 445: break;
! 446: }
! 447: }
! 448: if (pos_start != pos_next) {
! 449: php_stream_seek(from->fp, pos_start, SEEK_SET);
! 450: if (!php_stream_copy_to_stream(from->fp, dba->fp, pos_next - pos_start)) {
! 451: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy [%zu - %zu] from temporary stream", pos_next, pos_start);
! 452: ret = FAILURE;
! 453: }
! 454: }
! 455: inifile_line_free(&ln);
! 456: return SUCCESS;
! 457: }
! 458: /* }}} */
! 459:
! 460: /* {{{ inifile_delete_replace_append
! 461: */
! 462: static int inifile_delete_replace_append(inifile *dba, const key_type *key, const val_type *value, int append TSRMLS_DC)
! 463: {
! 464: size_t pos_grp_start, pos_grp_next;
! 465: inifile *ini_tmp = NULL;
! 466: php_stream *fp_tmp = NULL;
! 467: int ret;
! 468:
! 469: /* 1) Search group start
! 470: * 2) Search next group
! 471: * 3) If not append: Copy group to ini_tmp
! 472: * 4) Open temp_stream and copy remainder
! 473: * 5) Truncate stream
! 474: * 6) If not append AND key.name given: Filtered copy back from ini_tmp
! 475: * to stream. Otherwise the user wanted to delete the group.
! 476: * 7) Append value if given
! 477: * 8) Append temporary stream
! 478: */
! 479:
! 480: assert(!append || (key->name && value)); /* missuse */
! 481:
! 482: /* 1 - 3 */
! 483: inifile_find_group(dba, key, &pos_grp_start TSRMLS_CC);
! 484: inifile_next_group(dba, key, &pos_grp_next TSRMLS_CC);
! 485: if (append) {
! 486: ret = SUCCESS;
! 487: } else {
! 488: ret = inifile_copy_to(dba, pos_grp_start, pos_grp_next, &ini_tmp TSRMLS_CC);
! 489: }
! 490:
! 491: /* 4 */
! 492: if (ret == SUCCESS) {
! 493: fp_tmp = php_stream_temp_create(0, 64 * 1024);
! 494: if (!fp_tmp) {
! 495: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create temporary stream");
! 496: ret = FAILURE;
! 497: } else {
! 498: php_stream_seek(dba->fp, 0, SEEK_END);
! 499: if (pos_grp_next != (size_t)php_stream_tell(dba->fp)) {
! 500: php_stream_seek(dba->fp, pos_grp_next, SEEK_SET);
! 501: if (!php_stream_copy_to_stream(dba->fp, fp_tmp, PHP_STREAM_COPY_ALL)) {
! 502: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy remainder to temporary stream");
! 503: ret = FAILURE;
! 504: }
! 505: }
! 506: }
! 507: }
! 508:
! 509: /* 5 */
! 510: if (ret == SUCCESS) {
! 511: if (!value || (key->name && strlen(key->name))) {
! 512: ret = inifile_truncate(dba, append ? pos_grp_next : pos_grp_start TSRMLS_CC); /* writes error on fail */
! 513: }
! 514: }
! 515:
! 516: if (ret == SUCCESS) {
! 517: if (key->name && strlen(key->name)) {
! 518: /* 6 */
! 519: if (!append && ini_tmp) {
! 520: ret = inifile_filter(dba, ini_tmp, key TSRMLS_CC);
! 521: }
! 522:
! 523: /* 7 */
! 524: /* important: do not query ret==SUCCESS again: inifile_filter might fail but
! 525: * however next operation must be done.
! 526: */
! 527: if (value) {
! 528: if (pos_grp_start == pos_grp_next && key->group && strlen(key->group)) {
! 529: php_stream_printf(dba->fp TSRMLS_CC, "[%s]\n", key->group);
! 530: }
! 531: php_stream_printf(dba->fp TSRMLS_CC, "%s=%s\n", key->name, value->value ? value->value : "");
! 532: }
! 533: }
! 534:
! 535: /* 8 */
! 536: /* important: do not query ret==SUCCESS again: inifile_filter might fail but
! 537: * however next operation must be done.
! 538: */
! 539: if (fp_tmp && php_stream_tell(fp_tmp)) {
! 540: php_stream_seek(fp_tmp, 0, SEEK_SET);
! 541: php_stream_seek(dba->fp, 0, SEEK_END);
! 542: if (!php_stream_copy_to_stream(fp_tmp, dba->fp, PHP_STREAM_COPY_ALL)) {
! 543: php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Could not copy from temporary stream - ini file truncated");
! 544: ret = FAILURE;
! 545: }
! 546: }
! 547: }
! 548:
! 549: if (ini_tmp) {
! 550: php_stream_close(ini_tmp->fp);
! 551: inifile_free(ini_tmp, 0);
! 552: }
! 553: if (fp_tmp) {
! 554: php_stream_close(fp_tmp);
! 555: }
! 556: php_stream_flush(dba->fp);
! 557: php_stream_seek(dba->fp, 0, SEEK_SET);
! 558:
! 559: return ret;
! 560: }
! 561: /* }}} */
! 562:
! 563: /* {{{ inifile_delete
! 564: */
! 565: int inifile_delete(inifile *dba, const key_type *key TSRMLS_DC)
! 566: {
! 567: return inifile_delete_replace_append(dba, key, NULL, 0 TSRMLS_CC);
! 568: }
! 569: /* }}} */
! 570:
! 571: /* {{{ inifile_relace
! 572: */
! 573: int inifile_replace(inifile *dba, const key_type *key, const val_type *value TSRMLS_DC)
! 574: {
! 575: return inifile_delete_replace_append(dba, key, value, 0 TSRMLS_CC);
! 576: }
! 577: /* }}} */
! 578:
! 579: /* {{{ inifile_append
! 580: */
! 581: int inifile_append(inifile *dba, const key_type *key, const val_type *value TSRMLS_DC)
! 582: {
! 583: return inifile_delete_replace_append(dba, key, value, 1 TSRMLS_CC);
! 584: }
! 585: /* }}} */
! 586:
! 587: /*
! 588: * Local variables:
! 589: * tab-width: 4
! 590: * c-basic-offset: 4
! 591: * End:
! 592: * vim600: sw=4 ts=4 fdm=marker
! 593: * vim<600: sw=4 ts=4
! 594: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>