Annotation of embedaddon/php/ext/dba/libinifile/inifile.c, revision 1.1.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>