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, &ltime) || 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