ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

profile.c
Go to the documentation of this file.
00001 /*
00002  * Profile functions
00003  *
00004  * Copyright 1993 Miguel de Icaza
00005  * Copyright 1996 Alexandre Julliard
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00020  */
00021 
00022 //#include "config.h"
00023 //#include "wine/port.h"
00024 
00025 #include <string.h>
00026 #include <stdarg.h>
00027 
00028 #include "windef.h"
00029 #include "winbase.h"
00030 #include "winnls.h"
00031 #include "winerror.h"
00032 #include "winternl.h"
00033 #include "wine/unicode.h"
00034 #include "wine/library.h"
00035 #include "wine/debug.h"
00036 
00037 #define HeapAlloc RtlAllocateHeap
00038 #define HeapReAlloc RtlReAllocateHeap
00039 #define HeapFree RtlFreeHeap
00040 WINE_DEFAULT_DEBUG_CHANNEL(profile);
00041 
00042 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
00043 
00044 typedef enum
00045 {
00046     ENCODING_ANSI = 1,
00047     ENCODING_UTF8,
00048     ENCODING_UTF16LE,
00049     ENCODING_UTF16BE
00050 } ENCODING;
00051 
00052 typedef struct tagPROFILEKEY
00053 {
00054     WCHAR                 *value;
00055     struct tagPROFILEKEY  *next;
00056     WCHAR                  name[1];
00057 } PROFILEKEY;
00058 
00059 typedef struct tagPROFILESECTION
00060 {
00061     struct tagPROFILEKEY       *key;
00062     struct tagPROFILESECTION   *next;
00063     WCHAR                       name[1];
00064 } PROFILESECTION;
00065 
00066 
00067 typedef struct
00068 {
00069     BOOL             changed;
00070     PROFILESECTION  *section;
00071     WCHAR           *filename;
00072     FILETIME LastWriteTime;
00073     ENCODING encoding;
00074 } PROFILE;
00075 
00076 
00077 #define N_CACHED_PROFILES 10
00078 
00079 /* Cached profile files */
00080 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
00081 
00082 #define CurProfile (MRUProfile[0])
00083 
00084 /* Check for comments in profile */
00085 #define IS_ENTRY_COMMENT(str)  ((str)[0] == ';')
00086 
00087 static const WCHAR emptystringW[] = {0};
00088 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
00089 
00090 static RTL_CRITICAL_SECTION PROFILE_CritSect;
00091 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
00092 {
00093     0, 0, &PROFILE_CritSect,
00094     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
00095       0, 0, 0
00096 };
00097 static RTL_CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
00098 
00099 static const char hex[16] = "0123456789ABCDEF";
00100 
00101 /***********************************************************************
00102  *           PROFILE_CopyEntry
00103  *
00104  * Copy the content of an entry into a buffer, removing quotes, and possibly
00105  * translating environment variables.
00106  */
00107 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
00108                                BOOL strip_quote )
00109 {
00110     WCHAR quote = '\0';
00111 
00112     if(!buffer) return;
00113 
00114     if (strip_quote && ((*value == '\'') || (*value == '\"')))
00115     {
00116         if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
00117     }
00118 
00119     lstrcpynW( buffer, value, len );
00120     if (quote && (len >= lstrlenW(value))) buffer[strlenW(buffer)-1] = '\0';
00121 }
00122 
00123 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
00124 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
00125 {
00126     int i;
00127     USHORT * shortbuffer = buffer;
00128     for (i = 0; i < len; i++)
00129         shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
00130 }
00131 
00132 /* writes any necessary encoding marker to the file */
00133 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
00134 {
00135     DWORD dwBytesWritten;
00136     WCHAR bom;
00137     switch (encoding)
00138     {
00139     case ENCODING_ANSI:
00140         break;
00141     case ENCODING_UTF8:
00142         WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
00143         break;
00144     case ENCODING_UTF16LE:
00145         bom = 0xFEFF;
00146         WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
00147         break;
00148     case ENCODING_UTF16BE:
00149         bom = 0xFFFE;
00150         WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
00151         break;
00152     }
00153 }
00154 
00155 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
00156 {
00157     char * write_buffer;
00158     int write_buffer_len;
00159     DWORD dwBytesWritten;
00160 
00161     TRACE("writing: %s\n", debugstr_wn(szLine, len));
00162 
00163     switch (encoding)
00164     {
00165     case ENCODING_ANSI:
00166         write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
00167         write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
00168         if (!write_buffer) return;
00169         len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
00170         WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
00171         HeapFree(GetProcessHeap(), 0, write_buffer);
00172         break;
00173     case ENCODING_UTF8:
00174         write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
00175         write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
00176         if (!write_buffer) return;
00177         len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
00178         WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
00179         HeapFree(GetProcessHeap(), 0, write_buffer);
00180         break;
00181     case ENCODING_UTF16LE:
00182         WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
00183         break;
00184     case ENCODING_UTF16BE:
00185         PROFILE_ByteSwapShortBuffer(szLine, len);
00186         WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
00187         break;
00188     default:
00189         FIXME("encoding type %d not implemented\n", encoding);
00190     }
00191 }
00192 
00193 /***********************************************************************
00194  *           PROFILE_Save
00195  *
00196  * Save a profile tree to a file.
00197  */
00198 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
00199 {
00200     PROFILEKEY *key;
00201     WCHAR *buffer, *p;
00202 
00203     PROFILE_WriteMarker(hFile, encoding);
00204 
00205     for ( ; section; section = section->next)
00206     {
00207         int len = 0;
00208 
00209         if (section->name[0]) len += strlenW(section->name) + 4;
00210 
00211         for (key = section->key; key; key = key->next)
00212         {
00213             len += strlenW(key->name) + 2;
00214             if (key->value) len += strlenW(key->value) + 1;
00215         }
00216 
00217         buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
00218         if (!buffer) return;
00219 
00220         p = buffer;
00221         if (section->name[0])
00222         {
00223             *p++ = '[';
00224             strcpyW( p, section->name );
00225             p += strlenW(p);
00226             *p++ = ']';
00227             *p++ = '\r';
00228             *p++ = '\n';
00229         }
00230 
00231         for (key = section->key; key; key = key->next)
00232         {
00233             strcpyW( p, key->name );
00234             p += strlenW(p);
00235             if (key->value)
00236             {
00237                 *p++ = '=';
00238                 strcpyW( p, key->value );
00239                 p += strlenW(p);
00240             }
00241             *p++ = '\r';
00242             *p++ = '\n';
00243         }
00244         PROFILE_WriteLine( hFile, buffer, len, encoding );
00245         HeapFree(GetProcessHeap(), 0, buffer);
00246     }
00247 }
00248 
00249 
00250 /***********************************************************************
00251  *           PROFILE_Free
00252  *
00253  * Free a profile tree.
00254  */
00255 static void PROFILE_Free( PROFILESECTION *section )
00256 {
00257     PROFILESECTION *next_section;
00258     PROFILEKEY *key, *next_key;
00259 
00260     for ( ; section; section = next_section)
00261     {
00262         for (key = section->key; key; key = next_key)
00263         {
00264             next_key = key->next;
00265             HeapFree( GetProcessHeap(), 0, key->value );
00266             HeapFree( GetProcessHeap(), 0, key );
00267         }
00268         next_section = section->next;
00269         HeapFree( GetProcessHeap(), 0, section );
00270     }
00271 }
00272 
00273 /* returns 1 if a character white space else 0 */
00274 static inline int PROFILE_isspaceW(WCHAR c)
00275 {
00276     /* ^Z (DOS EOF) is a space too  (found on CD-ROMs) */
00277     return isspaceW(c) || c == 0x1a;
00278 }
00279 
00280 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
00281 {
00282     int flags = IS_TEXT_UNICODE_SIGNATURE |
00283                 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
00284                 IS_TEXT_UNICODE_ODD_LENGTH;
00285     if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
00286     {
00287         *len = sizeof(bom_utf8);
00288         return ENCODING_UTF8;
00289     }
00290     RtlIsTextUnicode(buffer, *len, &flags);
00291     if (flags & IS_TEXT_UNICODE_SIGNATURE)
00292     {
00293         *len = sizeof(WCHAR);
00294         return ENCODING_UTF16LE;
00295     }
00296     if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
00297     {
00298         *len = sizeof(WCHAR);
00299         return ENCODING_UTF16BE;
00300     }
00301     *len = 0;
00302     return ENCODING_ANSI;
00303 }
00304 
00305 
00306 /***********************************************************************
00307  *           PROFILE_Load
00308  *
00309  * Load a profile tree from a file.
00310  */
00311 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
00312 {
00313     void *buffer_base, *pBuffer;
00314     WCHAR * szFile;
00315     const WCHAR *szLineStart, *szLineEnd;
00316     const WCHAR *szValueStart, *szEnd, *next_line;
00317     int line = 0, len;
00318     PROFILESECTION *section, *first_section;
00319     PROFILESECTION **next_section;
00320     PROFILEKEY *key, *prev_key, **next_key;
00321     DWORD dwFileSize;
00322     
00323     TRACE("%p\n", hFile);
00324     
00325     dwFileSize = GetFileSize(hFile, NULL);
00326     if (dwFileSize == INVALID_FILE_SIZE || dwFileSize == 0)
00327         return NULL;
00328 
00329     buffer_base = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
00330     if (!buffer_base) return NULL;
00331     
00332     if (!ReadFile(hFile, buffer_base, dwFileSize, &dwFileSize, NULL))
00333     {
00334         HeapFree(GetProcessHeap(), 0, buffer_base);
00335         WARN("Error %d reading file\n", GetLastError());
00336         return NULL;
00337     }
00338     len = dwFileSize;
00339     *pEncoding = PROFILE_DetectTextEncoding(buffer_base, &len);
00340     /* len is set to the number of bytes in the character marker.
00341      * we want to skip these bytes */
00342     pBuffer = (char *)buffer_base + len;
00343     dwFileSize -= len;
00344     switch (*pEncoding)
00345     {
00346     case ENCODING_ANSI:
00347         TRACE("ANSI encoding\n");
00348 
00349         len = MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, NULL, 0);
00350         szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
00351         if (!szFile)
00352         {
00353             HeapFree(GetProcessHeap(), 0, buffer_base);
00354             return NULL;
00355         }
00356         MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, szFile, len);
00357         szEnd = szFile + len;
00358         break;
00359     case ENCODING_UTF8:
00360         TRACE("UTF8 encoding\n");
00361 
00362         len = MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, NULL, 0);
00363         szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
00364         if (!szFile)
00365         {
00366             HeapFree(GetProcessHeap(), 0, buffer_base);
00367             return NULL;
00368         }
00369         MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, szFile, len);
00370         szEnd = szFile + len;
00371         break;
00372     case ENCODING_UTF16LE:
00373         TRACE("UTF16 Little Endian encoding\n");
00374         szFile = pBuffer;
00375         szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
00376         break;
00377     case ENCODING_UTF16BE:
00378         TRACE("UTF16 Big Endian encoding\n");
00379         szFile = pBuffer;
00380         szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
00381         PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
00382         break;
00383     default:
00384         FIXME("encoding type %d not implemented\n", *pEncoding);
00385         HeapFree(GetProcessHeap(), 0, buffer_base);
00386         return NULL;
00387     }
00388 
00389     first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
00390     if(first_section == NULL)
00391     {
00392         if (szFile != pBuffer)
00393             HeapFree(GetProcessHeap(), 0, szFile);
00394         HeapFree(GetProcessHeap(), 0, buffer_base);
00395         return NULL;
00396     }
00397     first_section->name[0] = 0;
00398     first_section->key  = NULL;
00399     first_section->next = NULL;
00400     next_section = &first_section->next;
00401     next_key     = &first_section->key;
00402     prev_key     = NULL;
00403     next_line    = szFile;
00404 
00405     while (next_line < szEnd)
00406     {
00407         szLineStart = next_line;
00408         next_line = memchrW(szLineStart, '\n', szEnd - szLineStart);
00409         if (!next_line) next_line = memchrW(szLineStart, '\r', szEnd - szLineStart);
00410         if (!next_line) next_line = szEnd;
00411         else next_line++;
00412         szLineEnd = next_line;
00413 
00414         line++;
00415 
00416         /* get rid of white space */
00417         while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
00418         while ((szLineEnd > szLineStart) && PROFILE_isspaceW(szLineEnd[-1])) szLineEnd--;
00419 
00420         if (szLineStart >= szLineEnd) continue;
00421 
00422         if (*szLineStart == '[')  /* section start */
00423         {
00424             const WCHAR * szSectionEnd;
00425             if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
00426             {
00427                 WARN("Invalid section header at line %d: %s\n",
00428                     line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
00429             }
00430             else
00431             {
00432                 szLineStart++;
00433                 len = (int)(szSectionEnd - szLineStart);
00434                 /* no need to allocate +1 for NULL terminating character as
00435                  * already included in structure */
00436                 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
00437                     break;
00438                 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
00439                 section->name[len] = '\0';
00440                 section->key  = NULL;
00441                 section->next = NULL;
00442                 *next_section = section;
00443                 next_section  = &section->next;
00444                 next_key      = &section->key;
00445                 prev_key      = NULL;
00446 
00447                 TRACE("New section: %s\n", debugstr_w(section->name));
00448 
00449                 continue;
00450             }
00451         }
00452 
00453         /* get rid of white space after the name and before the start
00454          * of the value */
00455         len = szLineEnd - szLineStart;
00456         if ((szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
00457         {
00458             const WCHAR *szNameEnd = szValueStart;
00459             while ((szNameEnd > szLineStart) && PROFILE_isspaceW(szNameEnd[-1])) szNameEnd--;
00460             len = szNameEnd - szLineStart;
00461             szValueStart++;
00462             while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
00463         }
00464 
00465         if (len || !prev_key || *prev_key->name)
00466         {
00467             /* no need to allocate +1 for NULL terminating character as
00468              * already included in structure */
00469             if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
00470             memcpy(key->name, szLineStart, len * sizeof(WCHAR));
00471             key->name[len] = '\0';
00472             if (szValueStart)
00473             {
00474                 len = (int)(szLineEnd - szValueStart);
00475                 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
00476                 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
00477                 key->value[len] = '\0';
00478             }
00479             else key->value = NULL;
00480 
00481            key->next  = NULL;
00482            *next_key  = key;
00483            next_key   = &key->next;
00484            prev_key   = key;
00485 
00486            TRACE("New key: name=%s, value=%s\n",
00487                debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
00488         }
00489     }
00490     if (szFile != pBuffer)
00491         HeapFree(GetProcessHeap(), 0, szFile);
00492     HeapFree(GetProcessHeap(), 0, buffer_base);
00493     return first_section;
00494 }
00495 
00496 
00497 /***********************************************************************
00498  *           PROFILE_DeleteSection
00499  *
00500  * Delete a section from a profile tree.
00501  */
00502 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
00503 {
00504     while (*section)
00505     {
00506         if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
00507         {
00508             PROFILESECTION *to_del = *section;
00509             *section = to_del->next;
00510             to_del->next = NULL;
00511             PROFILE_Free( to_del );
00512             return TRUE;
00513         }
00514         section = &(*section)->next;
00515     }
00516     return FALSE;
00517 }
00518 
00519 
00520 /***********************************************************************
00521  *           PROFILE_DeleteKey
00522  *
00523  * Delete a key from a profile tree.
00524  */
00525 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
00526                    LPCWSTR section_name, LPCWSTR key_name )
00527 {
00528     while (*section)
00529     {
00530         if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
00531         {
00532             PROFILEKEY **key = &(*section)->key;
00533             while (*key)
00534             {
00535                 if (!strcmpiW( (*key)->name, key_name ))
00536                 {
00537                     PROFILEKEY *to_del = *key;
00538                     *key = to_del->next;
00539                     HeapFree( GetProcessHeap(), 0, to_del->value);
00540                     HeapFree( GetProcessHeap(), 0, to_del );
00541                     return TRUE;
00542                 }
00543                 key = &(*key)->next;
00544             }
00545         }
00546         section = &(*section)->next;
00547     }
00548     return FALSE;
00549 }
00550 
00551 
00552 /***********************************************************************
00553  *           PROFILE_DeleteAllKeys
00554  *
00555  * Delete all keys from a profile tree.
00556  */
00557 static void PROFILE_DeleteAllKeys( LPCWSTR section_name)
00558 {
00559     PROFILESECTION **section= &CurProfile->section;
00560     while (*section)
00561     {
00562         if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
00563         {
00564             PROFILEKEY **key = &(*section)->key;
00565             while (*key)
00566             {
00567                 PROFILEKEY *to_del = *key;
00568         *key = to_del->next;
00569                 HeapFree( GetProcessHeap(), 0, to_del->value);
00570         HeapFree( GetProcessHeap(), 0, to_del );
00571         CurProfile->changed =TRUE;
00572             }
00573         }
00574         section = &(*section)->next;
00575     }
00576 }
00577 
00578 
00579 /***********************************************************************
00580  *           PROFILE_Find
00581  *
00582  * Find a key in a profile tree, optionally creating it.
00583  */
00584 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
00585                                  LPCWSTR key_name, BOOL create, BOOL create_always )
00586 {
00587     LPCWSTR p;
00588     int seclen, keylen;
00589 
00590     while (PROFILE_isspaceW(*section_name)) section_name++;
00591     if (*section_name)
00592         p = section_name + strlenW(section_name) - 1;
00593     else
00594         p = section_name;
00595 
00596     while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
00597     seclen = p - section_name + 1;
00598 
00599     while (PROFILE_isspaceW(*key_name)) key_name++;
00600     if (*key_name)
00601         p = key_name + strlenW(key_name) - 1;
00602     else
00603         p = key_name;
00604 
00605     while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
00606     keylen = p - key_name + 1;
00607 
00608     while (*section)
00609     {
00610         if ( ((*section)->name[0])
00611              && (!(strncmpiW( (*section)->name, section_name, seclen )))
00612              && (((*section)->name)[seclen] == '\0') )
00613         {
00614             PROFILEKEY **key = &(*section)->key;
00615 
00616             while (*key)
00617             {
00618                 /* If create_always is FALSE then we check if the keyname
00619                  * already exists. Otherwise we add it regardless of its
00620                  * existence, to allow keys to be added more than once in
00621                  * some cases.
00622                  */
00623                 if(!create_always)
00624                 {
00625                     if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
00626                          && (((*key)->name)[keylen] == '\0') )
00627                         return *key;
00628                 }
00629                 key = &(*key)->next;
00630             }
00631             if (!create) return NULL;
00632             if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
00633                 return NULL;
00634             strcpyW( (*key)->name, key_name );
00635             (*key)->value = NULL;
00636             (*key)->next  = NULL;
00637             return *key;
00638         }
00639         section = &(*section)->next;
00640     }
00641     if (!create) return NULL;
00642     *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
00643     if(*section == NULL) return NULL;
00644     strcpyW( (*section)->name, section_name );
00645     (*section)->next = NULL;
00646     if (!((*section)->key  = HeapAlloc( GetProcessHeap(), 0,
00647                                         sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
00648     {
00649         HeapFree(GetProcessHeap(), 0, *section);
00650         return NULL;
00651     }
00652     strcpyW( (*section)->key->name, key_name );
00653     (*section)->key->value = NULL;
00654     (*section)->key->next  = NULL;
00655     return (*section)->key;
00656 }
00657 
00658 
00659 /***********************************************************************
00660  *           PROFILE_FlushFile
00661  *
00662  * Flush the current profile to disk if changed.
00663  */
00664 static BOOL PROFILE_FlushFile(void)
00665 {
00666     HANDLE hFile = NULL;
00667     FILETIME LastWriteTime;
00668 
00669     if(!CurProfile)
00670     {
00671         WARN("No current profile!\n");
00672         return FALSE;
00673     }
00674 
00675     if (!CurProfile->changed) return TRUE;
00676 
00677     hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
00678                         NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
00679 
00680     if (hFile == INVALID_HANDLE_VALUE)
00681     {
00682         WARN("could not save profile file %s (error was %d)\n", debugstr_w(CurProfile->filename), GetLastError());
00683         return FALSE;
00684     }
00685 
00686     TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
00687     PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
00688     if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
00689        CurProfile->LastWriteTime=LastWriteTime;
00690     CloseHandle( hFile );
00691     CurProfile->changed = FALSE;
00692     return TRUE;
00693 }
00694 
00695 
00696 /***********************************************************************
00697  *           PROFILE_ReleaseFile
00698  *
00699  * Flush the current profile to disk and remove it from the cache.
00700  */
00701 static void PROFILE_ReleaseFile(void)
00702 {
00703     PROFILE_FlushFile();
00704     PROFILE_Free( CurProfile->section );
00705     HeapFree( GetProcessHeap(), 0, CurProfile->filename );
00706     CurProfile->changed = FALSE;
00707     CurProfile->section = NULL;
00708     CurProfile->filename  = NULL;
00709     CurProfile->encoding = ENCODING_ANSI;
00710     ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
00711 }
00712 
00713 /***********************************************************************
00714  *
00715  * Compares a file time with the current time. If the file time is
00716  * at least 2.1 seconds in the past, return true.
00717  *
00718  * Intended as cache safety measure: The time resolution on FAT is
00719  * two seconds, so files that are not at least two seconds old might
00720  * keep their time even on modification, so don't cache them.
00721  */
00722 static BOOL is_not_current(FILETIME * ft)
00723 {
00724     FILETIME Now;
00725     LONGLONG ftll, nowll;
00726     GetSystemTimeAsFileTime(&Now);
00727     ftll = ((LONGLONG)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
00728     nowll = ((LONGLONG)Now.dwHighDateTime << 32) + Now.dwLowDateTime;
00729     TRACE("%08x;%08x\n",(unsigned)ftll+21000000,(unsigned)nowll);
00730     return ftll + 21000000 < nowll;
00731 }
00732 
00733 /***********************************************************************
00734  *           PROFILE_Open
00735  *
00736  * Open a profile file, checking the cached file first.
00737  */
00738 static BOOL PROFILE_Open( LPCWSTR filename, BOOL write_access )
00739 {
00740     WCHAR buffer[MAX_PATH];
00741     HANDLE hFile = INVALID_HANDLE_VALUE;
00742     FILETIME LastWriteTime;
00743     int i,j;
00744     PROFILE *tempProfile;
00745     
00746     ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
00747 
00748     /* First time around */
00749 
00750     if(!CurProfile)
00751        for(i=0;i<N_CACHED_PROFILES;i++)
00752        {
00753           MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
00754           if(MRUProfile[i] == NULL) break;
00755           MRUProfile[i]->changed=FALSE;
00756           MRUProfile[i]->section=NULL;
00757           MRUProfile[i]->filename=NULL;
00758           MRUProfile[i]->encoding=ENCODING_ANSI;
00759           ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
00760        }
00761 
00762     if (!filename)
00763     filename = wininiW;
00764 
00765     if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
00766         !strchrW(filename, '\\') && !strchrW(filename, '/'))
00767     {
00768         static const WCHAR wszSeparator[] = {'\\', 0};
00769         WCHAR windirW[MAX_PATH];
00770         GetWindowsDirectoryW( windirW, MAX_PATH );
00771         strcpyW(buffer, windirW);
00772         strcatW(buffer, wszSeparator);
00773         strcatW(buffer, filename);
00774     }
00775     else
00776     {
00777         LPWSTR dummy;
00778         GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
00779     }
00780         
00781     TRACE("path: %s\n", debugstr_w(buffer));
00782 
00783     hFile = CreateFileW(buffer, GENERIC_READ | (write_access ? GENERIC_WRITE : 0),
00784                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
00785                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
00786 
00787     if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
00788     {
00789         WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer));
00790         return FALSE;
00791     }
00792 
00793     for(i=0;i<N_CACHED_PROFILES;i++)
00794     {
00795         if ((MRUProfile[i]->filename && !strcmpiW( buffer, MRUProfile[i]->filename )))
00796         {
00797             TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
00798             if(i)
00799             {
00800                 PROFILE_FlushFile();
00801                 tempProfile=MRUProfile[i];
00802                 for(j=i;j>0;j--)
00803                     MRUProfile[j]=MRUProfile[j-1];
00804                 CurProfile=tempProfile;
00805             }
00806 
00807             if (hFile != INVALID_HANDLE_VALUE)
00808             {
00809                 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
00810                 if (!memcmp( &CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME) ) &&
00811                     is_not_current(&LastWriteTime))
00812                     TRACE("(%s): already opened (mru=%d)\n",
00813                           debugstr_w(buffer), i);
00814                 else
00815                 {
00816                     TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
00817                           debugstr_w(buffer), i);
00818                     PROFILE_Free(CurProfile->section);
00819                     CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
00820                     CurProfile->LastWriteTime = LastWriteTime;
00821                 }
00822                 CloseHandle(hFile);
00823             }
00824             else TRACE("(%s): already opened, not yet created (mru=%d)\n",
00825                        debugstr_w(buffer), i);
00826             return TRUE;
00827         }
00828     }
00829 
00830     /* Flush the old current profile */
00831     PROFILE_FlushFile();
00832 
00833     /* Make the oldest profile the current one only in order to get rid of it */
00834     if(i==N_CACHED_PROFILES)
00835       {
00836        tempProfile=MRUProfile[N_CACHED_PROFILES-1];
00837        for(i=N_CACHED_PROFILES-1;i>0;i--)
00838           MRUProfile[i]=MRUProfile[i-1];
00839        CurProfile=tempProfile;
00840       }
00841     if(CurProfile->filename) PROFILE_ReleaseFile();
00842 
00843     /* OK, now that CurProfile is definitely free we assign it our new file */
00844     CurProfile->filename  = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
00845     strcpyW( CurProfile->filename, buffer );
00846 
00847     if (hFile != INVALID_HANDLE_VALUE)
00848     {
00849         CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
00850         GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
00851         CloseHandle(hFile);
00852     }
00853     else
00854     {
00855         /* Does not exist yet, we will create it in PROFILE_FlushFile */
00856         WARN("profile file %s not found\n", debugstr_w(buffer) );
00857     }
00858     return TRUE;
00859 }
00860 
00861 
00862 /***********************************************************************
00863  *           PROFILE_GetSection
00864  *
00865  * Returns all keys of a section.
00866  * If return_values is TRUE, also include the corresponding values.
00867  */
00868 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
00869                    LPWSTR buffer, DWORD len, BOOL return_values )
00870 {
00871     PROFILEKEY *key;
00872 
00873     if(!buffer) return 0;
00874 
00875     TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
00876 
00877     while (section)
00878     {
00879         if (section->name[0] && !strcmpiW( section->name, section_name ))
00880         {
00881             UINT oldlen = len;
00882             for (key = section->key; key; key = key->next)
00883             {
00884                 if (len <= 2) break;
00885                 if (!*key->name) continue;  /* Skip empty lines */
00886                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
00887                 if (!return_values && !key->value) continue;  /* Skip lines w.o. '=' */
00888                 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
00889                 len -= strlenW(buffer) + 1;
00890                 buffer += strlenW(buffer) + 1;
00891         if (len < 2)
00892             break;
00893         if (return_values && key->value) {
00894             buffer[-1] = '=';
00895             PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
00896             len -= strlenW(buffer) + 1;
00897             buffer += strlenW(buffer) + 1;
00898                 }
00899             }
00900             *buffer = '\0';
00901             if (len <= 1)
00902                 /*If either lpszSection or lpszKey is NULL and the supplied
00903                   destination buffer is too small to hold all the strings,
00904                   the last string is truncated and followed by two null characters.
00905                   In this case, the return value is equal to cchReturnBuffer
00906                   minus two. */
00907             {
00908         buffer[-1] = '\0';
00909                 return oldlen - 2;
00910             }
00911             return oldlen - len;
00912         }
00913         section = section->next;
00914     }
00915     buffer[0] = buffer[1] = '\0';
00916     return 0;
00917 }
00918 
00919 /* See GetPrivateProfileSectionNamesA for documentation */
00920 static INT PROFILE_GetSectionNames( LPWSTR buffer, DWORD len )
00921 {
00922     LPWSTR buf;
00923     UINT buflen,tmplen;
00924     PROFILESECTION *section;
00925 
00926     TRACE("(%p, %d)\n", buffer, len);
00927 
00928     if (!buffer || !len)
00929         return 0;
00930     if (len==1) {
00931         *buffer='\0';
00932         return 0;
00933     }
00934 
00935     buflen=len-1;
00936     buf=buffer;
00937     section = CurProfile->section;
00938     while ((section!=NULL)) {
00939         if (section->name[0]) {
00940             tmplen = strlenW(section->name)+1;
00941             if (tmplen >= buflen) {
00942                 if (buflen > 0) {
00943                     memcpy(buf, section->name, (buflen-1) * sizeof(WCHAR));
00944                     buf += buflen-1;
00945                     *buf++='\0';
00946                 }
00947                 *buf='\0';
00948                 return len-2;
00949             }
00950             memcpy(buf, section->name, tmplen * sizeof(WCHAR));
00951             buf += tmplen;
00952             buflen -= tmplen;
00953         }
00954         section = section->next;
00955     }
00956     *buf='\0';
00957     return buf-buffer;
00958 }
00959 
00960 
00961 /***********************************************************************
00962  *           PROFILE_GetString
00963  *
00964  * Get a profile string.
00965  *
00966  * Tests with GetPrivateProfileString16, W95a,
00967  * with filled buffer ("****...") and section "set1" and key_name "1" valid:
00968  * section  key_name    def_val     res buffer
00969  * "set1"   "1"     "x"     43  [data]
00970  * "set1"   "1   "      "x"     43  [data]      (!)
00971  * "set1"   "  1  "'    "x"     43  [data]      (!)
00972  * "set1"   ""      "x"     1   "x"
00973  * "set1"   ""      "x   "      1   "x"     (!)
00974  * "set1"   ""      "  x   "    3   "  x"       (!)
00975  * "set1"   NULL        "x"     6   "1\02\03\0\0"
00976  * "set1"   ""      "x"     1   "x"
00977  * NULL     "1"     "x"     0   ""      (!)
00978  * ""       "1"     "x"     1   "x"
00979  * NULL     NULL        ""      0   ""
00980  *
00981  *
00982  */
00983 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
00984                               LPCWSTR def_val, LPWSTR buffer, DWORD len )
00985 {
00986     PROFILEKEY *key = NULL;
00987     static const WCHAR empty_strW[] = { 0 };
00988 
00989     if(!buffer || !len) return 0;
00990 
00991     if (!def_val) def_val = empty_strW;
00992     if (key_name)
00993     {
00994     if (!key_name[0])
00995         {
00996             PROFILE_CopyEntry(buffer, def_val, len, TRUE);
00997             return strlenW(buffer);
00998         }
00999         key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
01000         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
01001                            len, TRUE );
01002         TRACE("(%s,%s,%s): returning %s\n",
01003               debugstr_w(section), debugstr_w(key_name),
01004               debugstr_w(def_val), debugstr_w(buffer) );
01005         return strlenW( buffer );
01006     }
01007     /* no "else" here ! */
01008     if (section && section[0])
01009     {
01010         INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
01011         if (!buffer[0]) /* no luck -> def_val */
01012         {
01013             PROFILE_CopyEntry(buffer, def_val, len, TRUE);
01014             ret = strlenW(buffer);
01015         }
01016         return ret;
01017     }
01018     buffer[0] = '\0';
01019     return 0;
01020 }
01021 
01022 
01023 /***********************************************************************
01024  *           PROFILE_SetString
01025  *
01026  * Set a profile string.
01027  */
01028 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
01029                                LPCWSTR value, BOOL create_always )
01030 {
01031     if (!key_name)  /* Delete a whole section */
01032     {
01033         TRACE("(%s)\n", debugstr_w(section_name));
01034         CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
01035                                                       section_name );
01036         return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
01037                                 this is not an error on application's level.*/
01038     }
01039     else if (!value)  /* Delete a key */
01040     {
01041         TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
01042         CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
01043                                                   section_name, key_name );
01044         return TRUE;          /* same error handling as above */
01045     }
01046     else  /* Set the key value */
01047     {
01048         PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
01049                                         key_name, TRUE, create_always );
01050         TRACE("(%s,%s,%s):\n",
01051               debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
01052         if (!key) return FALSE;
01053 
01054         /* strip the leading spaces. We can safely strip \n\r and
01055          * friends too, they should not happen here anyway. */
01056         while (PROFILE_isspaceW(*value)) value++;
01057 
01058         if (key->value)
01059         {
01060             if (!strcmpW( key->value, value ))
01061             {
01062                 TRACE("  no change needed\n" );
01063                 return TRUE;  /* No change needed */
01064             }
01065             TRACE("  replacing %s\n", debugstr_w(key->value) );
01066             HeapFree( GetProcessHeap(), 0, key->value );
01067         }
01068         else TRACE("  creating key\n" );
01069         key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
01070         strcpyW( key->value, value );
01071         CurProfile->changed = TRUE;
01072     }
01073     return TRUE;
01074 }
01075 
01076 
01077 /********************* API functions **********************************/
01078 
01079 
01080 /***********************************************************************
01081  *           GetProfileIntA   (KERNEL32.@)
01082  */
01083 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
01084 {
01085     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
01086 }
01087 
01088 /***********************************************************************
01089  *           GetProfileIntW   (KERNEL32.@)
01090  */
01091 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
01092 {
01093     return GetPrivateProfileIntW( section, entry, def_val, wininiW );
01094 }
01095 
01096 /***********************************************************************
01097  *           GetPrivateProfileStringW   (KERNEL32.@)
01098  */
01099 DWORD WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
01100                      LPCWSTR def_val, LPWSTR buffer,
01101                      DWORD len, LPCWSTR filename )
01102 {
01103     int     ret;
01104     LPWSTR  defval_tmp = NULL;
01105 
01106     TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
01107           debugstr_w(def_val), buffer, len, debugstr_w(filename));
01108 
01109     /* strip any trailing ' ' of def_val. */
01110     if (def_val)
01111     {
01112         LPCWSTR p = def_val + strlenW(def_val) - 1;
01113 
01114         while (p > def_val && *p == ' ')
01115             p--;
01116 
01117         if (p >= def_val)
01118         {
01119             int len = (int)(p - def_val) + 1;
01120 
01121             defval_tmp = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
01122             memcpy(defval_tmp, def_val, len * sizeof(WCHAR));
01123             defval_tmp[len] = '\0';
01124             def_val = defval_tmp;
01125         }
01126     }
01127 
01128     RtlEnterCriticalSection( &PROFILE_CritSect );
01129 
01130     if (PROFILE_Open( filename, FALSE )) {
01131     if (section == NULL)
01132             ret = PROFILE_GetSectionNames(buffer, len);
01133     else 
01134         /* PROFILE_GetString can handle the 'entry == NULL' case */
01135             ret = PROFILE_GetString( section, entry, def_val, buffer, len );
01136     } else if (buffer && def_val) {
01137        lstrcpynW( buffer, def_val, len );
01138        ret = strlenW( buffer );
01139     }
01140     else
01141        ret = 0;
01142 
01143     RtlLeaveCriticalSection( &PROFILE_CritSect );
01144 
01145     HeapFree(GetProcessHeap(), 0, defval_tmp);
01146 
01147     TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
01148 
01149     return ret;
01150 }
01151 
01152 /***********************************************************************
01153  *           GetPrivateProfileStringA   (KERNEL32.@)
01154  */
01155 DWORD WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
01156                      LPCSTR def_val, LPSTR buffer,
01157                      DWORD len, LPCSTR filename )
01158 {
01159     UNICODE_STRING sectionW, entryW, def_valW, filenameW;
01160     LPWSTR bufferW;
01161     INT retW, ret = 0;
01162 
01163     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
01164     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
01165     else sectionW.Buffer = NULL;
01166     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
01167     else entryW.Buffer = NULL;
01168     if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
01169     else def_valW.Buffer = NULL;
01170     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
01171     else filenameW.Buffer = NULL;
01172 
01173     retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
01174                                      def_valW.Buffer, bufferW, len,
01175                                      filenameW.Buffer);
01176     if (len && buffer)
01177     {
01178         if (retW)
01179         {
01180             ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, len - 1, NULL, NULL);
01181             if (!ret)
01182                 ret = len - 1;
01183         }
01184         buffer[ret] = 0;
01185     }
01186 
01187     RtlFreeUnicodeString(&sectionW);
01188     RtlFreeUnicodeString(&entryW);
01189     RtlFreeUnicodeString(&def_valW);
01190     RtlFreeUnicodeString(&filenameW);
01191     HeapFree(GetProcessHeap(), 0, bufferW);
01192     return ret;
01193 }
01194 
01195 /***********************************************************************
01196  *           GetProfileStringA   (KERNEL32.@)
01197  */
01198 DWORD WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
01199                   LPSTR buffer, DWORD len )
01200 {
01201     return GetPrivateProfileStringA( section, entry, def_val,
01202                                      buffer, len, "win.ini" );
01203 }
01204 
01205 /***********************************************************************
01206  *           GetProfileStringW   (KERNEL32.@)
01207  */
01208 DWORD WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
01209                   LPCWSTR def_val, LPWSTR buffer, DWORD len )
01210 {
01211     return GetPrivateProfileStringW( section, entry, def_val,
01212                      buffer, len, wininiW );
01213 }
01214 
01215 /***********************************************************************
01216  *           WriteProfileStringA   (KERNEL32.@)
01217  */
01218 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
01219                  LPCSTR string )
01220 {
01221     return WritePrivateProfileStringA( section, entry, string, "win.ini" );
01222 }
01223 
01224 /***********************************************************************
01225  *           WriteProfileStringW   (KERNEL32.@)
01226  */
01227 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
01228                                      LPCWSTR string )
01229 {
01230     return WritePrivateProfileStringW( section, entry, string, wininiW );
01231 }
01232 
01233 
01234 /***********************************************************************
01235  *           GetPrivateProfileIntW   (KERNEL32.@)
01236  */
01237 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
01238                                    INT def_val, LPCWSTR filename )
01239 {
01240     WCHAR buffer[30];
01241     UNICODE_STRING bufferW;
01242     INT len;
01243     ULONG result;
01244 
01245     if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
01246                                           buffer, sizeof(buffer)/sizeof(WCHAR),
01247                                           filename )))
01248         return def_val;
01249 
01250     /* FIXME: if entry can be found but it's empty, then Win16 is
01251      * supposed to return 0 instead of def_val ! Difficult/problematic
01252      * to implement (every other failure also returns zero buffer),
01253      * thus wait until testing framework avail for making sure nothing
01254      * else gets broken that way. */
01255     if (!buffer[0]) return (UINT)def_val;
01256 
01257     RtlInitUnicodeString( &bufferW, buffer );
01258     RtlUnicodeStringToInteger( &bufferW, 0, &result);
01259     return result;
01260 }
01261 
01262 /***********************************************************************
01263  *           GetPrivateProfileIntA   (KERNEL32.@)
01264  *
01265  * FIXME: rewrite using unicode
01266  */
01267 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
01268                    INT def_val, LPCSTR filename )
01269 {
01270     UNICODE_STRING entryW, filenameW, sectionW;
01271     UINT res;
01272     if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
01273     else entryW.Buffer = NULL;
01274     if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
01275     else filenameW.Buffer = NULL;
01276     if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
01277     else sectionW.Buffer = NULL;
01278     res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
01279                                 filenameW.Buffer);
01280     RtlFreeUnicodeString(&sectionW);
01281     RtlFreeUnicodeString(&filenameW);
01282     RtlFreeUnicodeString(&entryW);
01283     return res;
01284 }
01285 
01286 /***********************************************************************
01287  *           GetPrivateProfileSectionW   (KERNEL32.@)
01288  */
01289 DWORD WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
01290                       DWORD len, LPCWSTR filename )
01291 {
01292     int ret = 0;
01293 
01294     if (!section || !buffer)
01295     {
01296         SetLastError(ERROR_INVALID_PARAMETER);
01297         return 0;
01298     }
01299 
01300     TRACE("(%s, %p, %d, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
01301 
01302     RtlEnterCriticalSection( &PROFILE_CritSect );
01303 
01304     if (PROFILE_Open( filename, FALSE ))
01305         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
01306 
01307     RtlLeaveCriticalSection( &PROFILE_CritSect );
01308 
01309     return ret;
01310 }
01311 
01312 /***********************************************************************
01313  *           GetPrivateProfileSectionA   (KERNEL32.@)
01314  */
01315 DWORD WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
01316                                       DWORD len, LPCSTR filename )
01317 {
01318     UNICODE_STRING sectionW, filenameW;
01319     LPWSTR bufferW;
01320     INT retW, ret = 0;
01321 
01322     if (!section || !buffer)
01323     {
01324         SetLastError(ERROR_INVALID_PARAMETER);
01325         return 0;
01326     }
01327 
01328     bufferW = HeapAlloc(GetProcessHeap(), 0, len * 2 * sizeof(WCHAR));
01329     RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
01330     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
01331     else filenameW.Buffer = NULL;
01332 
01333     retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len * 2, filenameW.Buffer);
01334     if (retW)
01335     {
01336         if (retW == len * 2 - 2) retW++;  /* overflow */
01337         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
01338         if (!ret || ret == len)  /* overflow */
01339         {
01340             ret = len - 2;
01341             buffer[len-2] = 0;
01342             buffer[len-1] = 0;
01343         }
01344         else ret--;
01345     }
01346     else
01347     {
01348         buffer[0] = 0;
01349         buffer[1] = 0;
01350     }
01351 
01352     RtlFreeUnicodeString(&sectionW);
01353     RtlFreeUnicodeString(&filenameW);
01354     HeapFree(GetProcessHeap(), 0, bufferW);
01355     return ret;
01356 }
01357 
01358 /***********************************************************************
01359  *           GetProfileSectionA   (KERNEL32.@)
01360  */
01361 DWORD WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
01362 {
01363     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
01364 }
01365 
01366 /***********************************************************************
01367  *           GetProfileSectionW   (KERNEL32.@)
01368  */
01369 DWORD WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
01370 {
01371     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
01372 }
01373 
01374 
01375 /***********************************************************************
01376  *           WritePrivateProfileStringW   (KERNEL32.@)
01377  */
01378 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
01379                     LPCWSTR string, LPCWSTR filename )
01380 {
01381     BOOL ret = FALSE;
01382 
01383     RtlEnterCriticalSection( &PROFILE_CritSect );
01384 
01385     if (!section && !entry && !string) /* documented "file flush" case */
01386     {
01387         if (!filename || PROFILE_Open( filename, TRUE ))
01388         {
01389             if (CurProfile) PROFILE_ReleaseFile();  /* always return FALSE in this case */
01390         }
01391     }
01392     else if (PROFILE_Open( filename, TRUE ))
01393     {
01394         if (!section) {
01395             SetLastError(ERROR_FILE_NOT_FOUND);
01396         } else {
01397             ret = PROFILE_SetString( section, entry, string, FALSE);
01398             PROFILE_FlushFile();
01399         }
01400     }
01401 
01402     RtlLeaveCriticalSection( &PROFILE_CritSect );
01403     return ret;
01404 }
01405 
01406 /***********************************************************************
01407  *           WritePrivateProfileStringA   (KERNEL32.@)
01408  */
01409 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
01410                     LPCSTR string, LPCSTR filename )
01411 {
01412     UNICODE_STRING sectionW, entryW, stringW, filenameW;
01413     BOOL ret;
01414 
01415     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
01416     else sectionW.Buffer = NULL;
01417     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
01418     else entryW.Buffer = NULL;
01419     if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
01420     else stringW.Buffer = NULL;
01421     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
01422     else filenameW.Buffer = NULL;
01423 
01424     ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
01425                                      stringW.Buffer, filenameW.Buffer);
01426     RtlFreeUnicodeString(&sectionW);
01427     RtlFreeUnicodeString(&entryW);
01428     RtlFreeUnicodeString(&stringW);
01429     RtlFreeUnicodeString(&filenameW);
01430     return ret;
01431 }
01432 
01433 /***********************************************************************
01434  *           WritePrivateProfileSectionW   (KERNEL32.@)
01435  */
01436 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
01437                                          LPCWSTR string, LPCWSTR filename )
01438 {
01439     BOOL ret = FALSE;
01440     LPWSTR p;
01441 
01442     RtlEnterCriticalSection( &PROFILE_CritSect );
01443 
01444     if (!section && !string)
01445     {
01446         if (!filename || PROFILE_Open( filename, TRUE ))
01447         {
01448             if (CurProfile) PROFILE_ReleaseFile();  /* always return FALSE in this case */
01449         }
01450     }
01451     else if (PROFILE_Open( filename, TRUE )) {
01452         if (!string) {/* delete the named section*/
01453         ret = PROFILE_SetString(section,NULL,NULL, FALSE);
01454         PROFILE_FlushFile();
01455         } else {
01456         PROFILE_DeleteAllKeys(section);
01457         ret = TRUE;
01458         while(*string) {
01459                 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
01460                 strcpyW( buf, string );
01461                 if((p = strchrW( buf, '='))) {
01462                     *p='\0';
01463                     ret = PROFILE_SetString( section, buf, p+1, TRUE);
01464                 }
01465                 HeapFree( GetProcessHeap(), 0, buf );
01466                 string += strlenW(string)+1;
01467             }
01468             PROFILE_FlushFile();
01469         }
01470     }
01471 
01472     RtlLeaveCriticalSection( &PROFILE_CritSect );
01473     return ret;
01474 }
01475 
01476 /***********************************************************************
01477  *           WritePrivateProfileSectionA   (KERNEL32.@)
01478  */
01479 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
01480                                          LPCSTR string, LPCSTR filename)
01481 
01482 {
01483     UNICODE_STRING sectionW, filenameW;
01484     LPWSTR stringW;
01485     BOOL ret;
01486 
01487     if (string)
01488     {
01489         INT lenA, lenW;
01490         LPCSTR p = string;
01491 
01492         while(*p) p += strlen(p) + 1;
01493         lenA = p - string + 1;
01494         lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
01495         if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
01496             MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
01497     }
01498     else stringW = NULL;
01499     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
01500     else sectionW.Buffer = NULL;
01501     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
01502     else filenameW.Buffer = NULL;
01503 
01504     ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
01505 
01506     HeapFree(GetProcessHeap(), 0, stringW);
01507     RtlFreeUnicodeString(&sectionW);
01508     RtlFreeUnicodeString(&filenameW);
01509     return ret;
01510 }
01511 
01512 /***********************************************************************
01513  *           WriteProfileSectionA   (KERNEL32.@)
01514  */
01515 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
01516 
01517 {
01518     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
01519 }
01520 
01521 /***********************************************************************
01522  *           WriteProfileSectionW   (KERNEL32.@)
01523  */
01524 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
01525 {
01526    return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
01527 }
01528 
01529 
01530 /***********************************************************************
01531  *           GetPrivateProfileSectionNamesW  (KERNEL32.@)
01532  *
01533  * Returns the section names contained in the specified file.
01534  * FIXME: Where do we find this file when the path is relative?
01535  * The section names are returned as a list of strings with an extra
01536  * '\0' to mark the end of the list. Except for that the behavior
01537  * depends on the Windows version.
01538  *
01539  * Win95:
01540  * - if the buffer is 0 or 1 character long then it is as if it was of
01541  *   infinite length.
01542  * - otherwise, if the buffer is too small only the section names that fit
01543  *   are returned.
01544  * - note that this means if the buffer was too small to return even just
01545  *   the first section name then a single '\0' will be returned.
01546  * - the return value is the number of characters written in the buffer,
01547  *   except if the buffer was too small in which case len-2 is returned
01548  *
01549  * Win2000:
01550  * - if the buffer is 0, 1 or 2 characters long then it is filled with
01551  *   '\0' and the return value is 0
01552  * - otherwise if the buffer is too small then the first section name that
01553  *   does not fit is truncated so that the string list can be terminated
01554  *   correctly (double '\0')
01555  * - the return value is the number of characters written in the buffer
01556  *   except for the trailing '\0'. If the buffer is too small, then the
01557  *   return value is len-2
01558  * - Win2000 has a bug that triggers when the section names and the
01559  *   trailing '\0' fit exactly in the buffer. In that case the trailing
01560  *   '\0' is missing.
01561  *
01562  * Wine implements the observed Win2000 behavior (except for the bug).
01563  *
01564  * Note that when the buffer is big enough then the return value may be any
01565  * value between 1 and len-1 (or len in Win95), including len-2.
01566  */
01567 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
01568                          LPCWSTR filename)
01569 {
01570     DWORD ret = 0;
01571 
01572     RtlEnterCriticalSection( &PROFILE_CritSect );
01573 
01574     if (PROFILE_Open( filename, FALSE ))
01575         ret = PROFILE_GetSectionNames(buffer, size);
01576 
01577     RtlLeaveCriticalSection( &PROFILE_CritSect );
01578 
01579     return ret;
01580 }
01581 
01582 
01583 /***********************************************************************
01584  *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
01585  */
01586 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
01587                          LPCSTR filename)
01588 {
01589     UNICODE_STRING filenameW;
01590     LPWSTR bufferW;
01591     INT retW, ret = 0;
01592 
01593     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
01594     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
01595     else filenameW.Buffer = NULL;
01596 
01597     retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
01598     if (retW && size)
01599     {
01600         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW+1, buffer, size-1, NULL, NULL);
01601         if (!ret)
01602         {
01603             ret = size-2;
01604             buffer[size-1] = 0;
01605         }
01606         else
01607           ret = ret-1;
01608     }
01609     else if(size)
01610         buffer[0] = '\0';
01611 
01612     RtlFreeUnicodeString(&filenameW);
01613     HeapFree(GetProcessHeap(), 0, bufferW);
01614     return ret;
01615 }
01616 
01617 /***********************************************************************
01618  *           GetPrivateProfileStructW (KERNEL32.@)
01619  *
01620  * Should match Win95's behaviour pretty much
01621  */
01622 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
01623                                       LPVOID buf, UINT len, LPCWSTR filename)
01624 {
01625     BOOL    ret = FALSE;
01626 
01627     RtlEnterCriticalSection( &PROFILE_CritSect );
01628 
01629     if (PROFILE_Open( filename, FALSE )) {
01630         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
01631     if (k) {
01632         TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
01633         if (((strlenW(k->value) - 2) / 2) == len)
01634         {
01635         LPWSTR end, p;
01636         BOOL valid = TRUE;
01637         WCHAR c;
01638         DWORD chksum = 0;
01639 
01640             end  = k->value + strlenW(k->value); /* -> '\0' */
01641             /* check for invalid chars in ASCII coded hex string */
01642             for (p=k->value; p < end; p++)
01643         {
01644                     if (!isxdigitW(*p))
01645             {
01646             WARN("invalid char '%x' in file %s->[%s]->%s !\n",
01647                              *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
01648                 valid = FALSE;
01649                 break;
01650             }
01651         }
01652         if (valid)
01653         {
01654             BOOL highnibble = TRUE;
01655             BYTE b = 0, val;
01656                     LPBYTE binbuf = buf;
01657 
01658                 end -= 2; /* don't include checksum in output data */
01659                 /* translate ASCII hex format into binary data */
01660                     for (p=k->value; p < end; p++)
01661                     {
01662                 c = toupperW(*p);
01663             val = (c > '9') ?
01664                 (c - 'A' + 10) : (c - '0');
01665 
01666             if (highnibble)
01667                     b = val << 4;
01668             else
01669             {
01670                     b += val;
01671                     *binbuf++ = b; /* feed binary data into output */
01672                     chksum += b; /* calculate checksum */
01673             }
01674             highnibble ^= 1; /* toggle */
01675                     }
01676             /* retrieve stored checksum value */
01677             c = toupperW(*p++);
01678             b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
01679             c = toupperW(*p);
01680             b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
01681                 if (b == (chksum & 0xff)) /* checksums match ? */
01682                         ret = TRUE;
01683                 }
01684             }
01685     }
01686     }
01687     RtlLeaveCriticalSection( &PROFILE_CritSect );
01688 
01689     return ret;
01690 }
01691 
01692 /***********************************************************************
01693  *           GetPrivateProfileStructA (KERNEL32.@)
01694  */
01695 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
01696                       LPVOID buffer, UINT len, LPCSTR filename)
01697 {
01698     UNICODE_STRING sectionW, keyW, filenameW;
01699     INT ret;
01700 
01701     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
01702     else sectionW.Buffer = NULL;
01703     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
01704     else keyW.Buffer = NULL;
01705     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
01706     else filenameW.Buffer = NULL;
01707 
01708     ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
01709                                    filenameW.Buffer);
01710     /* Do not translate binary data. */
01711 
01712     RtlFreeUnicodeString(&sectionW);
01713     RtlFreeUnicodeString(&keyW);
01714     RtlFreeUnicodeString(&filenameW);
01715     return ret;
01716 }
01717 
01718 
01719 
01720 /***********************************************************************
01721  *           WritePrivateProfileStructW (KERNEL32.@)
01722  */
01723 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
01724                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
01725 {
01726     BOOL ret = FALSE;
01727     LPBYTE binbuf;
01728     LPWSTR outstring, p;
01729     DWORD sum = 0;
01730 
01731     if (!section && !key && !buf)  /* flush the cache */
01732         return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
01733 
01734     /* allocate string buffer for hex chars + checksum hex char + '\0' */
01735     outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
01736     p = outstring;
01737     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
01738       *p++ = hex[*binbuf >> 4];
01739       *p++ = hex[*binbuf & 0xf];
01740       sum += *binbuf;
01741     }
01742     /* checksum is sum & 0xff */
01743     *p++ = hex[(sum & 0xf0) >> 4];
01744     *p++ = hex[sum & 0xf];
01745     *p++ = '\0';
01746 
01747     RtlEnterCriticalSection( &PROFILE_CritSect );
01748 
01749     if (PROFILE_Open( filename, TRUE )) {
01750         ret = PROFILE_SetString( section, key, outstring, FALSE);
01751         PROFILE_FlushFile();
01752     }
01753 
01754     RtlLeaveCriticalSection( &PROFILE_CritSect );
01755 
01756     HeapFree( GetProcessHeap(), 0, outstring );
01757 
01758     return ret;
01759 }
01760 
01761 /***********************************************************************
01762  *           WritePrivateProfileStructA (KERNEL32.@)
01763  */
01764 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
01765                     LPVOID buf, UINT bufsize, LPCSTR filename)
01766 {
01767     UNICODE_STRING sectionW, keyW, filenameW;
01768     INT ret;
01769 
01770     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
01771     else sectionW.Buffer = NULL;
01772     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
01773     else keyW.Buffer = NULL;
01774     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
01775     else filenameW.Buffer = NULL;
01776 
01777     /* Do not translate binary data. */
01778     ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
01779                                      filenameW.Buffer);
01780 
01781     RtlFreeUnicodeString(&sectionW);
01782     RtlFreeUnicodeString(&keyW);
01783     RtlFreeUnicodeString(&filenameW);
01784     return ret;
01785 }
01786 
01787 
01788 /***********************************************************************
01789  *           OpenProfileUserMapping   (KERNEL32.@)
01790  */
01791 BOOL WINAPI OpenProfileUserMapping(void) {
01792     FIXME("(), stub!\n");
01793     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
01794     return FALSE;
01795 }
01796 
01797 /***********************************************************************
01798  *           CloseProfileUserMapping   (KERNEL32.@)
01799  */
01800 BOOL WINAPI CloseProfileUserMapping(void) {
01801     FIXME("(), stub!\n");
01802     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
01803     return FALSE;
01804 }

Generated on Sun May 27 2012 04:23:53 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.