1 /*
2 * file ini_file.h - parsing ini-file into a database
3 *
4 * $Id: ini_file.c,v 1.25 2006/04/11 16:44:00 fzago Exp $
5 *
6 * Program XBLAST
7 * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net)
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2; or (at your option)
12 * any later version
13 *
14 * This program is distributed in the hope that it will be entertaining,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17 * Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "xblast.h"
25
26 /*
27 * local types
28 */
29 /* single entry */
30 struct _db_entry
31 {
32 XBAtom atom; /* atom for entry key */
33 char *value; /* string value of key */
34 struct _db_entry *next; /* next entry for this section */
35 };
36
37 /* one complete section */
38 struct _db_section
39 {
40 XBAtom atom; /* name atom of section -> [...] line in file */
41 XBBool changed; /* flag for unsaved changes in section */
42 DBEntry *entry; /* list of entries */
43 struct _db_section *next; /* next section in this database */
44 };
45
46 /* one database */
47 struct _db_root
48 {
49 DBType type; /* in which list is database stored -> dirname */
50 XBAtom atom; /* name atom for database -> filename */
51 XBBool changed; /* flag for changes */
52 DBSection *section; /* pointer to sections */
53 DBRoot *next; /* next database of this type */
54 };
55
56 /*
57 * local variables
58 */
59
60 /* database lists */
61 static DBRoot *db_list[NUM_DT] = {
62 NULL, NULL, NULL, NULL,
63 };
64
65 /* paths for database file */
66 static const char *db_path[NUM_DT] = {
67 "config",
68 GAME_DATADIR "/level",
69 "demo",
70 "central",
71 };
72
73 /* file extensions */
74 static const char *db_ext[NUM_DT] = {
75 "cfg",
76 "xal",
77 "dem",
78 "dat",
79 };
80
81 /*******************
82 * local functions *
83 *******************/
84
85 /* free memory of defined structures */
86
87 /*
88 * free memory of an entry
89 */
90 static void
FreeEntry(DBEntry * entry)91 FreeEntry (DBEntry * entry)
92 {
93 assert (entry != NULL);
94 assert (entry->value != NULL);
95 free (entry->value);
96 free (entry);
97 } /* FreeEntry */
98
99 /*
100 * free memory of a section and all its entries
101 */
102 static void
FreeSection(DBSection * section)103 FreeSection (DBSection * section)
104 {
105 DBEntry *entry;
106 DBEntry *nextEntry;
107
108 assert (NULL != section);
109 /* clear all entries */
110 for (entry = section->entry; entry != NULL; entry = nextEntry) {
111 nextEntry = entry->next;
112 FreeEntry (entry);
113 }
114 free (section);
115 } /* FreeSection */
116
117 /*
118 * free memory of a given database
119 */
120 static void
FreeRoot(DBRoot * db)121 FreeRoot (DBRoot * db)
122 {
123 DBSection *section;
124 DBSection *nextSection;
125
126 assert (db != NULL);
127 /* clear all sections */
128 for (section = db->section; section != NULL; section = nextSection) {
129 nextSection = section->next;
130 FreeSection (section);
131 }
132 free (db);
133 } /* DB_Delete */
134
135 /* parse input lines */
136
137 /*
138 * parse section name from given line
139 */
140 static const char *
ParseSection(const char * line)141 ParseSection (const char *line)
142 {
143 const char *left;
144 char *dst;
145 static char result[256];
146
147 /* check left side */
148 for (left = line; *left != 0 && isspace ((unsigned char)*left); left++)
149 continue;
150 if (*left != '[') {
151 return NULL;
152 }
153 left++;
154 dst = result;
155 while (*left != ']') {
156 if ('\0' == *left) {
157 /* premature end of line */
158 return NULL;
159 }
160 *dst = *left;
161 dst++;
162 left++;
163 }
164 *dst = '\0';
165 return result;
166 } /* ParseSection */
167
168 /*
169 * parse key part from a given line
170 */
171 static const char *
ParseEntryName(const char * line)172 ParseEntryName (const char *line)
173 {
174 const char *src;
175 char *dst;
176 static char result[256];
177
178 src = line;
179 dst = result;
180 while (*src != '=') {
181 if ('\0' == *src) {
182 /* premature end of line */
183 return NULL;
184 }
185 *dst = *src;
186 dst++;
187 src++;
188 }
189 *dst = '\0';
190 return result;
191 } /* ParseEntryName */
192
193 /*
194 * return pointer to a named entry in given section
195 */
196 static const DBEntry *
GetEntry(const DBSection * section,XBAtom atom)197 GetEntry (const DBSection * section, XBAtom atom)
198 {
199 const DBEntry *ptr;
200 assert (section != NULL);
201 /* find entry */
202 for (ptr = section->entry; ptr != NULL; ptr = ptr->next) {
203 if (ptr->atom == atom) {
204 return ptr;
205 }
206 }
207 return NULL;
208 } /* GetEntry */
209
210 /*
211 * return if character is eol
212 * External level files could also come from windows environment,
213 * where '\r' can be considered an EOL char for ini-file purposes.
214 */
215 static int
XB_iniEOL(char c)216 XB_iniEOL (char c)
217 {
218 switch (c) {
219 case '\n': /* FALLTHRU */
220 case '\r':
221 return 1;
222 default:
223 return 0;
224 }
225 } /* XB_iniEOL */
226
227 /*
228 * parse the entry value part of a line
229 */
230 static const char *
ParseEntryValue(const char * line)231 ParseEntryValue (const char *line)
232 {
233 const char *src;
234 char *dst;
235 static char result[256];
236 dst = result;
237 src = strchr (line, '=');
238 if (NULL == src) {
239 return NULL;
240 }
241 src++;
242 while (isspace ((unsigned char)*src)) {
243 src++;
244 }
245 while ((!XB_iniEOL (*src)) && (*src != '\0')) {
246 *dst = *src;
247 dst++;
248 src++;
249 }
250
251 while (isspace ((unsigned char)*(dst - 1))) {
252 --dst;
253 }
254 *dst = 0;
255 return result;
256 } /* ParseEntryValue */
257
258 /* file operations */
259
260 /*
261 * open file corresponding to given database
262 */
263 static FILE *
OpenDBFile(const DBRoot * db,const char * mode)264 OpenDBFile (const DBRoot * db, const char *mode)
265 {
266 char db_name[320];
267 assert (NULL != db);
268 assert (NULL != mode);
269 /* add index for demos */
270 sprintf (db_name, "%s", GUI_AtomToString (db->atom));
271 return FileOpen (db_path[db->type], db_name, db_ext[db->type], mode);
272 } /* OpenDBFile */
273
274 /*
275 * check if file directory database must be updated
276 */
277 static XBBool
CheckInsertFile(const DBRoot * db,const char * name,XBAtom timeAtom,time_t mtime)278 CheckInsertFile (const DBRoot * db, const char *name, XBAtom timeAtom, time_t mtime)
279 {
280 const DBSection *section;
281 time_t ltime;
282 /* does file exist as section name in database */
283 section = DB_GetSection (db, GUI_StringToAtom (name));
284 if (NULL == section) {
285 return XBFalse;
286 }
287 /* has the file been modified */
288 if (!DB_GetEntryTime (section, timeAtom, <ime) || ltime != mtime) {
289 return XBFalse;
290 }
291 /* file entry exists and is up-to-date */
292 return XBTrue;
293 } /* CheckInsertFile */
294
295 /*
296 * insert file into database
297 */
298 static XBBool
InsertFile(DBRoot * db,DBType type,const char * name,XBAtom timeAtom,time_t mtime,XBAtom fromAtom,DBLoadDirFunc func)299 InsertFile (DBRoot * db, DBType type, const char *name, XBAtom timeAtom, time_t mtime,
300 XBAtom fromAtom, DBLoadDirFunc func)
301 {
302 DBRoot *dbFile = NULL;
303 DBSection *toSection = NULL;
304 const DBSection *fromSection = NULL;
305 const DBEntry *entry = NULL;
306 XBAtom toAtom = GUI_StringToAtom (name);
307 /* try to load file type/name.ext into a temp database */
308 dbFile = DB_Create (type, toAtom);
309 assert (dbFile != NULL);
310 if (!DB_Load (dbFile)) {
311 goto Error;
312 }
313 /* try to find given section */
314 fromSection = DB_GetSection (dbFile, fromAtom);
315 if (NULL == fromSection) {
316 goto Error;
317 }
318 /* create new section for file data */
319 toSection = DB_CreateSection (db, toAtom);
320 assert (NULL != toSection);
321 /* create entry for file modification time */
322 DB_CreateEntryTime (toSection, timeAtom, mtime);
323 /* now copy section */
324 for (entry = fromSection->entry; NULL != entry; entry = entry->next) {
325 (void)DB_CreateEntryString (toSection, entry->atom, entry->value);
326 }
327 /* call user function if any */
328 if (NULL != func) {
329 (*func) (toSection);
330 }
331 /* clean up */
332 DB_Delete (dbFile);
333 return XBTrue;
334
335 Error:
336 if (NULL != dbFile) {
337 DB_Delete (dbFile);
338 }
339 if (NULL != toSection) {
340 DB_DeleteSection (db, toAtom);
341 }
342 return XBFalse;
343 } /* CheckInsertFile */
344
345 /*
346 * create named section in given database and mark it changed
347 */
348 static void
UpdateFile(DBRoot * db,const char * name)349 UpdateFile (DBRoot * db, const char *name)
350 {
351 DBSection *section = DB_CreateSection (db, GUI_StringToAtom (name));
352 assert (NULL != section);
353 section->changed = XBTrue;
354 } /* UpdateFile */
355
356 /*
357 * delete any unchanged sections
358 */
359 static XBBool
DeleteUnchangedSections(DBRoot * db)360 DeleteUnchangedSections (DBRoot * db)
361 {
362 DBSection *ptr;
363 DBSection *next;
364 XBBool deleted = XBFalse;
365 assert (NULL != db);
366 /* delete first section, while unchanged */
367 while (NULL != db->section && !db->section->changed) {
368 next = db->section->next;
369 FreeSection (db->section);
370 db->section = next;
371 deleted = XBTrue;
372 #ifdef DEBUG
373 putchar ('-');
374 #endif
375 }
376 /* now proceed with the rest */
377 if (NULL != db->section) {
378 for (ptr = db->section; ptr != NULL && ptr->next != NULL; ptr = ptr->next) {
379 while (NULL != ptr->next && !ptr->next->changed) {
380 next = ptr->next->next;
381 FreeSection (ptr->next);
382 ptr->next = next;
383 deleted = XBTrue;
384 #ifdef DEBUG
385 putchar ('-');
386 #endif
387 }
388 }
389 }
390 #ifdef DEBUG
391 fflush (stdout);
392 #endif
393 return deleted;
394 } /* DeleteUnchangedSections */
395
396 /*************************
397 * get/set database info *
398 *************************/
399
400 /*
401 * create a new database of given type and name
402 */
403 DBRoot *
DB_Create(DBType type,XBAtom atom)404 DB_Create (DBType type, XBAtom atom)
405 {
406 DBRoot *db;
407 assert (ATOM_INVALID != atom);
408 /* create new database */
409 db = calloc (1, sizeof (DBRoot));
410 assert (NULL != db);
411 db->atom = atom;
412 db->type = type;
413 db->changed = XBTrue;
414 /* store in list */
415 db->next = db_list[type];
416 db_list[type] = db;
417 /* that�s all */
418 return db;
419 } /* DB_Create */
420
421 /*
422 * return pointer of an existing database
423 */
424 const DBRoot *
DB_Get(DBType type,XBAtom atom)425 DB_Get (DBType type, XBAtom atom)
426 {
427 const DBRoot *db;
428 assert (ATOM_INVALID != atom);
429 for (db = db_list[type]; db != NULL; db = db->next) {
430 if (db->atom == atom) {
431 return db;
432 }
433 }
434 return NULL;
435 } /* DB_Get */
436
437 /*
438 * get name atom of database (filename)
439 */
440 XBAtom
DB_Atom(const DBRoot * db)441 DB_Atom (const DBRoot * db)
442 {
443 assert (NULL != db);
444 return db->atom;
445 } /* DB_Changed */
446
447 /*
448 * check if database has changed
449 */
450 XBBool
DB_Changed(const DBRoot * db)451 DB_Changed (const DBRoot * db)
452 {
453 const DBSection *section;
454 if (db->changed) {
455 return XBTrue;
456 }
457 for (section = db->section; section != NULL; section = section->next) {
458 if (section->changed) {
459 return XBTrue;
460 }
461 }
462 return XBFalse;
463 } /* DB_Changed */
464
465 /*
466 * set name atom of given database (filename)
467 */
468 void
DB_SetAtom(DBRoot * db,XBAtom atom)469 DB_SetAtom (DBRoot * db, XBAtom atom)
470 {
471 db->atom = atom;
472 db->changed = XBTrue;
473 } /* DB_SetAtom */
474
475 /*
476 * mark given database as unchanged
477 */
478 static void
MarkUnchanged(DBRoot * db)479 MarkUnchanged (DBRoot * db)
480 {
481 DBSection *section;
482 /* mark all as unchanged */
483 db->changed = XBFalse;
484 for (section = db->section; section != NULL; section = section->next) {
485 section->changed = XBFalse;
486 }
487 } /* MarkUnchanged */
488
489 /*
490 * delete a given database
491 */
492 void
DB_Delete(DBRoot * db)493 DB_Delete (DBRoot * db)
494 {
495 DBRoot *ptr;
496 assert (db_list[db->type] != NULL);
497 /* check if first element in list */
498 if (db_list[db->type] == db) {
499 db_list[db->type] = db_list[db->type]->next;
500 }
501 else {
502 for (ptr = db_list[db->type]; ptr->next != NULL; ptr = ptr->next) {
503 if (db == ptr->next) {
504 ptr->next = db->next;
505 break;
506 }
507 }
508 }
509 FreeRoot (db);
510 } /* DB_Delete */
511
512 /************************
513 * get/set section info *
514 ************************/
515
516 /*
517 * create a new section in a given database
518 */
519 DBSection *
DB_CreateSection(DBRoot * db,XBAtom atom)520 DB_CreateSection (DBRoot * db, XBAtom atom)
521 {
522 DBSection *section;
523
524 /* create new data structure */
525 assert (NULL != db);
526 assert (ATOM_INVALID != atom);
527 /* try to find section first */
528 for (section = db->section; section != NULL; section = section->next) {
529 if (section->atom == atom) {
530 return section;
531 }
532 }
533 /* create new section */
534 db->changed = XBTrue;
535 section = calloc (1, sizeof (DBSection));
536 assert (NULL != section);
537 section->atom = atom;
538 section->changed = XBTrue;
539 /* store in database */
540 section->next = db->section;
541 db->section = section;
542 /* that�s all */
543 return section;
544 } /* DB_CreateSection */
545
546 /*
547 * return pointer to a named section in given database
548 */
549 DBSection *
DB_GetSection(const DBRoot * db,XBAtom atom)550 DB_GetSection (const DBRoot * db, XBAtom atom)
551 {
552 DBSection *ptr;
553
554 assert (NULL != db);
555 assert (ATOM_INVALID != atom);
556 /* find section */
557 for (ptr = db->section; ptr != NULL; ptr = ptr->next) {
558 if (ptr->atom == atom) {
559 return ptr;
560 }
561 }
562 return NULL;
563 } /* DB_GetSection */
564
565 /*
566 * get section atom
567 */
568 XBAtom
DB_SectionAtom(const DBSection * section)569 DB_SectionAtom (const DBSection * section)
570 {
571 assert (section != NULL);
572 return section->atom;
573 } /* DB_SectionAtom */
574
575 /*
576 * get the name of the nth section in given database
577 */
578 XBAtom
DB_IndexSection(const DBRoot * db,int index)579 DB_IndexSection (const DBRoot * db, int index)
580 {
581 const DBSection *ptr;
582 int num = 0;
583 assert (NULL != db);
584 /* find section */
585 for (ptr = db->section; ptr != NULL; ptr = ptr->next) {
586 if (num == index) {
587 return ptr->atom;
588 }
589 num++;
590 }
591 return ATOM_INVALID;
592 } /* DB_IndexSection */
593
594 /*
595 * number of sections in a given database
596 */
597 int
DB_NumSections(const DBRoot * db)598 DB_NumSections (const DBRoot * db)
599 {
600 const DBSection *ptr;
601 int num = 0;
602 assert (NULL != db);
603 /* find section */
604 for (ptr = db->section; ptr != NULL; ptr = ptr->next) {
605 num++;
606 }
607 return num;
608 } /* DB_NumSections */
609
610 /*
611 * delete a section in a given database
612 */
613 void
DB_DeleteSection(DBRoot * db,XBAtom atom)614 DB_DeleteSection (DBRoot * db, XBAtom atom)
615 {
616 DBSection *save, *ptr;
617 assert (db != NULL);
618 db->changed = XBTrue;
619 /* do we have any sections ? */
620 if (NULL == db->section) {
621 return;
622 }
623 assert (ATOM_INVALID != atom);
624 /* it is the first section ? */
625 if (db->section->atom == atom) {
626 save = db->section;
627 db->section = db->section->next;
628 FreeSection (save);
629 }
630 else {
631 for (ptr = db->section; ptr->next != NULL; ptr = ptr->next) {
632 if (ptr->next->atom == atom) {
633 save = ptr->next;
634 ptr->next = ptr->next->next;
635 FreeSection (save);
636 break;
637 }
638 }
639 }
640 } /* DB_DeleteSection */
641
642 /*
643 * delete all sections in a given database
644 */
645 void
DB_DeleteAll(DBRoot * db)646 DB_DeleteAll (DBRoot * db)
647 {
648 DBSection *save;
649 assert (db != NULL);
650 db->changed = XBTrue;
651 /* do we have any sections ? */
652 if (NULL == db->section) {
653 return;
654 }
655 while (db->section != NULL) {
656 save = db->section;
657 db->section = db->section->next;
658 FreeSection (save);
659 }
660 } /* DB_DeleteAll */
661
662 /*************************
663 * get entry information *
664 *************************/
665
666 /*
667 * create a string entry in a given section
668 */
669 XBBool
DB_CreateEntryString(DBSection * section,XBAtom atom,const char * value)670 DB_CreateEntryString (DBSection * section, XBAtom atom, const char *value)
671 {
672 DBEntry *entry;
673
674 assert (ATOM_INVALID != atom);
675 assert (NULL != section);
676 /* mark as changed */
677 section->changed = XBTrue;
678 /* try to find entry first */
679 for (entry = section->entry; entry != NULL; entry = entry->next) {
680 if (entry->atom == atom) {
681 if (NULL != entry->value) {
682 free (entry->value);
683 }
684 entry->value = DupString (value);
685 assert (NULL != entry->value);
686 return XBTrue;
687 }
688 }
689 /* create new data strutcure */
690 entry = calloc (1, sizeof (DBEntry));
691 assert (NULL != entry);
692 entry->atom = atom;
693 entry->value = DupString (value);
694 assert (NULL != entry->value);
695 /* store in database */
696 entry->next = section->entry;
697 section->entry = entry;
698 /* that's all */
699 return XBTrue;
700 } /* DB_CreateEntryString */
701
702 /*
703 * create an atom entry in a given section
704 */
705 XBBool
DB_CreateEntryAtom(DBSection * section,XBAtom atom,XBAtom value)706 DB_CreateEntryAtom (DBSection * section, XBAtom atom, XBAtom value)
707 {
708 return DB_CreateEntryString (section, atom, GUI_AtomToString (value));
709 } /* DB_CreateEntryAtom */
710
711 /*
712 * create an integer entry in a given section
713 */
714 XBBool
DB_CreateEntryInt(DBSection * section,XBAtom atom,int value)715 DB_CreateEntryInt (DBSection * section, XBAtom atom, int value)
716 {
717 char tmp[32];
718 sprintf (tmp, "%d", value);
719 return DB_CreateEntryString (section, atom, tmp);
720 } /* DB_CreateEntryInt */
721
722 /*
723 * create a game result entry in a given section
724 */
725 XBBool
DB_CreateEntryGameResult(DBSection * section,XBAtom atom,int score)726 DB_CreateEntryGameResult (DBSection * section, XBAtom atom, int score)
727 {
728 char tmp[32];
729 sprintf (tmp, "%d", score);
730 return DB_CreateEntryString (section, atom, tmp);
731 } /* DB_CreateEntry */
732
733 /*
734 * create a player rating entry in a given section
735 */
736 XBBool
DB_CreateEntryPlayerRating(DBSection * section,XBAtom atom,int PID,float rating)737 DB_CreateEntryPlayerRating (DBSection * section, XBAtom atom, int PID, float rating)
738 {
739 char tmp[32];
740 sprintf (tmp, "%d %f", PID, rating);
741 return DB_CreateEntryString (section, atom, tmp);
742 } /* DB_CreateEntryPlayerRating */
743
744 /*
745 * create a bool entry in given section
746 */
747 XBBool
DB_CreateEntryBool(DBSection * section,XBAtom atom,XBBool value)748 DB_CreateEntryBool (DBSection * section, XBAtom atom, XBBool value)
749 {
750 return DB_CreateEntryString (section, atom, value ? "true" : "false");
751 } /* DB_CreateEntryBool */
752
753 /*
754 * create a color entry in given section
755 */
756 XBBool
DB_CreateEntryColor(DBSection * section,XBAtom atom,XBColor color)757 DB_CreateEntryColor (DBSection * section, XBAtom atom, XBColor color)
758 {
759 char tmp[32];
760 sprintf (tmp, "#%02x%02x%02x", GET_RED (color) << 3, GET_GREEN (color) << 3,
761 GET_BLUE (color) << 3);
762 return DB_CreateEntryString (section, atom, tmp);
763 } /* DB_CreateEntryColor */
764
765 /*
766 * create a time entry in given section
767 */
768 XBBool
DB_CreateEntryTime(DBSection * section,XBAtom atom,time_t value)769 DB_CreateEntryTime (DBSection * section, XBAtom atom, time_t value)
770 {
771 char tmp[64];
772 size_t len;
773 len = sprintf (tmp, "%lu ; %s", value, ctime (&value));
774 /* cut trailing newline */
775 tmp[len - 1] = 0;
776 return DB_CreateEntryString (section, atom, tmp);
777 } /* DB_CreateEntryTime */
778
779 /*
780 * create a float entry in given section
781 */
782 XBBool
DB_CreateEntryFloat(DBSection * section,XBAtom atom,double value)783 DB_CreateEntryFloat (DBSection * section, XBAtom atom, double value)
784 {
785 char tmp[32];
786 sprintf (tmp, "%f", value);
787 return DB_CreateEntryString (section, atom, tmp);
788 } /* DB_CreateEntryFloat */
789
790 /*
791 * create a position entry in given section
792 */
793 XBBool
DB_CreateEntryPos(DBSection * section,XBAtom atom,BMPosition * pValue)794 DB_CreateEntryPos (DBSection * section, XBAtom atom, BMPosition * pValue)
795 {
796 char tmp[64];
797 sprintf (tmp, "%d %d", pValue->x, pValue->y);
798 return DB_CreateEntryString (section, atom, tmp);
799 } /* DB_CreateEntryPos */
800
801 /*
802 * create entry in given section, defined by given key=value line
803 */
804 XBBool
DB_ParseEntry(DBSection * section,const char * line)805 DB_ParseEntry (DBSection * section, const char *line)
806 {
807 const char *entryName;
808 assert (NULL != section);
809 assert (NULL != line);
810 /* parse entry */
811 if (NULL == (entryName = ParseEntryName (line))) {
812 return XBFalse;
813 }
814 return DB_CreateEntryString (section, GUI_StringToAtom (entryName), ParseEntryValue (line));
815 } /* DB_ParseEntry */
816
817 /*
818 * copy name section/entry from given section to given database
819 */
820 XBBool
DB_CopyEntry(DBRoot * db,const DBSection * section,const XBAtom atom)821 DB_CopyEntry (DBRoot * db, const DBSection * section, const XBAtom atom)
822 {
823 DBSection *sec;
824 const DBEntry *ptr;
825 assert (NULL != db);
826 assert (NULL != section);
827 assert (NULL != db);
828 /* try to create section in database */
829 sec = DB_CreateSection (db, section->atom);
830 if (NULL == sec) {
831 return XBFalse;
832 }
833 /* try to get entry */
834 ptr = GetEntry (section, atom);
835 if (NULL == ptr) {
836 return XBFalse;
837 }
838 /* now duplicate entry */
839 return DB_CreateEntryString (sec, atom, ptr->value);
840 } /* DB_CopyEntry */
841
842 /*
843 * get the name of the nth entry in given section
844 */
845 XBAtom
DB_IndexEntry(const DBSection * section,int index)846 DB_IndexEntry (const DBSection * section, int index)
847 {
848 const DBEntry *ptr;
849 int num = 0;
850 assert (NULL != section);
851 /* find section */
852 for (ptr = section->entry; ptr != NULL; ptr = ptr->next) {
853 if (num == index) {
854 return ptr->atom;
855 }
856 num++;
857 }
858 return ATOM_INVALID;
859 } /* DB_IndexSection */
860
861 /*
862 * number of entries in given database
863 */
864 int
DB_NumAllEntries(const DBRoot * db)865 DB_NumAllEntries (const DBRoot * db)
866 {
867 const DBSection *sec;
868 int num = 0;
869 assert (NULL != db);
870 for (sec = db->section; sec != NULL; sec = sec->next) {
871 num += DB_NumEntries (sec);
872 }
873 return num;
874 } /* DB_NumAllEntries */
875
876 /*
877 * number of entries in given section
878 */
879 int
DB_NumEntries(const DBSection * section)880 DB_NumEntries (const DBSection * section)
881 {
882 const DBEntry *ptr;
883 int num = 0;
884 assert (NULL != section);
885 /* find section */
886 for (ptr = section->entry; ptr != NULL; ptr = ptr->next) {
887 num++;
888 }
889 return num;
890 } /* DB_NumEntries */
891
892 /*
893 * get string value for named entry in given section
894 */
895 XBBool
DB_GetEntryString(const DBSection * section,XBAtom atom,const char ** pValue)896 DB_GetEntryString (const DBSection * section, XBAtom atom, const char **pValue)
897 {
898 const DBEntry *entry;
899 assert (pValue != NULL);
900 entry = GetEntry (section, atom);
901 if (NULL == entry) {
902 return XBFalse;
903 }
904 assert (entry->value != NULL);
905 *pValue = entry->value;
906 return XBTrue;
907 } /* DB_GetEntryString */
908
909 /*
910 * get atom value for named entry in given section
911 */
912 XBBool
DB_GetEntryAtom(const DBSection * section,XBAtom atom,XBAtom * pValue)913 DB_GetEntryAtom (const DBSection * section, XBAtom atom, XBAtom * pValue)
914 {
915 const DBEntry *entry;
916 assert (pValue != NULL);
917 entry = GetEntry (section, atom);
918 if (NULL == entry) {
919 return XBFalse;
920 }
921 assert (NULL != entry->value);
922 *pValue = GUI_StringToAtom (entry->value);
923 return (ATOM_INVALID != *pValue);
924 } /* DB_GetEntryAtom */
925
926 /*
927 * get int value for named entry in given section
928 */
929 XBBool
DB_GetEntryInt(const DBSection * section,XBAtom atom,int * pValue)930 DB_GetEntryInt (const DBSection * section, XBAtom atom, int *pValue)
931 {
932 const DBEntry *entry;
933 assert (pValue != NULL);
934 entry = GetEntry (section, atom);
935 if (NULL == entry) {
936 return XBFalse;
937 }
938 assert (NULL != entry->value);
939 return (1 == sscanf (entry->value, "%d", pValue)) ? XBTrue : XBFalse;
940 } /* DB_GetEntryInt */
941
942 /*
943 * get bool value for named entry in given section
944 */
945 XBBool
DB_GetEntryBool(const DBSection * section,XBAtom atom,XBBool * pValue)946 DB_GetEntryBool (const DBSection * section, XBAtom atom, XBBool * pValue)
947 {
948 const DBEntry *entry;
949 assert (pValue != NULL);
950 entry = GetEntry (section, atom);
951 if (NULL == entry) {
952 return XBFalse;
953 }
954 assert (NULL != entry->value);
955 if (0 == strcmp (entry->value, "true")) {
956 *pValue = XBTrue;
957 }
958 else if (0 == strcmp (entry->value, "false")) {
959 *pValue = XBFalse;
960 }
961 else {
962 return XBFalse;
963 }
964 return XBTrue;
965 } /* DB_GetEntryBool */
966
967 /*
968 * get color value for named entry in given section
969 */
970 XBBool
DB_GetEntryColor(const DBSection * section,XBAtom atom,XBColor * pValue)971 DB_GetEntryColor (const DBSection * section, XBAtom atom, XBColor * pValue)
972 {
973 const DBEntry *entry;
974 unsigned red, green, blue;
975 char hash;
976 assert (pValue != NULL);
977 entry = GetEntry (section, atom);
978 if (NULL == entry) {
979 return XBFalse;
980 }
981 assert (NULL != entry->value);
982 if (4 != sscanf (entry->value, "%c%2X%2X%2X", &hash, &red, &green, &blue)) {
983 return XBFalse;
984 }
985 if (hash != '#') {
986 return XBFalse;
987 }
988 *pValue = SET_COLOR (red >> 3, green >> 3, blue >> 3);
989 return XBTrue;
990 } /* DB_GetEntryColor */
991
992 /*
993 * get time value for named entry in given sectiong
994 */
995 XBBool
DB_GetEntryTime(const DBSection * section,XBAtom atom,time_t * pValue)996 DB_GetEntryTime (const DBSection * section, XBAtom atom, time_t * pValue)
997 {
998 const DBEntry *entry;
999 assert (pValue != NULL);
1000 entry = GetEntry (section, atom);
1001 if (NULL == entry) {
1002 return XBFalse;
1003 }
1004 assert (NULL != entry->value);
1005 return (1 == sscanf (entry->value, "%lu", pValue)) ? XBTrue : XBFalse;
1006 } /* DB_GetEntryTime */
1007
1008 /*
1009 * get float value for named entry in given section
1010 */
1011 XBBool
DB_GetEntryFloat(const DBSection * section,XBAtom atom,double * pValue)1012 DB_GetEntryFloat (const DBSection * section, XBAtom atom, double *pValue)
1013 {
1014 const DBEntry *entry;
1015 assert (pValue != NULL);
1016 entry = GetEntry (section, atom);
1017 if (NULL == entry) {
1018 return XBFalse;
1019 }
1020 assert (NULL != entry->value);
1021 return (1 == sscanf (entry->value, "%lf", pValue)) ? XBTrue : XBFalse;
1022 } /* DB_GetEntryFloat */
1023
1024 /*
1025 * get position value for named entry in given section
1026 */
1027 XBBool
DB_GetEntryPos(const DBSection * section,XBAtom atom,BMPosition * pValue)1028 DB_GetEntryPos (const DBSection * section, XBAtom atom, BMPosition * pValue)
1029 {
1030 const DBEntry *entry;
1031 assert (pValue != NULL);
1032 entry = GetEntry (section, atom);
1033 if (NULL == entry) {
1034 return XBFalse;
1035 }
1036 assert (NULL != entry->value);
1037 return (2 == sscanf (entry->value, "%hd%hd", &pValue->x, &pValue->y)) ? XBTrue : XBFalse;
1038 } /* DB_GetEntryPos */
1039
1040 /*
1041 * get string value for named entry in given section and convert to integer
1042 */
1043 DBConversionResult
DB_ConvertEntryInt(const DBSection * section,XBAtom atom,int * pValue,const DBToInt * convTable)1044 DB_ConvertEntryInt (const DBSection * section, XBAtom atom, int *pValue, const DBToInt * convTable)
1045 {
1046 const DBEntry *entry;
1047 const DBToInt *ptr;
1048 int cmp;
1049 if (NULL == (entry = GetEntry (section, atom))) {
1050 return DCR_NoSuchEntry;
1051 }
1052 assert (convTable != NULL);
1053 for (ptr = convTable; ptr->key != NULL; ptr++) {
1054 cmp = strcmp (ptr->key, entry->value);
1055 if (0 == cmp) {
1056 /* match */
1057 assert (pValue != NULL);
1058 *pValue = ptr->value;
1059 return DCR_Okay;
1060 }
1061 else if (cmp > 0) {
1062 return DCR_Failure;
1063 }
1064 }
1065 return DCR_Failure;
1066 } /* DB_ConvertEntryInt */
1067
1068 /*
1069 * get string value for named entry in given section and convert to a generic pointer
1070 */
1071 DBConversionResult
DB_ConvertEntryData(const DBSection * section,XBAtom atom,void ** pValue,const DBToData * convTable)1072 DB_ConvertEntryData (const DBSection * section, XBAtom atom, void **pValue,
1073 const DBToData * convTable)
1074 {
1075 const DBEntry *entry;
1076 const DBToData *ptr;
1077 int cmp;
1078 if (NULL == (entry = GetEntry (section, atom))) {
1079 return DCR_NoSuchEntry;
1080 }
1081 assert (convTable != NULL);
1082 for (ptr = convTable; ptr->key != NULL; ptr++) {
1083 cmp = strcmp (ptr->key, entry->value);
1084 if (0 == cmp) {
1085 /* match */
1086 assert (pValue != NULL);
1087 *pValue = ptr->value;
1088 return DCR_Okay;
1089 }
1090 else if (cmp > 0) {
1091 return DCR_Failure;
1092 }
1093 }
1094 return DCR_Failure;
1095 } /* DB_ConvertEntryData */
1096
1097 /*
1098 * get string value for named entry in given section and convert to an entry flag number
1099 */
1100 DBConversionResult
DB_ConvertEntryFlags(const DBSection * section,XBAtom atom,unsigned * pValue,const DBToInt * convTable)1101 DB_ConvertEntryFlags (const DBSection * section, XBAtom atom, unsigned *pValue,
1102 const DBToInt * convTable)
1103 {
1104 const DBEntry *entry;
1105 const DBToInt *ptr;
1106 int cmp;
1107 int i, argc;
1108 char **argv;
1109 if (NULL == (entry = GetEntry (section, atom))) {
1110 return DCR_NoSuchEntry;
1111 }
1112 assert (convTable != NULL);
1113 assert (pValue != NULL);
1114 *pValue = 0;
1115 /* split value string */
1116 argv = SplitString (entry->value, &argc);
1117 assert (NULL != argv);
1118 /* parse list */
1119 for (i = 0; i < argc; i++) {
1120 for (ptr = convTable; ptr->key != NULL; ptr++) {
1121 cmp = strcmp (ptr->key, argv[i]);
1122 if (0 == cmp) {
1123 /* match */
1124 *pValue |= ptr->value;
1125 break;
1126 }
1127 else if (cmp > 0) {
1128 free ((char *)argv);
1129 return DCR_Failure;
1130 }
1131 }
1132 }
1133 free ((char *)argv);
1134 return DCR_Okay;
1135 } /* DB_ConvertEntryFlag */
1136
1137 /*
1138 * create a string line for n-th entry in given section, return length
1139 */
1140 size_t
DB_PrintEntry(char * buffer,const DBSection * section,int index)1141 DB_PrintEntry (char *buffer, const DBSection * section, int index)
1142 {
1143 const DBEntry *ptr;
1144 int num = 0;
1145 assert (NULL != buffer);
1146 assert (NULL != section);
1147 /* find entry */
1148 for (ptr = section->entry; ptr != NULL; ptr = ptr->next) {
1149 if (num == index) {
1150 return sprintf (buffer, "%s=%s", GUI_AtomToString (ptr->atom), ptr->value);
1151 }
1152 num++;
1153 }
1154 /* entry not found */
1155 buffer[0] = 0;
1156 return 0;
1157 } /* DB_PrintEntry */
1158
1159 /*
1160 * add string to sized buffer, fill with x if added string does not fit
1161 */
1162 static XBBool
AddStringToBuffer(char * buf,size_t len,const char * add)1163 AddStringToBuffer (char *buf, size_t len, const char *add)
1164 {
1165 int i, nul, write;
1166 /* sanity checks */
1167 assert (NULL != buf);
1168 assert (NULL != add);
1169 /* find first null byte */
1170 nul = -1;
1171 for (i = 0; (i < len) && (nul < 0); i++) {
1172 if (0 == *(buf + i)) {
1173 nul = i;
1174 }
1175 }
1176 /* check if buffer is full */
1177 if (nul == len - 1) {
1178 return XBFalse;
1179 }
1180 /* check if there is a null byte */
1181 if (nul < 0) {
1182 Dbg_Out ("Buffer has no null byte!\n");
1183 memset (buf + len - 1, 0, 1);
1184 return XBFalse;
1185 }
1186 /* check if add string fits */
1187 write = strlen (add);
1188 if (nul + write < len) {
1189 memcpy (buf + nul, add, write + 1);
1190 return XBTrue;
1191 }
1192 /* no, does not fit */
1193 memset (buf + nul, (unsigned char)"x", len - nul - 1);
1194 memset (buf + len - 1, 0, 1);
1195 return XBFalse;
1196 } /* AddStringToBuffer */
1197
1198 /*
1199 * return a string for entry in given section
1200 */
1201 char *
DB_SectionEntryString(const DBSection * section,XBAtom atom)1202 DB_SectionEntryString (const DBSection * section, XBAtom atom)
1203 {
1204 static char buffer[100];
1205 const char *tmp;
1206 const DBEntry *ptr;
1207 /* init buffer */
1208 memset (buffer, 0, 1);
1209 /* get section name */
1210 AddStringToBuffer (buffer, sizeof (buffer), "[");
1211 assert (NULL != section);
1212 tmp = GUI_AtomToString (section->atom);
1213 if (NULL == tmp) {
1214 tmp = "???";
1215 }
1216 AddStringToBuffer (buffer, sizeof (buffer), tmp);
1217 AddStringToBuffer (buffer, sizeof (buffer), "] ");
1218 /* get entry name */
1219 tmp = GUI_AtomToString (atom);
1220 if (NULL == tmp) {
1221 tmp = "???";
1222 }
1223 AddStringToBuffer (buffer, sizeof (buffer), tmp);
1224 AddStringToBuffer (buffer, sizeof (buffer), "=");
1225 /* find entry value */
1226 ptr = GetEntry (section, atom);
1227 if (NULL == ptr) {
1228 tmp = "<none>";
1229 }
1230 else {
1231 tmp = ptr->value;
1232 }
1233 AddStringToBuffer (buffer, sizeof (buffer), tmp);
1234 return buffer;
1235 } /* DB_PrintEntry */
1236
1237 /*
1238 * delete an entry from section
1239 */
1240 void
DB_DeleteEntry(DBSection * section,XBAtom atom)1241 DB_DeleteEntry (DBSection * section, XBAtom atom)
1242 {
1243 DBEntry *save, *ptr;
1244 assert (section != NULL);
1245 section->changed = XBTrue;
1246 /* do we have any entries ? */
1247 if (NULL == section->entry) {
1248 return;
1249 }
1250 assert (ATOM_INVALID != atom);
1251 /* it is the first entry ? */
1252 if (section->entry->atom == atom) {
1253 save = section->entry;
1254 section->entry = section->entry->next;
1255 FreeEntry (save);
1256 }
1257 else {
1258 for (ptr = section->entry; ptr->next != NULL; ptr = ptr->next) {
1259 if (ptr->next->atom == atom) {
1260 save = ptr->next;
1261 ptr->next = ptr->next->next;
1262 FreeEntry (save);
1263 break;
1264 }
1265 }
1266 }
1267 } /* DB_DeleteEntry */
1268
1269 /************************
1270 * conversion functions *
1271 ************************/
1272
1273 /*
1274 * translate an integer value into its conversion string
1275 */
1276 const char *
DB_IntToString(const DBToInt * table,int value)1277 DB_IntToString (const DBToInt * table, int value)
1278 {
1279 const DBToInt *ptr;
1280 assert (NULL != table);
1281 for (ptr = table; ptr->key != NULL; ptr++) {
1282 if (ptr->value == value) {
1283 return ptr->key;
1284 }
1285 }
1286 return NULL;
1287 } /* DB_IntToString */
1288
1289 /*
1290 * translate a data pointer into its conversion string
1291 */
1292 const char *
DB_DataToString(const DBToData * table,void * pValue)1293 DB_DataToString (const DBToData * table, void *pValue)
1294 {
1295 const DBToData *ptr;
1296 assert (NULL != table);
1297 for (ptr = table; ptr->key != NULL; ptr++) {
1298 if (ptr->value == pValue) {
1299 return ptr->key;
1300 }
1301 }
1302 return NULL;
1303 } /* DB_DataToString */
1304
1305 /*******************
1306 * file operations *
1307 *******************/
1308
1309 /*
1310 * load given database from file
1311 */
1312 XBBool
DB_Load(DBRoot * db)1313 DB_Load (DBRoot * db)
1314 {
1315 FILE *fp;
1316 DBSection *section;
1317 char line[256];
1318 const char *sectionName;
1319 const char *entryName;
1320 assert (db != NULL);
1321 assert (ATOM_INVALID != db->atom);
1322 /* try to open file for reading */
1323 if (NULL == (fp = OpenDBFile (db, "r"))) {
1324 return XBFalse;
1325 }
1326 /* parse it */
1327 section = NULL;
1328 while (NULL != fgets (line, sizeof (line), fp)) {
1329 if (NULL != (sectionName = ParseSection (line))) {
1330 section = DB_CreateSection (db, GUI_StringToAtom (sectionName));
1331 }
1332 else if (NULL != section && NULL != (entryName = ParseEntryName (line))) {
1333 (void)DB_CreateEntryString (section, GUI_StringToAtom (entryName),
1334 ParseEntryValue (line));
1335 }
1336 }
1337 /* mark all as unchanged */
1338 MarkUnchanged (db);
1339 /* that's all */
1340 fclose (fp);
1341 return XBTrue;
1342 } /* DB_Load */
1343
1344 /*
1345 * store database in file
1346 */
1347 XBBool
DB_Store(DBRoot * db)1348 DB_Store (DBRoot * db)
1349 {
1350 FILE *fp;
1351 const DBSection *section;
1352 const DBEntry *entry;
1353 /* try to open file for writing */
1354 if (NULL == (fp = OpenDBFile (db, "w"))) {
1355 return XBFalse;
1356 }
1357 /* now write it */
1358 for (section = db->section; section != NULL; section = section->next) {
1359 fprintf (fp, "[%s]\n", GUI_AtomToString (section->atom));
1360 for (entry = section->entry; entry != NULL; entry = entry->next) {
1361 fprintf (fp, "%s=%s\n", GUI_AtomToString (entry->atom), entry->value);
1362 }
1363 fprintf (fp, "\n");
1364 }
1365 /* mark all as unchanged */
1366 MarkUnchanged (db);
1367 /* that's all */
1368 fclose (fp);
1369 return XBTrue;
1370 } /* DB_Store */
1371
1372 /*
1373 * append database to file
1374 */
1375 XBBool
DB_Append(DBRoot * db)1376 DB_Append (DBRoot * db)
1377 {
1378 FILE *fp;
1379 const DBSection *section;
1380 const DBEntry *entry;
1381 /* try to open for appending */
1382 if (NULL == (fp = OpenDBFile (db, "a"))) {
1383 return XBFalse;
1384 }
1385 /* now write it */
1386 for (section = db->section; section != NULL; section = section->next) {
1387 fprintf (fp, "[%s]\n", GUI_AtomToString (section->atom));
1388 for (entry = section->entry; entry != NULL; entry = entry->next) {
1389 fprintf (fp, "%s=%s\n", GUI_AtomToString (entry->atom), entry->value);
1390 }
1391 fprintf (fp, "\n");
1392 }
1393 /* mark all as unchanged */
1394 MarkUnchanged (db);
1395 /* that's all */
1396 fclose (fp);
1397 return XBTrue;
1398 } /* DB_Append */
1399
1400 /*
1401 * dump database to stderr
1402 */
1403 XBBool
DB_Dump(const DBRoot * db)1404 DB_Dump (const DBRoot * db)
1405 {
1406 const DBSection *section;
1407 const DBEntry *entry;
1408 /* now write it */
1409 fprintf (stderr, "---database dump----\n");
1410 for (section = db->section; section != NULL; section = section->next) {
1411 fprintf (stderr, "[%s]\n", GUI_AtomToString (section->atom));
1412 for (entry = section->entry; entry != NULL; entry = entry->next) {
1413 fprintf (stderr, "%s=%s\n", GUI_AtomToString (entry->atom), entry->value);
1414 }
1415 fprintf (stderr, "\n");
1416 }
1417 fprintf (stderr, "---end of database dump---\n");
1418 /* that's all */
1419 return XBTrue;
1420 } /* DB_Dump */
1421
1422 /*
1423 * load complete directory of files into database,
1424 */
1425 XBBool
DB_LoadDir(DBRoot * db,const char * path,const char * ext,DBType type,XBAtom timeAtom,XBAtom section,DBLoadDirFunc func,XBBool rec)1426 DB_LoadDir (DBRoot * db, const char *path, const char *ext, DBType type, XBAtom timeAtom,
1427 XBAtom section, DBLoadDirFunc func, XBBool rec)
1428 {
1429 XBBool changed;
1430 XBDir *fileList;
1431 XBDir *ptr;
1432 /* sanity checks */
1433 assert (NULL != db);
1434 assert (NULL != path);
1435 assert (NULL != ext);
1436 /* load database from file */
1437 DB_Load (db);
1438 /* now compare with current directory contents */
1439 changed = XBFalse;
1440 fileList = CreateFileList (path, ext, rec);
1441 for (ptr = fileList; NULL != ptr; ptr = ptr->next) {
1442 if (!CheckInsertFile (db, ptr->name, timeAtom, ptr->mtime)) {
1443 InsertFile (db, type, ptr->name, timeAtom, ptr->mtime, section, func);
1444 changed = XBTrue;
1445 #ifdef DEBUG
1446 putchar ('*');
1447 #endif
1448 }
1449 else {
1450 UpdateFile (db, ptr->name);
1451 #ifdef DEBUG
1452 putchar ('.');
1453 #endif
1454 }
1455 #ifdef DEBUG
1456 fflush (stdout);
1457 #endif
1458 }
1459 /* now delete "unchanged section", because they do not have any related files */
1460 if (DeleteUnchangedSections (db)) {
1461 changed = XBTrue;
1462 }
1463 if (NULL != fileList) {
1464 DeleteFileList (fileList);
1465 #ifdef DEBUG
1466 putchar ('\n');
1467 #endif
1468 }
1469 return changed;
1470 } /* DB_LoadDir */
1471
1472 /*
1473 * read message of the day, return line count
1474 */
1475 int
ReadMessageOfTheDay(int m,char * s,int * l)1476 ReadMessageOfTheDay (int m, char *s, int *l)
1477 {
1478 FILE *fp;
1479 int i;
1480 char *c, *d;
1481 i = 0;
1482 /* try to open the file */
1483 fp = FileOpen (db_path[3], "message", "txt", "r");
1484 if (NULL != fp) {
1485 c = s;
1486 i = 0;
1487 l[i] = 0;
1488 while (1) {
1489 /* get character */
1490 d = fgets (c, m - l[i], fp);
1491 if (d == NULL) {
1492 break;
1493 }
1494 i++;
1495 l[i] = strlen (s) - 1;
1496 c = s + strlen (s) - 1;
1497 }
1498 l[i + 1] = strlen (s);
1499 }
1500 return i;
1501 } /* ReadmessageOfTheDay */
1502
1503 /*
1504 * clean up databases
1505 */
1506 void
DB_Finish(void)1507 DB_Finish (void)
1508 {
1509 DBType t;
1510 for (t = DT_Config; t < NUM_DT; t++) {
1511 while (NULL != db_list[t]) {
1512 DB_Delete (db_list[t]);
1513 }
1514 db_list[t] = NULL;
1515 }
1516 } /* FinishControl */
1517
1518 /*
1519 * end of file ini_file.c
1520 */
1521