summaryrefslogtreecommitdiff
path: root/storage/connect/inihandl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/connect/inihandl.cpp')
-rw-r--r--storage/connect/inihandl.cpp1407
1 files changed, 1407 insertions, 0 deletions
diff --git a/storage/connect/inihandl.cpp b/storage/connect/inihandl.cpp
new file mode 100644
index 00000000000..96ae0a67a6b
--- /dev/null
+++ b/storage/connect/inihandl.cpp
@@ -0,0 +1,1407 @@
+/*
+ * Profile functions
+ *
+ * Copyright 1993 Miguel de Icaza
+ * Copyright 1996 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+#include "my_global.h"
+
+#include <ctype.h>
+//#include <errno.h>
+#include <fcntl.h>
+//#include <io.h> commented this line out to compile for solaris
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+//#include <sys/types.h>
+//#include <memory.h>
+#include "osutil.h"
+#include "global.h"
+#include "inihandl.h"
+
+// The types and variables used locally
+//typedef int bool;
+typedef unsigned int uint;
+//#define SVP(S) ((S) ? S : "<null>")
+#define _strlwr(P) strlwr(P) //OB: changed this line
+#define MAX_PATHNAME_LEN 256
+#define N_CACHED_PROFILES 10
+#ifndef WIN32
+#define stricmp strcasecmp
+#define _strnicmp strncasecmp
+#endif // !WIN32
+#define EnterCriticalSection(x)
+#define LeaveCriticalSection(x)
+
+#if defined(TEST_MODULE)
+// Stand alone test program
+#include <stdarg.h>
+ int trace = 0;
+void htrc(char const *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end (ap);
+} /* end of htrc */
+#else // !TEST_MODULE
+// Normal included functions
+//extern int trace;
+//void htrc(char const *fmt, ...);
+#endif // !TEST MODULE
+
+
+typedef struct tagPROFILEKEY {
+ char *value;
+ struct tagPROFILEKEY *next;
+ char name[1];
+ } PROFILEKEY;
+
+typedef struct tagPROFILESECTION {
+ struct tagPROFILEKEY *key;
+ struct tagPROFILESECTION *next;
+ char name[1];
+ } PROFILESECTION;
+
+typedef struct {
+ BOOL changed;
+ PROFILESECTION *section;
+//char *dos_name;
+//char *unix_name;
+ char *filename;
+ time_t mtime;
+ } PROFILE;
+
+#define memfree(P) if (P) free(P)
+
+/* Cached profile files */
+static PROFILE *MRUProfile[N_CACHED_PROFILES] = {NULL};
+
+#define CurProfile (MRUProfile[0])
+
+/* wine.ini config file registry root */
+//static HKEY wine_profile_key;
+
+#define PROFILE_MAX_LINE_LEN 1024
+
+/* Wine profile name in $HOME directory; must begin with slash */
+//static const char PROFILE_WineIniName[] = "/.winerc";
+
+/* Wine profile: the profile file being used */
+//static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
+
+/* Check for comments in profile */
+#define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
+
+//static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
+
+//static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
+
+static const char hex[17] = "0123456789ABCDEF";
+
+BOOL WritePrivateProfileString(LPCSTR section, LPCSTR entry,
+ LPCSTR string, LPCSTR filename);
+
+/***********************************************************************
+ * PROFILE_CopyEntry
+ *
+ * Copy the content of an entry into a buffer, removing quotes,
+ * and possibly translating environment variables.
+ ***********************************************************************/
+static void PROFILE_CopyEntry( char *buffer, const char *value, uint len,
+ int handle_env )
+{
+ const char *p;
+ char quote = '\0';
+
+ if (!buffer)
+ return;
+
+ if ((*value == '\'') || (*value == '\"'))
+ if (value[1] && (value[strlen(value)-1] == *value))
+ quote = *value++;
+
+ if (!handle_env) {
+ strncpy(buffer, value, len);
+
+ if (quote && (len >= strlen(value)))
+ buffer[strlen(buffer)-1] = '\0';
+
+ return;
+ } // endif handle
+
+ for (p = value; (*p && (len > 1)); *buffer++ = *p++, len--) {
+ if ((*p == '$') && (p[1] == '{')) {
+ char env_val[1024];
+ const char *env_p;
+ const char *p2 = strchr(p, '}');
+
+ if (!p2)
+ continue; /* ignore it */
+
+ strncpy(env_val, p + 2, MY_MIN((int) sizeof(env_val), (int)(p2-p)-1));
+
+ if ((env_p = getenv(env_val)) != NULL) {
+ int buffer_len;
+
+ strncpy( buffer, env_p, len );
+ buffer_len = strlen( buffer );
+ buffer += buffer_len;
+ len -= buffer_len;
+ } // endif env_p
+
+ p = p2 + 1;
+ } // endif p
+
+ } // endfor p
+
+ if (quote && (len > 1))
+ buffer--;
+
+ *buffer = '\0';
+} // end of PROFILE_CopyEntry
+
+
+/***********************************************************************
+ * PROFILE_Save
+ *
+ * Save a profile tree to a file.
+ ***********************************************************************/
+static void PROFILE_Save( FILE *file, PROFILESECTION *section )
+{
+ PROFILEKEY *key;
+ int secno;
+
+ for (secno= 0; section; section= section->next) {
+ if (section->name[0]) {
+ fprintf(file, "%s[%s]\n", secno ? "\n" : "", SVP(section->name));
+ secno++;
+ }
+
+ for (key = section->key; key; key = key->next)
+ if (key->name && key->name[0]) {
+ fprintf(file, "%s", SVP(key->name));
+
+ if (key->value)
+ fprintf(file, "=%s", SVP(key->value));
+
+ fprintf(file, "\n");
+ } // endif key->name
+
+ } // endfor section
+
+} // end of PROFILE_Save
+
+
+/***********************************************************************
+ * PROFILE_Free
+ *
+ * Free a profile tree.
+ ***********************************************************************/
+static void PROFILE_Free( PROFILESECTION *section )
+{
+ PROFILESECTION *next_section;
+ PROFILEKEY *key, *next_key;
+
+ for (; section; section = next_section) {
+ for (key = section->key; key; key = next_key) {
+ next_key = key->next;
+ memfree(key->value);
+ free(key);
+ } // endfor key
+
+ next_section = section->next;
+ free(section);
+ } // endfor section
+
+} // end of PROFILE_Free
+
+static int PROFILE_isspace(char c)
+{
+ /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
+ if (isspace(c) || c=='\r' || c==0x1a)
+ return 1;
+
+ return 0;
+} // end of PROFILE_isspace
+
+
+/***********************************************************************
+ * PROFILE_Load
+ *
+ * Load a profile tree from a file.
+ ***********************************************************************/
+static PROFILESECTION *PROFILE_Load( FILE *file )
+{
+ char buffer[PROFILE_MAX_LINE_LEN];
+ char *p, *p2;
+ int line = 0;
+ PROFILESECTION *section, *first_section;
+ PROFILESECTION* *next_section;
+ PROFILEKEY *key, *prev_key, **next_key;
+
+ first_section = (PROFILESECTION*)malloc(sizeof(*section));
+
+ if (first_section == NULL)
+ return NULL;
+
+ first_section->name[0] = 0;
+ first_section->key = NULL;
+ first_section->next = NULL;
+ next_section = &first_section->next;
+ next_key = &first_section->key;
+ prev_key = NULL;
+
+ while (fgets(buffer, PROFILE_MAX_LINE_LEN, file)) {
+ line++;
+ p = buffer;
+
+ while (*p && PROFILE_isspace(*p))
+ p++;
+
+ if (*p == '[') { /* section start */
+ if (!(p2 = strrchr( p, ']'))) {
+ fprintf(stderr, "Invalid section header at line %d: '%s'\n",
+ line, p);
+ } else {
+ *p2 = '\0';
+ p++;
+
+ if (!(section = (PROFILESECTION*)malloc(sizeof(*section) + strlen(p))))
+ break;
+
+ strcpy(section->name, p);
+ section->key = NULL;
+ section->next = NULL;
+ *next_section = section;
+ next_section = &section->next;
+ next_key = &section->key;
+ prev_key = NULL;
+
+ if (trace > 1)
+ htrc("New section: '%s'\n",section->name);
+
+ continue;
+ } // endif p2
+
+ } // endif p
+
+ p2 = p + strlen(p) - 1;
+
+ while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2)))
+ *p2-- = '\0';
+
+ if ((p2 = strchr(p, '=')) != NULL) {
+ char *p3 = p2 - 1;
+
+ while ((p3 > p) && PROFILE_isspace(*p3))
+ *p3-- = '\0';
+
+ *p2++ = '\0';
+
+ while (*p2 && PROFILE_isspace(*p2))
+ p2++;
+
+ } // endif p2
+
+ if (*p || !prev_key || *prev_key->name) {
+ if (!(key = (PROFILEKEY*)malloc(sizeof(*key) + strlen(p))))
+ break;
+
+ strcpy(key->name, p);
+
+ if (p2) {
+ key->value = (char*)malloc(strlen(p2)+1);
+ strcpy(key->value, p2);
+ } else
+ key->value = NULL;
+
+ key->next = NULL;
+ *next_key = key;
+ next_key = &key->next;
+ prev_key = key;
+
+ if (trace > 1)
+ htrc("New key: name='%s', value='%s'\n",
+ key->name,key->value?key->value:"(none)");
+
+ } // endif p || prev_key
+
+ } // endif *p
+
+ return first_section;
+} // end of PROFILE_Load
+
+/***********************************************************************
+ * PROFILE_FlushFile
+ *
+ * Flush the current profile to disk if changed.
+ ***********************************************************************/
+static BOOL PROFILE_FlushFile(void)
+{
+//char *p, buffer[MAX_PATHNAME_LEN];
+//const char *unix_name;
+ FILE *file = NULL;
+ struct stat buf;
+
+ if (trace > 1)
+ htrc("PROFILE_FlushFile: CurProfile=%p\n", CurProfile);
+
+ if (!CurProfile) {
+ fprintf(stderr, "No current profile!\n");
+ return FALSE;
+ } // endif !CurProfile
+
+ if (!CurProfile->changed || !CurProfile->filename)
+ return TRUE;
+
+#if 0
+ if (!(file = fopen(unix_name, "w"))) {
+ /* Try to create it in $HOME/.wine */
+ /* FIXME: this will need a more general solution */
+ //strcpy( buffer, get_config_dir() );
+ //p = buffer + strlen(buffer);
+ //*p++ = '/';
+ char *p1 = strrchr(CurProfile->filename, '\\');
+
+ p = buffer; // OB: To be elaborate
+
+ if (p1)
+ p1++;
+ else
+ p1 = CurProfile->dos_name;
+
+ strcpy(p, p1);
+ _strlwr(p);
+ file = fopen(buffer, "w");
+ unix_name = buffer;
+ } // endif !unix_name
+#endif // 0
+
+ if (!(file = fopen(CurProfile->filename, "w"))) {
+ fprintf(stderr, "could not save profile file %s\n", CurProfile->filename);
+ return FALSE;
+ } // endif !file
+
+ if (trace > 1)
+ htrc("Saving '%s'\n", CurProfile->filename);
+
+ PROFILE_Save(file, CurProfile->section);
+ fclose(file);
+ CurProfile->changed = FALSE;
+
+ if (!stat(CurProfile->filename, &buf))
+ CurProfile->mtime = buf.st_mtime;
+
+ return TRUE;
+} // end of PROFILE_FlushFile
+
+
+/***********************************************************************
+ * PROFILE_ReleaseFile
+ *
+ * Flush the current profile to disk and remove it from the cache.
+ ***********************************************************************/
+static void PROFILE_ReleaseFile(void)
+{
+ PROFILE_FlushFile();
+ PROFILE_Free(CurProfile->section);
+//memfree(CurProfile->dos_name);
+//memfree(CurProfile->unix_name);
+ memfree(CurProfile->filename);
+ CurProfile->changed = FALSE;
+ CurProfile->section = NULL;
+//CurProfile->dos_name = NULL;
+//CurProfile->unix_name = NULL;
+ CurProfile->filename = NULL;
+ CurProfile->mtime = 0;
+} // end of PROFILE_ReleaseFile
+
+
+/***********************************************************************
+ * PROFILE_Open
+ *
+ * Open a profile file, checking the cached file first.
+ ***********************************************************************/
+static BOOL PROFILE_Open(LPCSTR filename)
+{
+//char buffer[MAX_PATHNAME_LEN];
+//char *p;
+ FILE *file = NULL;
+ int i, j;
+ struct stat buf;
+ PROFILE *tempProfile;
+
+ if (trace > 1)
+ htrc("PROFILE_Open: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES);
+
+ /* First time around */
+ if (!CurProfile)
+ for (i = 0; i < N_CACHED_PROFILES; i++) {
+ MRUProfile[i] = (PROFILE*)malloc(sizeof(PROFILE));
+
+ if (MRUProfile[i] == NULL)
+ break;
+
+ MRUProfile[i]->changed=FALSE;
+ MRUProfile[i]->section=NULL;
+// MRUProfile[i]->dos_name=NULL;
+// MRUProfile[i]->unix_name=NULL;
+ MRUProfile[i]->filename=NULL;
+ MRUProfile[i]->mtime=0;
+ } // endfor i
+
+ /* Check for a match */
+ for (i = 0; i < N_CACHED_PROFILES; i++) {
+ if (trace > 1)
+ htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i);
+
+ if (MRUProfile[i]->filename && !strcmp(filename, MRUProfile[i]->filename)) {
+ if (i) {
+ PROFILE_FlushFile();
+ tempProfile = MRUProfile[i];
+
+ for (j = i; j > 0; j--)
+ MRUProfile[j] = MRUProfile[j-1];
+
+ CurProfile=tempProfile;
+ } // endif i
+
+ if (!stat(CurProfile->filename, &buf) && CurProfile->mtime == buf.st_mtime) {
+ if (trace > 1)
+ htrc("(%s): already opened (mru=%d)\n", filename, i);
+
+ } else {
+ if (trace > 1)
+ htrc("(%s): already opened, needs refreshing (mru=%d)\n", filename, i);
+
+ } // endif stat
+
+ return TRUE;
+ } // endif filename
+
+ } // endfor i
+
+ /* Flush the old current profile */
+ PROFILE_FlushFile();
+
+ /* Make the oldest profile the current one only in order to get rid of it */
+ if (i == N_CACHED_PROFILES) {
+ tempProfile = MRUProfile[N_CACHED_PROFILES-1];
+
+ for(i = N_CACHED_PROFILES-1; i > 0; i--)
+ MRUProfile[i] = MRUProfile[i-1];
+
+ CurProfile = tempProfile;
+ } // endif i
+
+ if (CurProfile->filename)
+ PROFILE_ReleaseFile();
+
+ /* OK, now that CurProfile is definitely free we assign it our new file */
+// newdos_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.short_name)+1 );
+// strcpy( newdos_name, full_name.short_name );
+
+// newdos_name = malloc(strlen(filename)+1);
+// strcpy(newdos_name, filename);
+
+// CurProfile->dos_name = newdos_name;
+ CurProfile->filename = (char*)malloc(strlen(filename) + 1);
+ strcpy(CurProfile->filename, filename);
+
+ /* Try to open the profile file, first in $HOME/.wine */
+
+ /* FIXME: this will need a more general solution */
+// strcpy( buffer, get_config_dir() );
+// p = buffer + strlen(buffer);
+// *p++ = '/';
+// strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
+// p = buffer;
+// strcpy(p, filename);
+// _strlwr(p);
+
+ if (trace > 1)
+ htrc("Opening %s\n", filename);
+
+ if ((file = fopen(filename, "r"))) {
+ if (trace > 1)
+ htrc("(%s): found it\n", filename);
+
+// CurProfile->unix_name = malloc(strlen(buffer)+1);
+// strcpy(CurProfile->unix_name, buffer);
+ } /* endif file */
+
+ if (file) {
+ CurProfile->section = PROFILE_Load(file);
+ fclose(file);
+
+ if (!stat(CurProfile->filename, &buf))
+ CurProfile->mtime = buf.st_mtime;
+
+ } else {
+ /* Does not exist yet, we will create it in PROFILE_FlushFile */
+ fprintf(stderr, "profile file %s not found\n", filename);
+ } /* endif file */
+
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * PROFILE_Close
+ *
+ * Flush the named profile to disk and remove it from the cache.
+ ***********************************************************************/
+void PROFILE_Close(LPCSTR filename)
+{
+ int i;
+ BOOL close = FALSE;
+ struct stat buf;
+ PROFILE *tempProfile;
+
+ if (trace > 1)
+ htrc("PROFILE_Close: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES);
+
+ /* Check for a match */
+ for (i = 0; i < N_CACHED_PROFILES; i++) {
+ if (trace > 1)
+ htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i);
+
+ if (MRUProfile[i]->filename && !strcmp(filename, MRUProfile[i]->filename)) {
+ if (i) {
+ /* Make the profile to close current */
+ tempProfile = MRUProfile[i];
+ MRUProfile[i] = MRUProfile[0];
+ MRUProfile[0] = tempProfile;
+ CurProfile=tempProfile;
+ } // endif i
+
+ if (trace > 1) {
+ if (!stat(CurProfile->filename, &buf) && CurProfile->mtime == buf.st_mtime)
+ htrc("(%s): already opened (mru=%d)\n", filename, i);
+ else
+ htrc("(%s): already opened, needs refreshing (mru=%d)\n", filename, i);
+
+ } // endif trace
+
+ close = TRUE;
+ break;
+ } // endif filename
+
+ } // endfor i
+
+ if (close)
+ PROFILE_ReleaseFile();
+
+} // end of PROFILE_Close
+
+
+/***********************************************************************
+ * PROFILE_End
+ *
+ * Terminate and release the cache.
+ ***********************************************************************/
+void PROFILE_End(void)
+{
+ int i;
+
+ if (trace)
+ htrc("PROFILE_End: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES);
+
+ if (!CurProfile) // Sergey Vojtovich
+ return;
+
+ /* Close all opened files and free the cache structure */
+ for (i = 0; i < N_CACHED_PROFILES; i++) {
+ if (trace)
+ htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i);
+
+// CurProfile = MRUProfile[i]; Sergey Vojtovich
+// PROFILE_ReleaseFile(); see MDEV-9997
+ free(MRUProfile[i]);
+ } // endfor i
+
+} // end of PROFILE_End
+
+
+/***********************************************************************
+ * PROFILE_DeleteSection
+ *
+ * Delete a section from a profile tree.
+ ***********************************************************************/
+static BOOL PROFILE_DeleteSection(PROFILESECTION* *section, LPCSTR name)
+{
+ while (*section) {
+ if ((*section)->name[0] && !stricmp((*section)->name, name)) {
+ PROFILESECTION *to_del = *section;
+
+ *section = to_del->next;
+ to_del->next = NULL;
+ PROFILE_Free(to_del);
+ return TRUE;
+ } // endif section
+
+ section = &(*section)->next;
+ } // endwhile section
+
+ return FALSE;
+} // end of PROFILE_DeleteSection
+
+
+/***********************************************************************
+ * PROFILE_DeleteKey
+ *
+ * Delete a key from a profile tree.
+ ***********************************************************************/
+static BOOL PROFILE_DeleteKey(PROFILESECTION* *section,
+ LPCSTR section_name, LPCSTR key_name)
+{
+ while (*section) {
+ if ((*section)->name[0] && !stricmp((*section)->name, section_name)) {
+ PROFILEKEY* *key = &(*section)->key;
+
+ while (*key) {
+ if (!stricmp((*key)->name, key_name)) {
+ PROFILEKEY *to_del = *key;
+
+ *key = to_del->next;
+ memfree(to_del->value);
+ free(to_del);
+ return TRUE;
+ } // endif name
+
+ key = &(*key)->next;
+ } // endwhile *key
+
+ } // endif section->name
+
+ section = &(*section)->next;
+ } // endwhile *section
+
+ return FALSE;
+} // end of PROFILE_DeleteKey
+
+
+/***********************************************************************
+ * PROFILE_DeleteAllKeys
+ *
+ * Delete all keys from a profile tree.
+ ***********************************************************************/
+static void PROFILE_DeleteAllKeys(LPCSTR section_name)
+{
+ PROFILESECTION* *section= &CurProfile->section;
+
+ while (*section) {
+ if ((*section)->name[0] && !stricmp((*section)->name, section_name)) {
+ PROFILEKEY* *key = &(*section)->key;
+
+ while (*key) {
+ PROFILEKEY *to_del = *key;
+
+ *key = to_del->next;
+ memfree(to_del->value);
+ free(to_del);
+ CurProfile->changed = TRUE;
+ } // endwhile *key
+
+ } // endif section->name
+
+ section = &(*section)->next;
+ } // endwhile *section
+
+} // end of PROFILE_DeleteAllKeys
+
+
+/***********************************************************************
+ * PROFILE_Find
+ *
+ * Find a key in a profile tree, optionally creating it.
+ ***********************************************************************/
+static PROFILEKEY *PROFILE_Find(PROFILESECTION* *section,
+ const char *section_name,
+ const char *key_name,
+ BOOL create, BOOL create_always)
+{
+ const char *p;
+ int seclen, keylen;
+
+ while (PROFILE_isspace(*section_name))
+ section_name++;
+
+ p = section_name + strlen(section_name) - 1;
+
+ while ((p > section_name) && PROFILE_isspace(*p))
+ p--;
+
+ seclen = p - section_name + 1;
+
+ while (PROFILE_isspace(*key_name))
+ key_name++;
+
+ p = key_name + strlen(key_name) - 1;
+
+ while ((p > key_name) && PROFILE_isspace(*p))
+ p--;
+
+ keylen = p - key_name + 1;
+
+ while (*section) {
+ if (((*section)->name[0])
+ && (!(_strnicmp((*section)->name, section_name, seclen )))
+ && (((*section)->name)[seclen] == '\0')) {
+ PROFILEKEY* *key = &(*section)->key;
+
+ while (*key) {
+ /* If create_always is FALSE then we check if the keyname already exists.
+ * Otherwise we add it regardless of its existence, to allow
+ * keys to be added more then once in some cases.
+ */
+ if (!create_always) {
+ if ((!(_strnicmp( (*key)->name, key_name, keylen )))
+ && (((*key)->name)[keylen] == '\0'))
+ return *key;
+
+ } // endif !create_always
+
+ key = &(*key)->next;
+ } // endwhile *key
+
+ if (!create)
+ return NULL;
+
+ if (!(*key = (PROFILEKEY*)malloc(sizeof(PROFILEKEY) + strlen(key_name))))
+ return NULL;
+
+ strcpy((*key)->name, key_name);
+ (*key)->value = NULL;
+ (*key)->next = NULL;
+ return *key;
+ } // endifsection->name
+
+ section = &(*section)->next;
+ } // endwhile *section
+
+ if (!create)
+ return NULL;
+
+ *section = (PROFILESECTION*)malloc(sizeof(PROFILESECTION) + strlen(section_name));
+
+ if (*section == NULL)
+ return NULL;
+
+ strcpy((*section)->name, section_name);
+ (*section)->next = NULL;
+
+ if (!((*section)->key = (tagPROFILEKEY*)malloc(sizeof(PROFILEKEY) + strlen(key_name)))) {
+ free(*section);
+ return NULL;
+ } // endif malloc
+
+ strcpy((*section)->key->name, key_name);
+ (*section)->key->value = NULL;
+ (*section)->key->next = NULL;
+ return (*section)->key;
+} // end of PROFILE_Find
+
+
+/***********************************************************************
+ * PROFILE_GetSection
+ *
+ * Returns all keys of a section.
+ * If return_values is TRUE, also include the corresponding values.
+ ***********************************************************************/
+static int PROFILE_GetSection(PROFILESECTION *section, LPCSTR section_name,
+ LPSTR buffer, uint len,
+ BOOL handle_env, BOOL return_values)
+{
+ PROFILEKEY *key;
+
+ if(!buffer)
+ return 0;
+
+ while (section) {
+ if (section->name[0] && !stricmp(section->name, section_name)) {
+ uint oldlen = len;
+
+ for (key = section->key; key; key = key->next) {
+ if (len <= 2)
+ break;
+
+ if (!*key->name)
+ continue; /* Skip empty lines */
+
+ if (IS_ENTRY_COMMENT(key->name))
+ continue; /* Skip comments */
+
+ PROFILE_CopyEntry(buffer, key->name, len - 1, handle_env);
+ len -= strlen(buffer) + 1;
+ buffer += strlen(buffer) + 1;
+
+ if (len < 2)
+ break;
+
+ if (return_values && key->value) {
+ buffer[-1] = '=';
+ PROFILE_CopyEntry(buffer, key->value, len - 1, handle_env);
+ len -= strlen(buffer) + 1;
+ buffer += strlen(buffer) + 1;
+ } // endif return_values
+
+ } // endfor key
+
+ *buffer = '\0';
+
+ if (len <= 1) {
+ /*If either lpszSection or lpszKey is NULL and the supplied
+ destination buffer is too small to hold all the strings,
+ the last string is truncated and followed by two null characters.
+ In this case, the return value is equal to cchReturnBuffer
+ minus two. */
+ buffer[-1] = '\0';
+ return oldlen - 2;
+ } // endif len
+
+ return oldlen - len;
+ } // endif section->name
+
+ section = section->next;
+ } // endwhile section
+
+ buffer[0] = buffer[1] = '\0';
+ return 0;
+} // end of PROFILE_GetSection
+
+
+/* See GetPrivateProfileSectionNamesA for documentation */
+static int PROFILE_GetSectionNames(LPSTR buffer, uint len)
+{
+ LPSTR buf;
+ uint f,l;
+ PROFILESECTION *section;
+
+ if (trace > 1)
+ htrc("GetSectionNames: buffer=%p len=%u\n", buffer, len);
+
+ if (!buffer || !len)
+ return 0;
+
+ if (len == 1) {
+ *buffer='\0';
+ return 0;
+ } // endif len
+
+ f = len - 1;
+ buf = buffer;
+ section = CurProfile->section;
+
+ if (trace > 1)
+ htrc("GetSectionNames: section=%p\n", section);
+
+ while (section != NULL) {
+ if (trace > 1)
+ htrc("section=%s\n", section->name);
+
+ if (section->name[0]) {
+ l = strlen(section->name) + 1;
+
+ if (trace > 1)
+ htrc("l=%u f=%u\n", l, f);
+
+ if (l > f) {
+ if (f > 0) {
+ strncpy(buf, section->name, f-1);
+ buf += f-1;
+ *buf++='\0';
+ } // endif f
+
+ *buf = '\0';
+ return len - 2;
+ } // endif l
+
+ strcpy(buf, section->name);
+ buf += l;
+ f -= l;
+ } // endif section->name
+
+ section = section->next;
+ } // endwhile section
+
+ *buf='\0';
+ return buf-buffer;
+} // end of PROFILE_GetSectionNames
+
+
+/***********************************************************************
+ * PROFILE_GetString
+ *
+ * Get a profile string.
+ *
+ * Tests with GetPrivateProfileString16, W95a,
+ * with filled buffer ("****...") and section "set1" and key_name "1" valid:
+ * section key_name def_val res buffer
+ * "set1" "1" "x" 43 [data]
+ * "set1" "1 " "x" 43 [data] (!)
+ * "set1" " 1 "' "x" 43 [data] (!)
+ * "set1" "" "x" 1 "x"
+ * "set1" "" "x " 1 "x" (!)
+ * "set1" "" " x " 3 " x" (!)
+ * "set1" NULL "x" 6 "1\02\03\0\0"
+ * "set1" "" "x" 1 "x"
+ * NULL "1" "x" 0 "" (!)
+ * "" "1" "x" 1 "x"
+ * NULL NULL "" 0 ""
+ *
+ *************************************************************************/
+static int PROFILE_GetString(LPCSTR section, LPCSTR key_name,
+ LPCSTR def_val, LPSTR buffer, uint len)
+{
+ PROFILEKEY *key = NULL;
+
+ if(!buffer)
+ return 0;
+
+ if (!def_val)
+ def_val = "";
+
+ if (key_name && key_name[0]) {
+ key = PROFILE_Find(&CurProfile->section, section, key_name, FALSE, FALSE);
+ PROFILE_CopyEntry(buffer, (key && key->value) ? key->value : def_val, len, FALSE);
+
+ if (trace > 1)
+ htrc("('%s','%s','%s'): returning '%s'\n",
+ section, key_name, def_val, buffer );
+
+ return strlen(buffer);
+ } // endif key_name
+
+ if (key_name && !(key_name[0]))
+ /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
+ return 0;
+
+ if (section && section[0])
+ return PROFILE_GetSection(CurProfile->section, section, buffer, len,
+ FALSE, FALSE);
+ buffer[0] = '\0';
+ return 0;
+} // end of PROFILE_GetString
+
+
+/***********************************************************************
+ * PROFILE_SetString
+ *
+ * Set a profile string.
+ ***********************************************************************/
+static BOOL PROFILE_SetString(LPCSTR section_name, LPCSTR key_name,
+ LPCSTR value, BOOL create_always)
+{
+ if (!key_name) { /* Delete a whole section */
+ if (trace > 1)
+ htrc("Deleting('%s')\n", section_name);
+
+ CurProfile->changed |= PROFILE_DeleteSection(&CurProfile->section,
+ section_name);
+ return TRUE; /* Even if PROFILE_DeleteSection() has failed,
+ this is not an error on application's level.*/
+ } else if (!value) { /* Delete a key */
+ if (trace > 1)
+ htrc("Deleting('%s','%s')\n", section_name, key_name);
+
+ CurProfile->changed |= PROFILE_DeleteKey(&CurProfile->section,
+ section_name, key_name);
+ return TRUE; /* same error handling as above */
+ } else { /* Set the key value */
+ PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
+ key_name, TRUE, create_always);
+ if (trace > 1)
+ htrc("Setting('%s','%s','%s')\n", section_name, key_name, value);
+
+ if (!key)
+ return FALSE;
+
+ if (key->value) {
+ /* strip the leading spaces. We can safely strip \n\r and
+ * friends too, they should not happen here anyway. */
+ while (PROFILE_isspace(*value))
+ value++;
+
+ if (!strcmp(key->value, value)) {
+ if (trace > 1)
+ htrc(" no change needed\n" );
+
+ return TRUE; /* No change needed */
+ } // endif value
+
+ if (trace > 1)
+ htrc(" replacing '%s'\n", key->value);
+
+ free(key->value);
+ } else if (trace > 1)
+ htrc(" creating key\n" );
+
+ key->value = (char*)malloc(strlen(value) + 1);
+ strcpy(key->value, value);
+ CurProfile->changed = TRUE;
+ } // endelse
+
+ return TRUE;
+} // end of PROFILE_SetString
+
+
+/***********************************************************************
+ * PROFILE_GetStringItem
+ *
+ * Convenience function that turns a string 'xxx, yyy, zzz' into
+ * the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'.
+ ***********************************************************************/
+#if 0
+char *PROFILE_GetStringItem(char* start)
+{
+ char *lpchX, *lpch;
+
+ for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++) {
+ if (*lpchX == ',') {
+ if (lpch)
+ *lpch = '\0';
+ else
+ *lpchX = '\0';
+
+ while(*(++lpchX))
+ if (!PROFILE_isspace(*lpchX))
+ return lpchX;
+
+ } else if (PROFILE_isspace(*lpchX) && !lpch) {
+ lpch = lpchX;
+ } else
+ lpch = NULL;
+
+ } // endfor lpchX
+
+ if (lpch)
+ *lpch = '\0';
+
+ return NULL;
+} // end of PROFILE_GetStringItem
+#endif
+
+/**********************************************************************
+ * if allow_section_name_copy is TRUE, allow the copying :
+ * - of Section names if 'section' is NULL
+ * - of Keys in a Section if 'entry' is NULL
+ * (see MSDN doc for GetPrivateProfileString)
+ **********************************************************************/
+static int PROFILE_GetPrivateProfileString(LPCSTR section, LPCSTR entry,
+ LPCSTR def_val, LPSTR buffer,
+ uint len, LPCSTR filename,
+ BOOL allow_section_name_copy)
+{
+ int ret;
+ LPSTR pDefVal = NULL;
+
+ if (!filename)
+ filename = "win.ini";
+
+ /* strip any trailing ' ' of def_val. */
+ if (def_val) {
+ LPSTR p = (LPSTR)&def_val[strlen(def_val)]; // even "" works !
+
+ while (p > def_val)
+ if ((*(--p)) != ' ')
+ break;
+
+ if (*p == ' ') { /* ouch, contained trailing ' ' */
+ int len = p - (LPSTR)def_val;
+
+ pDefVal = (LPSTR)malloc(len + 1);
+ strncpy(pDefVal, def_val, len);
+ pDefVal[len] = '\0';
+ } // endif *p
+
+ } // endif def_val
+
+ if (!pDefVal)
+ pDefVal = (LPSTR)def_val;
+
+ EnterCriticalSection(&PROFILE_CritSect);
+
+ if (PROFILE_Open(filename)) {
+ if ((allow_section_name_copy) && (section == NULL))
+ ret = PROFILE_GetSectionNames(buffer, len);
+ else
+ /* PROFILE_GetString already handles the 'entry == NULL' case */
+ ret = PROFILE_GetString(section, entry, pDefVal, buffer, len);
+
+ } else {
+ strncpy(buffer, pDefVal, len);
+ ret = strlen(buffer);
+ } // endif Open
+
+ LeaveCriticalSection(&PROFILE_CritSect);
+
+ if (pDefVal != def_val) /* allocated */
+ memfree(pDefVal);
+
+ return ret;
+} // end of PROFILE_GetPrivateProfileString
+
+/********************** API functions **********************************/
+
+/***********************************************************************
+ * GetPrivateProfileStringA (KERNEL32.@)
+ ***********************************************************************/
+int GetPrivateProfileString(LPCSTR section, LPCSTR entry, LPCSTR def_val,
+ LPSTR buffer, DWORD len, LPCSTR filename)
+{
+ return PROFILE_GetPrivateProfileString(section, entry, def_val,
+ buffer, len, filename, TRUE);
+} // end of GetPrivateProfileString
+
+
+/***********************************************************************
+ * GetPrivateProfileIntA (KERNEL32.@)
+ ***********************************************************************/
+uint GetPrivateProfileInt(LPCSTR section, LPCSTR entry,
+ int def_val, LPCSTR filename)
+{
+ char buffer[20];
+ int result;
+
+ if (!PROFILE_GetPrivateProfileString(section, entry, "", buffer,
+ sizeof(buffer), filename, FALSE))
+ return def_val;
+
+ /* FIXME: if entry can be found but it's empty, then Win16 is
+ * supposed to return 0 instead of def_val ! Difficult/problematic
+ * to implement (every other failure also returns zero buffer),
+ * thus wait until testing framework avail for making sure nothing
+ * else gets broken that way. */
+ if (!buffer[0])
+ return (uint)def_val;
+
+ /* Don't use strtol() here !
+ * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
+ YES, scan for unsigned format ! (otherwise compatibility error) */
+ if (!sscanf(buffer, "%u", &result))
+ return 0;
+
+ return (uint)result;
+} // end of GetPrivateProfileInt
+
+
+/***********************************************************************
+ * GetPrivateProfileSectionA (KERNEL32.@)
+ ***********************************************************************/
+int GetPrivateProfileSection(LPCSTR section, LPSTR buffer,
+ DWORD len, LPCSTR filename)
+{
+ int ret = 0;
+
+ EnterCriticalSection( &PROFILE_CritSect );
+
+ if (PROFILE_Open(filename))
+ ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
+ FALSE, TRUE);
+
+ LeaveCriticalSection( &PROFILE_CritSect );
+ return ret;
+} // end of GetPrivateProfileSection
+
+
+/***********************************************************************
+ * WritePrivateProfileStringA (KERNEL32.@)
+ ***********************************************************************/
+BOOL WritePrivateProfileString(LPCSTR section, LPCSTR entry,
+ LPCSTR string, LPCSTR filename)
+{
+ BOOL ret = FALSE;
+
+ EnterCriticalSection( &PROFILE_CritSect );
+
+ if (PROFILE_Open(filename)) {
+ if (!section && !entry && !string) /* documented "file flush" case */
+ PROFILE_ReleaseFile(); /* always return FALSE in this case */
+ else {
+ if (!section) {
+ //FIXME("(NULL?,%s,%s,%s)? \n",entry,string,filename);
+ } else {
+ ret = PROFILE_SetString(section, entry, string, FALSE);
+
+ if (ret)
+ ret = PROFILE_FlushFile();
+
+ } // endif section
+
+ } // endif section || entry|| string
+
+ } // endif Open
+
+ LeaveCriticalSection( &PROFILE_CritSect );
+ return ret;
+} // end of WritePrivateProfileString
+
+
+/***********************************************************************
+ * WritePrivateProfileSectionA (KERNEL32.@)
+ ***********************************************************************/
+BOOL WritePrivateProfileSection(LPCSTR section,
+ LPCSTR string, LPCSTR filename )
+{
+ BOOL ret = FALSE;
+ LPSTR p ;
+
+ EnterCriticalSection(&PROFILE_CritSect);
+
+ if (PROFILE_Open(filename)) {
+ if (!section && !string)
+ PROFILE_ReleaseFile(); /* always return FALSE in this case */
+ else if (!string) { /* delete the named section*/
+ ret = PROFILE_SetString(section, NULL, NULL, FALSE);
+
+ if (ret)
+ ret = PROFILE_FlushFile();
+ } else {
+ PROFILE_DeleteAllKeys(section);
+ ret = TRUE;
+
+ while (*string) {
+ LPSTR buf = (LPSTR)malloc(strlen(string) + 1);
+ strcpy(buf, string);
+
+ if ((p = strchr(buf, '='))) {
+ *p='\0';
+ ret = PROFILE_SetString(section, buf, p+1, TRUE);
+ } // endif p
+
+ free(buf);
+ string += strlen(string) + 1;
+
+ if (ret)
+ ret = PROFILE_FlushFile();
+
+ } // endwhile *string
+
+ } // endelse
+
+ } // endif Open
+
+ LeaveCriticalSection(&PROFILE_CritSect);
+ return ret;
+} // end of WritePrivateProfileSection
+
+
+/***********************************************************************
+ * GetPrivateProfileSectionNamesA (KERNEL32.@)
+ *
+ * Returns the section names contained in the specified file.
+ * FIXME: Where do we find this file when the path is relative?
+ * The section names are returned as a list of strings with an extra
+ * '\0' to mark the end of the list. Except for that the behavior
+ * depends on the Windows version.
+ *
+ * Win95:
+ * - if the buffer is 0 or 1 character long then it is as if it was of
+ * infinite length.
+ * - otherwise, if the buffer is to small only the section names that fit
+ * are returned.
+ * - note that this means if the buffer was to small to return even just
+ * the first section name then a single '\0' will be returned.
+ * - the return value is the number of characters written in the buffer,
+ * except if the buffer was too smal in which case len-2 is returned
+ *
+ * Win2000:
+ * - if the buffer is 0, 1 or 2 characters long then it is filled with
+ * '\0' and the return value is 0
+ * - otherwise if the buffer is too small then the first section name that
+ * does not fit is truncated so that the string list can be terminated
+ * correctly (double '\0')
+ * - the return value is the number of characters written in the buffer
+ * except for the trailing '\0'. If the buffer is too small, then the
+ * return value is len-2
+ * - Win2000 has a bug that triggers when the section names and the
+ * trailing '\0' fit exactly in the buffer. In that case the trailing
+ * '\0' is missing.
+ *
+ * Wine implements the observed Win2000 behavior (except for the bug).
+ *
+ * Note that when the buffer is big enough then the return value may be any
+ * value between 1 and len-1 (or len in Win95), including len-2.
+ */
+static DWORD
+GetPrivateProfileSectionNames(LPSTR buffer, DWORD size, LPCSTR filename)
+{
+ DWORD ret = 0;
+
+ if (trace > 1)
+ htrc("GPPSN: filename=%s\n", filename);
+
+ EnterCriticalSection(&PROFILE_CritSect);
+
+ if (PROFILE_Open(filename))
+ ret = PROFILE_GetSectionNames(buffer, size);
+
+ LeaveCriticalSection(&PROFILE_CritSect);
+ return ret;
+} // end of GetPrivateProfileSectionNames
+
+
+/************************************************************************
+ * Program to test the above
+ ************************************************************************/
+#ifdef TEST_MODULE
+int main(int argc, char**argv) {
+ char buff[128];
+ char *p, *inifile = "D:\\Plug\\Data\\contact.ini";
+ DWORD n;
+
+ n = GetPrivateProfileSectionNames(buff, 128, inifile);
+ printf("Sections: n=%d\n", n);
+
+ for (p = buff; *p; p += (strlen(p) + 1))
+ printf("section=[%s]\n", p);
+
+ GetPrivateProfileString("BER", "name", "?", buff, 128, inifile);
+ printf("[BER](name) = %s\n", buff);
+
+ WritePrivateProfileString("FOO", "city", NULL, inifile);
+ GetPrivateProfileString("FOO", "city", "?", buff, 128, inifile);
+ printf("[FOO](city) = %s\n", buff);
+
+ printf("FOO city: ");
+ fgets(buff, sizeof(buff), stdin);
+ if (buff[strlen(buff) - 1] == '\n')
+ buff[strlen(buff) - 1] = '\0';
+ WritePrivateProfileString("FOO", "city", buff, inifile);
+ GetPrivateProfileString("FOO", "city", "???", buff, 128, inifile);
+ printf("After write, [FOO](City) = %s\n", buff);
+
+ printf("New city: ");
+ fgets(buff, sizeof(buff), stdin);
+ if (buff[strlen(buff) - 1] == '\n')
+ buff[strlen(buff) - 1] = '\0';
+ WritePrivateProfileString("FOO", "city", buff, inifile);
+ GetPrivateProfileString("FOO", "city", "???", buff, 128, inifile);
+ printf("After update, [FOO](City) = %s\n", buff);
+
+ printf("FOO name: ");
+ fgets(buff, sizeof(buff), stdin);
+ if (buff[strlen(buff) - 1] == '\n')
+ buff[strlen(buff) - 1] = '\0';
+ WritePrivateProfileString("FOO", "name", buff, inifile);
+ GetPrivateProfileString("FOO", "name", "X", buff, 128, inifile);
+ printf("[FOO](name) = %s\n", buff);
+} // end of main
+#endif // TEST_MODULE