Skip to content

Commit 95fa481

Browse files
committed
Avoid rewriting the INI file when attemptiny to delete a key that already isn't present in the INI file.
When writing to an INI file, reopen the original file *after* opening the temporary file (because opening the temporary file may have been blocked on a file lock). Added a section about file locking in the manual. Added an example glue file for file locking in Linux.
1 parent b40dff4 commit 95fa481

File tree

3 files changed

+79
-10
lines changed

3 files changed

+79
-10
lines changed

dev/minGlue-Linux.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* Glue functions for the minIni library, based on the C/C++ stdio library and
2+
* using BSD-style file locking to "serialize" concurrent accesses to an INI
3+
* file. It presumes GCC compiler extensions.
4+
*
5+
* By CompuPhase, 2020
6+
* This "glue file" is in the public domain. It is distributed without
7+
* warranties or conditions of any kind, either express or implied.
8+
*/
9+
10+
/* map required file I/O types and functions to the standard C library */
11+
#include <stdio.h>
12+
#include <unistd.h>
13+
#include <sys/file.h>
14+
15+
#define INI_FILETYPE FILE*
16+
17+
static inline int ini_openread(const char *filename, INI_FILETYPE *file) {
18+
if ((*file = fopen((filename),"r")) == NULL)
19+
return 0;
20+
return flock(fileno(*file), LOCK_SH) == 0;
21+
}
22+
23+
static inline int ini_openwrite(const char *filename, INI_FILETYPE *file) {
24+
if ((*file = fopen((filename),"r+")) == NULL
25+
&& (*file = fopen((filename),"w")) == NULL)
26+
return 0;
27+
if (flock(fileno(*file), LOCK_EX) < 0)
28+
return 0;
29+
return ftruncate(fileno(*file), 0);
30+
}
31+
32+
#define INI_OPENREWRITE
33+
static inline int ini_openrewrite(const char *filename, INI_FILETYPE *file) {
34+
if ((*file = fopen((filename),"r+")) == NULL)
35+
return 0;
36+
return flock(fileno(*file), LOCK_EX) == 0;
37+
}
38+
39+
#define ini_close(file) (fclose(*(file)) == 0)
40+
#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL)
41+
#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0)
42+
#define ini_rename(source,dest) (rename((source), (dest)) == 0)
43+
44+
#define INI_FILEPOS long int
45+
#define ini_tell(file,pos) (*(pos) = ftell(*(file)))
46+
#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0)
47+
48+
/* for floating-point support, define additional types and functions */
49+
#define INI_REAL float
50+
#define ini_ftoa(string,value) sprintf((string),"%f",(value))
51+
#define ini_atof(string) (INI_REAL)strtod((string),NULL)

dev/minIni.c

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* These routines are in part based on the article "Multiplatform .INI Files"
44
* by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal.
55
*
6-
* Copyright (c) CompuPhase, 2008-2017
6+
* Copyright (c) CompuPhase, 2008-2020
77
*
88
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
99
* use this file except in compliance with the License. You may obtain a copy
@@ -611,8 +611,10 @@ static int close_rename(INI_FILETYPE *rfp, INI_FILETYPE *wfp, const TCHAR *filen
611611
{
612612
(void)ini_close(rfp);
613613
(void)ini_close(wfp);
614-
(void)ini_remove(filename);
615614
(void)ini_tempname(buffer, filename, INI_BUFFERSIZE);
615+
#if defined ini_remove || defined INI_REMOVE
616+
(void)ini_remove(filename);
617+
#endif
616618
(void)ini_rename(buffer, filename);
617619
return 1;
618620
}
@@ -653,7 +655,6 @@ int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const T
653655
* the INI file.
654656
*/
655657
if (Key != NULL && Value != NULL) {
656-
ini_tell(&rfp, &mark);
657658
match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), &head);
658659
if (match) {
659660
/* if the current setting is identical to the one to write, there is
@@ -666,7 +667,7 @@ int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const T
666667
/* if the new setting has the same length as the current setting, and the
667668
* glue file permits file read/write access, we can modify in place.
668669
*/
669-
#if defined ini_openrewrite
670+
#if defined ini_openrewrite || defined INI_OPENREWRITE
670671
/* we already have the start of the (raw) line, get the end too */
671672
ini_tell(&rfp, &tail);
672673
/* create new buffer (without writing it to file) */
@@ -685,20 +686,37 @@ int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const T
685686
} /* if */
686687
#endif
687688
} /* if */
688-
/* key not found, or different value & length -> proceed (but rewind the
689-
* input file first)
690-
*/
691-
(void)ini_seek(&rfp, &mark);
689+
/* key not found, or different value & length -> proceed */
690+
} else if (Key != NULL && Value == NULL) {
691+
/* Conversely, for a request to delete a setting; if that setting isn't
692+
present, just return */
693+
match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), NULL);
694+
if (!match) {
695+
(void)ini_close(&rfp);
696+
return 1;
697+
} /* if */
698+
/* key found -> proceed to delete it */
692699
} /* if */
693700

694701
/* Get a temporary file name to copy to. Use the existing name, but with
695702
* the last character set to a '~'.
696703
*/
704+
(void)ini_close(&rfp);
697705
ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE);
698-
if (!ini_openwrite(LocalBuffer, &wfp)) {
699-
(void)ini_close(&rfp);
706+
if (!ini_openwrite(LocalBuffer, &wfp))
700707
return 0;
708+
/* In the case of (advisory) file locks, ini_openwrite() may have been blocked
709+
* on the open, and after the block is lifted, the original file may have been
710+
* renamed, which is why the original file was closed and is now reopened */
711+
if (!ini_openread(Filename, &rfp)) {
712+
/* If the .ini file doesn't exist any more, make a new file */
713+
assert(Key != NULL && Value != NULL);
714+
writesection(LocalBuffer, Section, &wfp);
715+
writekey(LocalBuffer, Key, Value, &wfp);
716+
(void)ini_close(&wfp);
717+
return 1;
701718
} /* if */
719+
702720
(void)ini_tell(&rfp, &mark);
703721
cachelen = 0;
704722

doc/minIni.pdf

2.97 KB
Binary file not shown.

0 commit comments

Comments
 (0)