Back to index

php5  5.3.10
inifile.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2010 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Marcus Boerger <helly@php.net>                               |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: inifile.c 293036 2010-01-03 09:23:27Z sebastian $ */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024 
00025 #include "php.h"
00026 #include "php_globals.h"
00027 #include "safe_mode.h"
00028 
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <errno.h>
00032 #if HAVE_UNISTD_H
00033 #include <unistd.h>
00034 #endif
00035 
00036 #include "inifile.h"
00037 
00038 /* ret = -1 means that database was opened for read-only
00039  * ret = 0  success
00040  * ret = 1  key already exists - nothing done
00041  */
00042 
00043 /* {{{ inifile_version */
00044 char *inifile_version() 
00045 {
00046        return "1.0, $Revision: 293036 $";
00047 }
00048 /* }}} */ 
00049 
00050 /* {{{ inifile_free_key */
00051 void inifile_key_free(key_type *key)
00052 {
00053        if (key->group) {
00054               efree(key->group);
00055        }
00056        if (key->name) {
00057               efree(key->name);
00058        }
00059        memset(key, 0, sizeof(key_type));
00060 }
00061 /* }}} */
00062 
00063 /* {{{ inifile_free_val */
00064 void inifile_val_free(val_type *val)
00065 {
00066        if (val->value) {
00067               efree(val->value);
00068        }
00069        memset(val, 0, sizeof(val_type));
00070 }
00071 /* }}} */
00072 
00073 /* {{{ inifile_free_val */
00074 void inifile_line_free(line_type *ln)
00075 {
00076        inifile_key_free(&ln->key);
00077        inifile_val_free(&ln->val);
00078        ln->pos = 0;
00079 }
00080 /* }}} */
00081 
00082 /* {{{ inifile_alloc */
00083 inifile * inifile_alloc(php_stream *fp, int readonly, int persistent TSRMLS_DC)
00084 {
00085        inifile *dba;
00086 
00087        if (!readonly) {
00088               if (!php_stream_truncate_supported(fp)) {
00089                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't truncate this stream");
00090                      return NULL;
00091               }
00092        }
00093  
00094        dba = pemalloc(sizeof(inifile), persistent);
00095        memset(dba, 0, sizeof(inifile));
00096        dba->fp = fp;
00097        dba->readonly = readonly;
00098        return dba;
00099 }
00100 /* }}} */
00101 
00102 /* {{{ inifile_free */
00103 void inifile_free(inifile *dba, int persistent)
00104 {
00105        if (dba) {
00106               inifile_line_free(&dba->curr);
00107               inifile_line_free(&dba->next);
00108               pefree(dba, persistent);
00109        }
00110 }
00111 /* }}} */
00112 
00113 /* {{{ inifile_key_split */
00114 key_type inifile_key_split(const char *group_name)
00115 {
00116        key_type key;
00117        char *name;
00118        
00119        if (group_name[0] == '[' && (name = strchr(group_name, ']')) != NULL) {
00120               key.group = estrndup(group_name+1, name - (group_name + 1));
00121               key.name = estrdup(name+1);
00122        } else {
00123               key.group = estrdup("");
00124               key.name = estrdup(group_name);
00125        }
00126        return key;
00127 }
00128 /* }}} */
00129 
00130 /* {{{ inifile_key_string */
00131 char * inifile_key_string(const key_type *key)
00132 {
00133        if (key->group && *key->group) {
00134               char *result;
00135               spprintf(&result, 0, "[%s]%s", key->group, key->name ? key->name : "");
00136               return result;
00137        } else if (key->name) {
00138               return estrdup(key->name);
00139        } else {
00140               return NULL;
00141        }
00142 }
00143 /* }}} */
00144 
00145 /* {{{ etrim */
00146 static char *etrim(const char *str)
00147 {
00148        char *val;
00149        size_t l;
00150        
00151        if (!str) {
00152               return NULL;
00153        }
00154        val = (char*)str;
00155        while (*val && strchr(" \t\r\n", *val)) {
00156               val++;
00157        }
00158        l = strlen(val);
00159        while (l && (strchr(" \t\r\n", val[l-1]))) {
00160               l--;
00161        }
00162        return estrndup(val, l);
00163 }
00164 /* }}} */
00165 
00166 /* {{{ inifile_findkey
00167  */
00168 static int inifile_read(inifile *dba, line_type *ln TSRMLS_DC) {
00169        char *fline;
00170        char *pos;
00171 
00172        inifile_val_free(&ln->val);
00173        while ((fline = php_stream_gets(dba->fp, NULL, 0)) != NULL) {
00174               if (fline) {
00175                      if (fline[0] == '[') {
00176                             /* A value name cannot start with '['
00177                              * So either we find a ']' or we found an error
00178                              */
00179                             pos = strchr(fline+1, ']');
00180                             if (pos) {
00181                                    *pos = '\0';
00182                                    inifile_key_free(&ln->key);
00183                                    ln->key.group = etrim(fline+1);
00184                                    ln->key.name = estrdup("");
00185                                    ln->pos = php_stream_tell(dba->fp);
00186                                    efree(fline);
00187                                    return 1;
00188                             } else {
00189                                    efree(fline);
00190                                    continue;
00191                             }
00192                      } else {
00193                             pos = strchr(fline, '=');
00194                             if (pos) {
00195                                    *pos = '\0';
00196                                    /* keep group or make empty if not existent */
00197                                    if (!ln->key.group) {
00198                                           ln->key.group = estrdup("");
00199                                    }
00200                                    if (ln->key.name) {
00201                                           efree(ln->key.name);
00202                                    }
00203                                    ln->key.name = etrim(fline);
00204                                    ln->val.value = etrim(pos+1);
00205                                    ln->pos = php_stream_tell(dba->fp);
00206                                    efree(fline);
00207                                    return 1;
00208                             } else {
00209                                    /* simply ignore lines without '='
00210                                     * those should be comments
00211                                     */
00212                                     efree(fline);
00213                                     continue;
00214                             }
00215                      }
00216               }
00217        }
00218        inifile_line_free(ln);
00219        return 0;
00220 }
00221 /* }}} */
00222 
00223 /* {{{ inifile_key_cmp */
00224 /* 0 = EQUAL
00225  * 1 = GROUP-EQUAL,NAME-DIFFERENT
00226  * 2 = DIFFERENT
00227  */
00228 static int inifile_key_cmp(const key_type *k1, const key_type *k2 TSRMLS_DC)
00229 {
00230        assert(k1->group && k1->name && k2->group && k2->name);
00231        
00232        if (!strcasecmp(k1->group, k2->group)) {
00233               if (!strcasecmp(k1->name, k2->name)) {
00234                      return 0;
00235               } else {
00236                      return 1;
00237               }
00238        } else {
00239               return 2;
00240        }
00241 }
00242 /* }}} */
00243 
00244 /* {{{ inifile_fetch
00245  */
00246 val_type inifile_fetch(inifile *dba, const key_type *key, int skip TSRMLS_DC) {
00247        line_type ln = {{NULL,NULL},{NULL}};
00248        val_type val;
00249        int res, grp_eq = 0;
00250 
00251        if (skip == -1 && dba->next.key.group && dba->next.key.name && !inifile_key_cmp(&dba->next.key, key TSRMLS_CC)) {
00252               /* we got position already from last fetch */
00253               php_stream_seek(dba->fp, dba->next.pos, SEEK_SET);
00254        } else {
00255               /* specific instance or not same key -> restart search */
00256               /* the slow way: restart and seacrch */
00257               php_stream_rewind(dba->fp);
00258               inifile_line_free(&dba->next);
00259        }
00260        if (skip == -1) {
00261               skip = 0;
00262        }
00263        while(inifile_read(dba, &ln TSRMLS_CC)) {
00264               if (!(res=inifile_key_cmp(&ln.key, key TSRMLS_CC))) {
00265                      if (!skip) {
00266                             val.value = estrdup(ln.val.value ? ln.val.value : "");
00267                             /* allow faster access by updating key read into next */
00268                             inifile_line_free(&dba->next);
00269                             dba->next = ln;
00270                             dba->next.pos = php_stream_tell(dba->fp);
00271                             return val;
00272                      }
00273                      skip--;
00274               } else if (res == 1) {
00275                      grp_eq = 1;
00276               } else if (grp_eq) {
00277                      /* we are leaving group now: that means we cannot find the key */
00278                      break;
00279               }
00280        }
00281        inifile_line_free(&ln);
00282        dba->next.pos = php_stream_tell(dba->fp);
00283        return ln.val;
00284 }
00285 /* }}} */
00286 
00287 /* {{{ inifile_firstkey
00288  */
00289 int inifile_firstkey(inifile *dba TSRMLS_DC) {
00290        inifile_line_free(&dba->curr);
00291        dba->curr.pos = 0;
00292        return inifile_nextkey(dba TSRMLS_CC);
00293 }
00294 /* }}} */
00295 
00296 /* {{{ inifile_nextkey
00297  */
00298 int inifile_nextkey(inifile *dba TSRMLS_DC) {
00299        line_type ln = {{NULL,NULL},{NULL}};
00300 
00301        /*inifile_line_free(&dba->next); ??? */
00302        php_stream_seek(dba->fp, dba->curr.pos, SEEK_SET);
00303        ln.key.group = estrdup(dba->curr.key.group ? dba->curr.key.group : "");
00304        inifile_read(dba, &ln TSRMLS_CC);
00305        inifile_line_free(&dba->curr);
00306        dba->curr = ln;
00307        return ln.key.group || ln.key.name;
00308 }      
00309 /* }}} */
00310 
00311 /* {{{ inifile_truncate
00312  */
00313 static int inifile_truncate(inifile *dba, size_t size TSRMLS_DC)
00314 {
00315        int res;
00316 
00317        if ((res=php_stream_truncate_set_size(dba->fp, size)) != 0) {
00318               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error in ftruncate: %d", res);
00319               return FAILURE;
00320        }
00321        php_stream_seek(dba->fp, size, SEEK_SET);
00322        return SUCCESS;
00323 }
00324 /* }}} */
00325 
00326 /* {{{ inifile_find_group
00327  * if found pos_grp_start points to "[group_name]"
00328  */
00329 static int inifile_find_group(inifile *dba, const key_type *key, size_t *pos_grp_start TSRMLS_DC) 
00330 {
00331        int ret = FAILURE;
00332 
00333        php_stream_flush(dba->fp);
00334        php_stream_seek(dba->fp, 0, SEEK_SET);
00335        inifile_line_free(&dba->curr);
00336        inifile_line_free(&dba->next);
00337 
00338        if (key->group && strlen(key->group)) {
00339               int res;
00340               line_type ln = {{NULL,NULL},{NULL}};
00341 
00342               res = 1;
00343               while(inifile_read(dba, &ln TSRMLS_CC)) {
00344                      if ((res=inifile_key_cmp(&ln.key, key TSRMLS_CC)) < 2) {
00345                             ret = SUCCESS;
00346                             break;
00347                      }
00348                      *pos_grp_start = php_stream_tell(dba->fp);
00349               }
00350               inifile_line_free(&ln);
00351        } else {
00352               *pos_grp_start = 0;
00353               ret = SUCCESS;
00354        }
00355        if (ret == FAILURE) {
00356               *pos_grp_start = php_stream_tell(dba->fp);
00357        }
00358        return ret;
00359 }
00360 /* }}} */
00361 
00362 /* {{{ inifile_next_group
00363  * only valid after a call to inifile_find_group
00364  * if any next group is found pos_grp_start points to "[group_name]" or whitespace before that
00365  */
00366 static int inifile_next_group(inifile *dba, const key_type *key, size_t *pos_grp_start TSRMLS_DC) 
00367 {
00368        int ret = FAILURE;
00369        line_type ln = {{NULL,NULL},{NULL}};
00370 
00371        *pos_grp_start = php_stream_tell(dba->fp);
00372        ln.key.group = estrdup(key->group);
00373        while(inifile_read(dba, &ln TSRMLS_CC)) {
00374               if (inifile_key_cmp(&ln.key, key TSRMLS_CC) == 2) {
00375                      ret = SUCCESS;
00376                      break;
00377               }
00378               *pos_grp_start = php_stream_tell(dba->fp);
00379        }
00380        inifile_line_free(&ln);
00381        return ret;
00382 }
00383 /* }}} */
00384 
00385 /* {{{ inifile_copy_to
00386  */
00387 static int inifile_copy_to(inifile *dba, size_t pos_start, size_t pos_end, inifile **ini_copy TSRMLS_DC)
00388 {
00389        php_stream *fp;
00390        
00391        if (pos_start == pos_end) {
00392               *ini_copy = NULL;
00393               return SUCCESS;
00394        }
00395        if ((fp = php_stream_temp_create(0, 64 * 1024)) == NULL) {
00396               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create temporary stream");
00397               *ini_copy = NULL;
00398               return FAILURE;
00399        }
00400 
00401        if ((*ini_copy = inifile_alloc(fp, 1, 0 TSRMLS_CC)) == NULL) {
00402               /* writes error */
00403               return FAILURE;
00404        }
00405        php_stream_seek(dba->fp, pos_start, SEEK_SET);
00406        if (!php_stream_copy_to_stream(dba->fp, fp, pos_end - pos_start)) {
00407               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy group [%zu - %zu] to temporary stream", pos_start, pos_end);
00408               return FAILURE;
00409        } 
00410        return SUCCESS;
00411 }
00412 /* }}} */
00413 
00414 /* {{{ inifile_filter
00415  * copy from to dba while ignoring key name (group must equal)
00416  */
00417 static int inifile_filter(inifile *dba, inifile *from, const key_type *key TSRMLS_DC) 
00418 {
00419        size_t pos_start = 0, pos_next = 0, pos_curr;
00420        int ret = SUCCESS;
00421        line_type ln = {{NULL,NULL},{NULL}};
00422 
00423        php_stream_seek(from->fp, 0, SEEK_SET);
00424        php_stream_seek(dba->fp, 0, SEEK_END);
00425        while(inifile_read(from, &ln TSRMLS_CC)) {
00426               switch(inifile_key_cmp(&ln.key, key TSRMLS_CC)) {
00427               case 0:
00428                      pos_curr = php_stream_tell(from->fp);
00429                      if (pos_start != pos_next) {
00430                             php_stream_seek(from->fp, pos_start, SEEK_SET);
00431                             if (!php_stream_copy_to_stream(from->fp, dba->fp, pos_next - pos_start)) {
00432                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy [%zu - %zu] from temporary stream", pos_next, pos_start);
00433                                    ret = FAILURE;
00434                             }
00435                             php_stream_seek(from->fp, pos_curr, SEEK_SET);
00436                      }
00437                      pos_next = pos_start = pos_curr;
00438                      break;
00439               case 1:
00440                      pos_next = php_stream_tell(from->fp);
00441                      break;
00442               case 2:
00443                      /* the function is meant to process only entries from same group */
00444                      assert(0);
00445                      break;
00446               }
00447        }
00448        if (pos_start != pos_next) {
00449               php_stream_seek(from->fp, pos_start, SEEK_SET);
00450               if (!php_stream_copy_to_stream(from->fp, dba->fp, pos_next - pos_start)) {
00451                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy [%zu - %zu] from temporary stream", pos_next, pos_start);
00452                      ret = FAILURE;
00453               }
00454        }
00455        inifile_line_free(&ln);
00456        return SUCCESS;
00457 }
00458 /* }}} */
00459 
00460 /* {{{ inifile_delete_replace_append
00461  */
00462 static int inifile_delete_replace_append(inifile *dba, const key_type *key, const val_type *value, int append TSRMLS_DC) 
00463 {
00464        size_t pos_grp_start, pos_grp_next;
00465        inifile *ini_tmp = NULL;
00466        php_stream *fp_tmp = NULL;
00467        int ret;
00468 
00469        /* 1) Search group start
00470         * 2) Search next group
00471         * 3) If not append: Copy group to ini_tmp
00472         * 4) Open temp_stream and copy remainder
00473         * 5) Truncate stream
00474         * 6) If not append AND key.name given: Filtered copy back from ini_tmp 
00475         *    to stream. Otherwise the user wanted to delete the group.
00476         * 7) Append value if given
00477         * 8) Append temporary stream
00478         */
00479 
00480        assert(!append || (key->name && value)); /* missuse */
00481 
00482        /* 1 - 3 */
00483        inifile_find_group(dba, key, &pos_grp_start TSRMLS_CC);
00484        inifile_next_group(dba, key, &pos_grp_next TSRMLS_CC);
00485        if (append) {
00486               ret = SUCCESS;
00487        } else {
00488               ret = inifile_copy_to(dba, pos_grp_start, pos_grp_next, &ini_tmp TSRMLS_CC);
00489        }
00490 
00491        /* 4 */
00492        if (ret == SUCCESS) {
00493               fp_tmp = php_stream_temp_create(0, 64 * 1024);
00494               if (!fp_tmp) {
00495                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create temporary stream");
00496                      ret = FAILURE;
00497               } else {
00498                      php_stream_seek(dba->fp, 0, SEEK_END);
00499                      if (pos_grp_next != (size_t)php_stream_tell(dba->fp)) {
00500                             php_stream_seek(dba->fp, pos_grp_next, SEEK_SET);
00501                             if (!php_stream_copy_to_stream(dba->fp, fp_tmp, PHP_STREAM_COPY_ALL)) {
00502                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy remainder to temporary stream");
00503                                    ret = FAILURE;
00504                             }
00505                      }
00506               }
00507        }
00508        
00509        /* 5 */
00510        if (ret == SUCCESS) {
00511               if (!value || (key->name && strlen(key->name))) {
00512                      ret = inifile_truncate(dba, append ? pos_grp_next : pos_grp_start TSRMLS_CC); /* writes error on fail */
00513               }
00514        }
00515 
00516        if (ret == SUCCESS) {
00517               if (key->name && strlen(key->name)) {
00518                      /* 6 */
00519                      if (!append && ini_tmp) {
00520                             ret = inifile_filter(dba, ini_tmp, key TSRMLS_CC);
00521                      }
00522 
00523                      /* 7 */
00524                      /* important: do not query ret==SUCCESS again: inifile_filter might fail but
00525                       * however next operation must be done.
00526                       */
00527                      if (value) {
00528                             if (pos_grp_start == pos_grp_next && key->group && strlen(key->group)) {
00529                                    php_stream_printf(dba->fp TSRMLS_CC, "[%s]\n", key->group);
00530                             }
00531                             php_stream_printf(dba->fp TSRMLS_CC, "%s=%s\n", key->name, value->value ? value->value : "");
00532                      }
00533               }
00534               
00535               /* 8 */ 
00536               /* important: do not query ret==SUCCESS again: inifile_filter might fail but
00537                * however next operation must be done.
00538                */
00539               if (fp_tmp && php_stream_tell(fp_tmp)) {
00540                      php_stream_seek(fp_tmp, 0, SEEK_SET);
00541                      php_stream_seek(dba->fp, 0, SEEK_END);
00542                      if (!php_stream_copy_to_stream(fp_tmp, dba->fp, PHP_STREAM_COPY_ALL)) {
00543                             php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Could not copy from temporary stream - ini file truncated");
00544                             ret = FAILURE;
00545                      }
00546               }
00547        }
00548 
00549        if (ini_tmp) {
00550               php_stream_close(ini_tmp->fp);
00551               inifile_free(ini_tmp, 0);
00552        }
00553        if (fp_tmp) {
00554               php_stream_close(fp_tmp);
00555        }
00556        php_stream_flush(dba->fp);
00557        php_stream_seek(dba->fp, 0, SEEK_SET);
00558 
00559        return ret;
00560 }
00561 /* }}} */
00562 
00563 /* {{{ inifile_delete
00564  */
00565 int inifile_delete(inifile *dba, const key_type *key TSRMLS_DC) 
00566 {
00567        return inifile_delete_replace_append(dba, key, NULL, 0 TSRMLS_CC);
00568 }      
00569 /* }}} */
00570 
00571 /* {{{ inifile_relace
00572  */
00573 int inifile_replace(inifile *dba, const key_type *key, const val_type *value TSRMLS_DC) 
00574 {
00575        return inifile_delete_replace_append(dba, key, value, 0 TSRMLS_CC);
00576 }
00577 /* }}} */
00578 
00579 /* {{{ inifile_append
00580  */
00581 int inifile_append(inifile *dba, const key_type *key, const val_type *value TSRMLS_DC) 
00582 {
00583        return inifile_delete_replace_append(dba, key, value, 1 TSRMLS_CC);
00584 }
00585 /* }}} */
00586 
00587 /*
00588  * Local variables:
00589  * tab-width: 4
00590  * c-basic-offset: 4
00591  * End:
00592  * vim600: sw=4 ts=4 fdm=marker
00593  * vim<600: sw=4 ts=4
00594  */