Annotation of embedaddon/php/ext/dba/libinifile/inifile.c, revision 1.1.1.5
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.5 ! misho 5: | Copyright (c) 1997-2014 The PHP Group |
1.1 misho 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:
1.1.1.5 ! misho 19: /* $Id: 0dd17da31eb151421cec57c92abfcb6bfa5c8c78 $ */
1.1 misho 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: {
1.1.1.5 ! misho 45: return "1.0, $Id: 0dd17da31eb151421cec57c92abfcb6bfa5c8c78 $";
1.1 misho 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 TSRMLS_DC)
83: {
84: inifile *dba;
85:
86: if (!readonly) {
87: if (!php_stream_truncate_supported(fp)) {
88: php_error_docref(NULL TSRMLS_CC, 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 TSRMLS_DC) {
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 TSRMLS_DC)
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 TSRMLS_DC) {
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 TSRMLS_CC)) {
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 TSRMLS_CC)) {
263: if (!(res=inifile_key_cmp(&ln.key, key TSRMLS_CC))) {
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 TSRMLS_DC) {
289: inifile_line_free(&dba->curr);
290: dba->curr.pos = 0;
291: return inifile_nextkey(dba TSRMLS_CC);
292: }
293: /* }}} */
294:
295: /* {{{ inifile_nextkey
296: */
297: int inifile_nextkey(inifile *dba TSRMLS_DC) {
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 TSRMLS_CC);
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 TSRMLS_DC)
313: {
314: int res;
315:
316: if ((res=php_stream_truncate_set_size(dba->fp, size)) != 0) {
317: php_error_docref(NULL TSRMLS_CC, 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 TSRMLS_DC)
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 TSRMLS_CC)) {
343: if ((res=inifile_key_cmp(&ln.key, key TSRMLS_CC)) < 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 TSRMLS_DC)
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 TSRMLS_CC)) {
373: if (inifile_key_cmp(&ln.key, key TSRMLS_CC) == 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 TSRMLS_DC)
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 TSRMLS_CC, 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 TSRMLS_CC)) == NULL) {
401: /* writes error */
402: return FAILURE;
403: }
404: php_stream_seek(dba->fp, pos_start, SEEK_SET);
405: if (!php_stream_copy_to_stream(dba->fp, fp, pos_end - pos_start)) {
406: php_error_docref(NULL TSRMLS_CC, 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 TSRMLS_DC)
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 TSRMLS_CC)) {
425: switch(inifile_key_cmp(&ln.key, key TSRMLS_CC)) {
426: case 0:
427: pos_curr = php_stream_tell(from->fp);
428: if (pos_start != pos_next) {
429: php_stream_seek(from->fp, pos_start, SEEK_SET);
430: if (!php_stream_copy_to_stream(from->fp, dba->fp, pos_next - pos_start)) {
431: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy [%zu - %zu] from temporary stream", pos_next, pos_start);
432: ret = FAILURE;
433: }
434: php_stream_seek(from->fp, pos_curr, SEEK_SET);
435: }
436: pos_next = pos_start = pos_curr;
437: break;
438: case 1:
439: pos_next = php_stream_tell(from->fp);
440: break;
441: case 2:
442: /* the function is meant to process only entries from same group */
443: assert(0);
444: break;
445: }
446: }
447: if (pos_start != pos_next) {
448: php_stream_seek(from->fp, pos_start, SEEK_SET);
449: if (!php_stream_copy_to_stream(from->fp, dba->fp, pos_next - pos_start)) {
450: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy [%zu - %zu] from temporary stream", pos_next, pos_start);
451: ret = FAILURE;
452: }
453: }
454: inifile_line_free(&ln);
455: return SUCCESS;
456: }
457: /* }}} */
458:
459: /* {{{ inifile_delete_replace_append
460: */
461: static int inifile_delete_replace_append(inifile *dba, const key_type *key, const val_type *value, int append TSRMLS_DC)
462: {
1.1.1.4 misho 463: size_t pos_grp_start = 0, pos_grp_next;
1.1 misho 464: inifile *ini_tmp = NULL;
465: php_stream *fp_tmp = NULL;
466: int ret;
467:
468: /* 1) Search group start
469: * 2) Search next group
470: * 3) If not append: Copy group to ini_tmp
471: * 4) Open temp_stream and copy remainder
472: * 5) Truncate stream
473: * 6) If not append AND key.name given: Filtered copy back from ini_tmp
474: * to stream. Otherwise the user wanted to delete the group.
475: * 7) Append value if given
476: * 8) Append temporary stream
477: */
478:
479: assert(!append || (key->name && value)); /* missuse */
480:
481: /* 1 - 3 */
482: inifile_find_group(dba, key, &pos_grp_start TSRMLS_CC);
483: inifile_next_group(dba, key, &pos_grp_next TSRMLS_CC);
484: if (append) {
485: ret = SUCCESS;
486: } else {
487: ret = inifile_copy_to(dba, pos_grp_start, pos_grp_next, &ini_tmp TSRMLS_CC);
488: }
489:
490: /* 4 */
491: if (ret == SUCCESS) {
492: fp_tmp = php_stream_temp_create(0, 64 * 1024);
493: if (!fp_tmp) {
494: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create temporary stream");
495: ret = FAILURE;
496: } else {
497: php_stream_seek(dba->fp, 0, SEEK_END);
498: if (pos_grp_next != (size_t)php_stream_tell(dba->fp)) {
499: php_stream_seek(dba->fp, pos_grp_next, SEEK_SET);
500: if (!php_stream_copy_to_stream(dba->fp, fp_tmp, PHP_STREAM_COPY_ALL)) {
501: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not copy remainder to temporary stream");
502: ret = FAILURE;
503: }
504: }
505: }
506: }
507:
508: /* 5 */
509: if (ret == SUCCESS) {
510: if (!value || (key->name && strlen(key->name))) {
511: ret = inifile_truncate(dba, append ? pos_grp_next : pos_grp_start TSRMLS_CC); /* writes error on fail */
512: }
513: }
514:
515: if (ret == SUCCESS) {
516: if (key->name && strlen(key->name)) {
517: /* 6 */
518: if (!append && ini_tmp) {
519: ret = inifile_filter(dba, ini_tmp, key TSRMLS_CC);
520: }
521:
522: /* 7 */
523: /* important: do not query ret==SUCCESS again: inifile_filter might fail but
524: * however next operation must be done.
525: */
526: if (value) {
527: if (pos_grp_start == pos_grp_next && key->group && strlen(key->group)) {
528: php_stream_printf(dba->fp TSRMLS_CC, "[%s]\n", key->group);
529: }
530: php_stream_printf(dba->fp TSRMLS_CC, "%s=%s\n", key->name, value->value ? value->value : "");
531: }
532: }
533:
534: /* 8 */
535: /* important: do not query ret==SUCCESS again: inifile_filter might fail but
536: * however next operation must be done.
537: */
538: if (fp_tmp && php_stream_tell(fp_tmp)) {
539: php_stream_seek(fp_tmp, 0, SEEK_SET);
540: php_stream_seek(dba->fp, 0, SEEK_END);
541: if (!php_stream_copy_to_stream(fp_tmp, dba->fp, PHP_STREAM_COPY_ALL)) {
542: php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Could not copy from temporary stream - ini file truncated");
543: ret = FAILURE;
544: }
545: }
546: }
547:
548: if (ini_tmp) {
549: php_stream_close(ini_tmp->fp);
550: inifile_free(ini_tmp, 0);
551: }
552: if (fp_tmp) {
553: php_stream_close(fp_tmp);
554: }
555: php_stream_flush(dba->fp);
556: php_stream_seek(dba->fp, 0, SEEK_SET);
557:
558: return ret;
559: }
560: /* }}} */
561:
562: /* {{{ inifile_delete
563: */
564: int inifile_delete(inifile *dba, const key_type *key TSRMLS_DC)
565: {
566: return inifile_delete_replace_append(dba, key, NULL, 0 TSRMLS_CC);
567: }
568: /* }}} */
569:
570: /* {{{ inifile_relace
571: */
572: int inifile_replace(inifile *dba, const key_type *key, const val_type *value TSRMLS_DC)
573: {
574: return inifile_delete_replace_append(dba, key, value, 0 TSRMLS_CC);
575: }
576: /* }}} */
577:
578: /* {{{ inifile_append
579: */
580: int inifile_append(inifile *dba, const key_type *key, const val_type *value TSRMLS_DC)
581: {
582: return inifile_delete_replace_append(dba, key, value, 1 TSRMLS_CC);
583: }
584: /* }}} */
585:
586: /*
587: * Local variables:
588: * tab-width: 4
589: * c-basic-offset: 4
590: * End:
591: * vim600: sw=4 ts=4 fdm=marker
592: * vim<600: sw=4 ts=4
593: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>