root/ext/dba/libinifile/inifile.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. inifile_version
  2. inifile_key_free
  3. inifile_val_free
  4. inifile_line_free
  5. inifile_alloc
  6. inifile_free
  7. inifile_key_split
  8. inifile_key_string
  9. etrim
  10. inifile_read
  11. inifile_key_cmp
  12. inifile_fetch
  13. inifile_firstkey
  14. inifile_nextkey
  15. inifile_truncate
  16. inifile_find_group
  17. inifile_next_group
  18. inifile_copy_to
  19. inifile_filter
  20. inifile_delete_replace_append
  21. inifile_delete
  22. inifile_delete_ex
  23. inifile_replace
  24. inifile_replace_ex
  25. inifile_append

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

/* [<][>][^][v][top][bottom][index][help] */