mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
1405 lines
40 KiB
C++
1405 lines
40 KiB
C++
/*
|
|
* 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 02110-1335 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");
|
|
|
|
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[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 = §ion->next;
|
|
next_key = §ion->key;
|
|
prev_key = NULL;
|
|
|
|
if (trace(2))
|
|
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(2))
|
|
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(2))
|
|
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(2))
|
|
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(2))
|
|
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(2))
|
|
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(2))
|
|
htrc("(%s): already opened (mru=%d)\n", filename, i);
|
|
|
|
} else {
|
|
if (trace(2))
|
|
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(2))
|
|
htrc("Opening %s\n", filename);
|
|
|
|
if ((file = fopen(filename, "r"))) {
|
|
if (trace(2))
|
|
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(2))
|
|
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(2))
|
|
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(2)) {
|
|
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(3))
|
|
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(3))
|
|
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(2))
|
|
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(2))
|
|
htrc("GetSectionNames: section=%p\n", section);
|
|
|
|
while (section != NULL) {
|
|
if (trace(2))
|
|
htrc("section=%s\n", section->name);
|
|
|
|
if (section->name[0]) {
|
|
l = strlen(section->name) + 1;
|
|
|
|
if (trace(2))
|
|
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(2))
|
|
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(2))
|
|
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(2))
|
|
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(2))
|
|
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(2))
|
|
htrc(" no change needed\n" );
|
|
|
|
return TRUE; /* No change needed */
|
|
} // endif value
|
|
|
|
if (trace(2))
|
|
htrc(" replacing '%s'\n", key->value);
|
|
|
|
free(key->value);
|
|
} else if (trace(2))
|
|
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.
|
|
*/
|
|
#ifdef TEST_MODULE
|
|
static DWORD
|
|
GetPrivateProfileSectionNames(LPSTR buffer, DWORD size, LPCSTR filename)
|
|
{
|
|
DWORD ret = 0;
|
|
|
|
if (trace(2))
|
|
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
|
|
************************************************************************/
|
|
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
|