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