1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2 Copyright (C) 2006 and later, Cockos, Inc.
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
20 This file implements basic win32 GetPrivateProfileString / etc support.
21 It works by caching reads, but writing through on every write that is required (to ensure
22 that updates take, especially when being updated from multiple modules who have their own
23 cache of the .ini file).
25 It is threadsafe, but in theory if two processes are trying to access the same ini,
26 results may go a bit unpredictable (but in general the file should NOT get corrupted,
31 #ifndef SWELL_PROVIDED_BY_APP
35 #include "../assocarray.h"
36 #include "../wdlcstring.h"
41 #include <sys/types.h>
43 static void deleteStringKeyedArray(WDL_StringKeyedArray
<char *> *p
) { delete p
; }
47 iniFileContext() : m_sections(false,deleteStringKeyedArray
)
56 WDL_UINT64 m_lastaccesscnt
;
61 WDL_StringKeyedArray
< WDL_StringKeyedArray
<char *> * > m_sections
;
65 #define NUM_OPEN_CONTEXTS 32
66 static iniFileContext s_ctxs
[NUM_OPEN_CONTEXTS
];
67 static WDL_Mutex m_mutex
;
69 static time_t getfileupdtimesize(const char *fn
, int *szOut
)
73 if (!fn
|| !fn
[0] || stat(fn
,&st
)) return 0;
74 *szOut
= (int)st
.st_size
;
78 static bool fgets_to_typedbuf(WDL_TypedBuf
<char> *buf
, FILE *fp
)
81 while (rdpos
< 1024*1024*32)
83 if (buf
->GetSize()<rdpos
+8192) buf
->Resize(rdpos
+8192);
84 if (buf
->GetSize()<rdpos
+4) break; // malloc fail, erg
85 char *p
= buf
->Get()+rdpos
;
87 fgets(p
,buf
->GetSize()-rdpos
,fp
);
90 if (p
[-1] == '\r' || p
[-1] == '\n') break;
92 rdpos
= (int) (p
- buf
->Get());
94 return buf
->GetSize()>0 && buf
->Get()[0];
97 // return true on success
98 static iniFileContext
*GetFileContext(const char *name
)
100 static WDL_UINT64 acc_cnt
;
103 if (!name
|| !strstr(name
,"/"))
105 extern char *g_swell_defini
;
108 lstrcpyn_safe(fntemp
,g_swell_defini
,sizeof(fntemp
));
112 const char *p
= getenv("HOME");
113 snprintf(fntemp
,sizeof(fntemp
),"%s/.libSwell.ini",
114 p
&& *p
? p
: "/tmp");
118 WDL_remove_fileext(fntemp
);
119 snprintf_append(fntemp
,sizeof(fntemp
),"_%s%s",name
,
120 stricmp(WDL_get_fileext(name
),".ini")?".ini":"");
127 WDL_UINT64 bestcnt
= 0;
130 for (w
=0;w
<NUM_OPEN_CONTEXTS
;w
++)
132 if (!s_ctxs
[w
].m_curfn
|| !stricmp(s_ctxs
[w
].m_curfn
,name
))
134 // we never clear m_curfn, so we'll always find an item in cache before an unused cache entry
139 if (s_ctxs
[w
].m_lastaccesscnt
< bestcnt
) { best_z
= w
; bestcnt
= s_ctxs
[w
].m_lastaccesscnt
; }
143 iniFileContext
*ctx
= &s_ctxs
[best_z
];
144 ctx
->m_lastaccesscnt
=++acc_cnt
;
147 if (!ctx
->m_curfn
|| stricmp(ctx
->m_curfn
,name
) || ctx
->m_curfn_time
!= getfileupdtimesize(ctx
->m_curfn
,&sz
) || sz
!= ctx
->m_curfn_sz
)
149 ctx
->m_sections
.DeleteAll();
150 // printf("reinitting to %s\n",name);
151 if (!ctx
->m_curfn
|| stricmp(ctx
->m_curfn
,name
))
154 ctx
->m_curfn
=strdup(name
);
156 FILE *fp
= fopen(name
,"r");
162 return ctx
; // allow to proceed (empty file)
165 flock(fileno(fp
),LOCK_SH
);
168 WDL_StringKeyedArray
<char *> *cursec
=NULL
;
173 static WDL_TypedBuf
<char> _buf
;
174 if (!fgets_to_typedbuf(&_buf
,fp
)) break;
176 char *buf
= _buf
.Get();
177 if (!ctx
->m_sections
.GetSize())
180 if (lcnt
> 256*1024) break; // dont bother reading more than 256kb if no section encountered
189 while (p
>= buf
&& (*p
==' ' || *p
== '\r' || *p
== '\n' || *p
== '\t')) p
--;
193 while (*p
== ' ' || *p
== '\t') p
++;
197 while (*p2
&& *p2
!= ']') p2
++;
201 if (cursec
) cursec
->Resort();
205 cursec
= ctx
->m_sections
.Get(p
+1);
208 cursec
= new WDL_StringKeyedArray
<char *>(false,WDL_StringKeyedArray
<char *>::freecharptr
);
209 ctx
->m_sections
.Insert(p
+1,cursec
);
211 else cursec
->DeleteAll();
218 char *t
=strstr(p
,"=");
223 cursec
->AddUnsorted(p
,strdup(t
));
227 ctx
->m_curfn_time
= getfileupdtimesize(name
,&ctx
->m_curfn_sz
);
228 flock(fileno(fp
),LOCK_UN
);
231 if (cursec
) cursec
->Resort();
236 static void WriteBackFile(iniFileContext
*ctx
)
238 if (!ctx
||!ctx
->m_curfn
) return;
240 lstrcpyn_safe(newfn
,ctx
->m_curfn
,sizeof(newfn
)-8);
244 while (p
>newfn
&& p
[-1] != '/') p
--;
256 FILE *fp
= fopen(newfn
,"w");
259 flock(fileno(fp
),LOCK_EX
);
264 const char *secname
=NULL
;
265 WDL_StringKeyedArray
<char *> * cursec
= ctx
->m_sections
.Enumerate(x
,&secname
);
266 if (!cursec
|| !secname
) break;
268 fprintf(fp
,"[%s]\n",secname
);
272 const char *keyname
= NULL
;
273 const char *keyvalue
= cursec
->Enumerate(y
,&keyname
);
274 if (!keyvalue
|| !keyname
) break;
275 if (*keyname
) fprintf(fp
,"%s=%s\n",keyname
,keyvalue
);
281 flock(fileno(fp
),LOCK_UN
);
284 if (!rename(newfn
,ctx
->m_curfn
))
286 ctx
->m_curfn_time
= getfileupdtimesize(ctx
->m_curfn
,&ctx
->m_curfn_sz
);
290 // error updating, hmm how to handle this?
294 BOOL
WritePrivateProfileSection(const char *appname
, const char *strings
, const char *fn
)
296 if (!appname
) return FALSE
;
297 WDL_MutexLock
lock(&m_mutex
);
298 iniFileContext
*ctx
= GetFileContext(fn
);
299 if (!ctx
) return FALSE
;
301 WDL_StringKeyedArray
<char *> * cursec
= ctx
->m_sections
.Get(appname
);
304 if (!*strings
) return TRUE
;
306 cursec
= new WDL_StringKeyedArray
<char *>(false,WDL_StringKeyedArray
<char *>::freecharptr
);
307 ctx
->m_sections
.Insert(appname
,cursec
);
309 else cursec
->DeleteAll();
316 lstrcpyn_safe(buf
,strings
,sizeof(buf
));
318 while (*p
&& *p
!= '=') p
++;
322 cursec
->Insert(buf
,strdup(strings
+ (p
-buf
)));
325 strings
+= strlen(strings
)+1;
334 BOOL
WritePrivateProfileString(const char *appname
, const char *keyname
, const char *val
, const char *fn
)
336 if (!appname
|| (keyname
&& !*keyname
)) return FALSE
;
337 // printf("writing %s %s %s %s\n",appname,keyname,val,fn);
338 WDL_MutexLock
lock(&m_mutex
);
340 iniFileContext
*ctx
= GetFileContext(fn
);
341 if (!ctx
) return FALSE
;
345 if (ctx
->m_sections
.Get(appname
))
347 ctx
->m_sections
.Delete(appname
);
353 WDL_StringKeyedArray
<char *> * cursec
= ctx
->m_sections
.Get(appname
);
356 if (cursec
&& cursec
->Get(keyname
))
358 cursec
->Delete(keyname
);
365 if (!cursec
|| !(p
=cursec
->Get(keyname
)) || strcmp(p
,val
))
369 cursec
= new WDL_StringKeyedArray
<char *>(false,WDL_StringKeyedArray
<char *>::freecharptr
);
370 ctx
->m_sections
.Insert(appname
,cursec
);
372 cursec
->Insert(keyname
,strdup(val
));
382 static void lstrcpyn_trimmed(char* dest
, const char* src
, int len
)
385 // Mimic Win32 behavior of stripping quotes and whitespace
386 while (*src
==' ' || *src
=='\t') ++src
; // Strip beginning whitespace
388 const char *end
= src
;
389 if (*end
) while (end
[1]) end
++;
391 while (end
>= src
&& (*end
==' ' || *end
=='\t')) --end
; // Strip end whitespace
393 if (end
> src
&& ((*src
=='\"' && *end
=='\"') || (*src
=='\'' && *end
=='\'')))
395 // Strip initial set of "" or ''
400 int newlen
= (int) (end
-src
+2);
401 if (newlen
< 1) newlen
= 1;
402 else if (newlen
> len
) newlen
= len
;
404 lstrcpyn_safe(dest
, src
, newlen
);
407 DWORD
GetPrivateProfileSection(const char *appname
, char *strout
, DWORD strout_len
, const char *fn
)
409 WDL_MutexLock
lock(&m_mutex
);
411 if (!strout
|| strout_len
<2)
413 if (strout
&& strout_len
==1) *strout
=0;
416 iniFileContext
*ctx
= GetFileContext(fn
);
418 WDL_StringKeyedArray
<char *> *cursec
= ctx
? ctx
->m_sections
.Get(appname
) : NULL
;
423 for(x
=0;x
<cursec
->GetSize();x
++)
425 const char *kv
= NULL
;
426 const char *val
= cursec
->Enumerate(x
,&kv
);
432 l = (int)strlen(v); \
433 if (l > (int)strout_len - szOut - 2) l = (int)strout_len - 2 - szOut; \
434 if (l>0) { memcpy(strout+szOut,v,l); szOut+=l; }
440 lstrcpyn_trimmed(strout
+szOut
, val
, (int)strout_len
- szOut
- 2);
441 szOut
+= strlen(strout
+szOut
);
444 if (l
> (int)strout_len
- szOut
- 1) l
= (int)strout_len
- 1 - szOut
;
445 if (l
>0) { memset(strout
+szOut
,0,l
); szOut
+=l
; }
446 if (szOut
>= (int)strout_len
-1)
448 strout
[strout_len
-1]=0;
455 if (!szOut
) strout
[1]=0;
459 DWORD
GetPrivateProfileString(const char *appname
, const char *keyname
, const char *def
, char *ret
, int retsize
, const char *fn
)
461 WDL_MutexLock
lock(&m_mutex
);
463 // printf("getprivateprofilestring: %s\n",fn);
464 iniFileContext
*ctx
= GetFileContext(fn
);
468 if (!appname
||!keyname
)
476 const char *secname
=NULL
;
477 if (!ctx
->m_sections
.Enumerate(x
,&secname
) || !secname
) break;
478 if (*secname
) tmpbuf
.Add(secname
,(int)strlen(secname
)+1);
483 WDL_StringKeyedArray
<char *> *cursec
= ctx
->m_sections
.Get(appname
);
490 if (!cursec
->Enumerate(y
,&k
)||!k
) break;
491 if (*k
) tmpbuf
.Add(k
,(int)strlen(k
)+1);
496 int sz
=tmpbuf
.GetSize()-1;
502 if (sz
> retsize
-2) sz
=retsize
-2;
503 memcpy(ret
,tmpbuf
.Get(),sz
);
509 WDL_StringKeyedArray
<char *> *cursec
= ctx
->m_sections
.Get(appname
);
512 const char *val
= cursec
->Get(keyname
);
515 lstrcpyn_trimmed(ret
,val
,retsize
);
516 return (DWORD
)strlen(ret
);
520 // printf("def %s %s %s %s\n",appname,keyname,def,fn);
521 lstrcpyn_safe(ret
,def
?def
:"",retsize
);
522 return (DWORD
)strlen(ret
);
525 int GetPrivateProfileInt(const char *appname
, const char *keyname
, int def
, const char *fn
)
528 GetPrivateProfileString(appname
,keyname
,"",buf
,sizeof(buf
),fn
);
532 if (a
||buf
[0]=='0') return a
;
537 static bool __readbyte(char *src
, unsigned char *out
)
543 if (*src
>= '0' && *src
<= '9') cv
+= (*src
-'0')<<s
;
544 else if (*src
>= 'a' && *src
<= 'f') cv
+= (*src
-'a' + 10)<<s
;
545 else if (*src
>= 'A' && *src
<= 'F') cv
+= (*src
-'A' + 10)<<s
;
555 BOOL
GetPrivateProfileStruct(const char *appname
, const char *keyname
, void *buf
, int bufsz
, const char *fn
)
557 if (!appname
|| !keyname
|| bufsz
<0) return 0;
558 char *tmp
=(char *)malloc((bufsz
+1)*2+16);
562 GetPrivateProfileString(appname
,keyname
,"",tmp
,(bufsz
+1)*2+15,fn
);
563 if (strlen(tmp
) == (size_t) (bufsz
+1)*2)
566 unsigned char *bufout
=(unsigned char *)buf
;
571 if (!__readbyte(src
,&cv
)) break;
576 ret
= bufsz
<0 && __readbyte(src
,&cv
) && cv
==sum
;
579 //printf("getprivateprofilestruct returning %d\n",ret);
583 BOOL
WritePrivateProfileStruct(const char *appname
, const char *keyname
, const void *buf
, int bufsz
, const char *fn
)
585 if (!keyname
|| !buf
) return WritePrivateProfileString(appname
,keyname
,(const char *)buf
,fn
);
586 char *tmp
=(char *)malloc((bufsz
+1)*2+1);
590 unsigned char *src
=(unsigned char *)buf
;
593 sprintf(p
,"%02X",*src
);
597 sprintf(p
,"%02X",sum
);
599 BOOL ret
=WritePrivateProfileString(appname
,keyname
,tmp
,fn
);