// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1993, 1994  Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   ini.c
//
//  PURPOSE:  Fills a TreeView control with a listing of the INI
//            files. The root items are the INI files, then children
//            off the roots are the sections, and the children off
//            the sections are the entries:
//
//            ROOT WIN.INI
//                   |
//                   --[Fonts]
//                     |
//                     --AlbertusBold
//            ROOT SYSTEM.INI
//    
//
//  FUNCTIONS:
//
//    INI_FillTreeView - Entry point function to fill TreeView Control
//    FillIniKeys      - Build a list of sections for each INI file
//    FillKeyItems     - Build a list of entries for each section
//    
//    MyGetPrivateProfileSection - Hack to get around bug in Build 87
//    
//  COMMENTS:
//
//    The INI_FillTreView function is called. It adds the INI file
//    names to the treeview. It calls FillIniKeys for each INI file. 
//    The FillIniKeys function adds the sections for the passed in
//    INI filename. The FillIniKeys calls FillKeyItems for each section.
//    The FillKeyItems function adds the entries for the passed in
//    section.

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <commctrl.h>           // TreeView declarations live here
#include "globals.h"            // prototypes specific to this application

#define MAX_SECTION_SIZE      65536
#define MAX_SECTION_KEY_SIZE  256
#define MAX_ENTRY_SIZE        256
#define MAX_STRING_SIZE       256
#define MAX_INIFILE_SIZE      65536

//
//  FUNCTION PROTOTYPES
//

void INI_FillTreeView(HWND        hwndTreeView, 
                      int         iINIFileImage,
                      int         iSectionKeyImage,
                      int         iSelectedEntryImage,
                      int         iNonSelectedEntryImage);

void FillIniKeys(HWND hwndTreeView, 
                 HTREEITEM hParent, 
                 LPSTR szIniFileName,
                 int         iSectionKeyImage,
                 int         iSelectedEntryImage,
                 int         iNonSelectedEntryImage);

void FillKeyItems(HWND        hwndTreeView, 
                  HTREEITEM   hParent, 
                  LPSTR       szIniFileName, 
                  LPSTR       szSection,
                  int         iSelectedEntryImage,
                  int         iNonSelectedEntryImage);

void MyGetPrivateProfileSection(LPSTR szSection, 
                                LPSTR szSectionData, 
                                int   cbSize, 
                                LPSTR  szIniFileName);

//
//  FUNCTION: INI_FillTreeView(HWND        hwndTreeView, 
//                             int         iINIFileImage,
//                             int         iSectionKeyImage,
//                             int         iSelectedEntryImage,
//                             int         iNonSelectedEntryImage)
//
//
//  PURPOSE: Fills the TreeView control with the INI files,
//           call helper functions to fill in more detailed
//           information.
//
//  PARAMETERS:
//    hwndTreeView           - Handle fo TreeView Control
//    iINIFileImage          - ImageList ID for "INI" item.
//    iSectionKeyImage       - ImageList ID for "Folder" item.
//    iSelectedEntryImage    - ImageList ID for selected entry.
//    iNonSelectedEntryImage - ImageList ID for non-selected entry.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This function walks the Windows directory for INI files.
//    For each file found, a root entry is added to the TreeView,
//    and then the FillIniKeys helper function is called to
//    fill in the sections for the INI file.
//

void INI_FillTreeView(HWND        hwndTreeView, 
                      int         iINIFileImage,
                      int         iSectionKeyImage,
                      int         iSelectedEntryImage,
                      int         iNonSelectedEntryImage)
{
    char             szWindowsDirectory[MAX_PATH]; // Windows Directory.
    WIN32_FIND_DATA  FindFile;                     // For file searches.
    HANDLE           hFindFile;                    // For file searches.
    TV_ITEM          tvi;                          // TreeView Item.
    TV_INSERTSTRUCT  tvins;                        // TreeView Insert Struct.
    HTREEITEM        hPrev = NULL;                 // Previous Item Added.

    // Hourglass on!

    SetCapture(GetParent(hwndTreeView));
    SetCursor(LoadCursor(NULL, IDC_WAIT));

    // Clear TreeView Control

    TreeView_DeleteAllItems(hwndTreeView);

    // Find Windows Directory

    GetWindowsDirectory(szWindowsDirectory, sizeof(szWindowsDirectory));

    // Make a *.INI mask

    lstrcat(szWindowsDirectory, "\\*.ini");

    // Find the first INI file 

    hFindFile = FindFirstFile(szWindowsDirectory, &FindFile);

    // Loop through each INI file and add to the tree

    if (INVALID_HANDLE_VALUE != hFindFile)
    {
        do
        {
            // Add this file to the TreeView
            // Set up the structure for a text label, and use
            // the ImageList ID iINIFileImage.
            
            tvi.mask            = TVIF_TEXT | TVIF_IMAGE;
            tvi.iImage          =
            tvi.iSelectedImage  = iINIFileImage;

            tvi.pszText         = FindFile.cFileName;
            tvi.cchTextMax      = MAX_PATH;

            // Populate the TreeVeiw Insert Struct
            // The item is the one filled above.
            // Insert it after the last item inserted at this level.
            // And indicate this is a root entry.

            tvins.item         = tvi;
            tvins.hInsertAfter = hPrev;
            tvins.hParent      = TVI_ROOT;

            // Add the item to the tree

            hPrev = TreeView_InsertItem(hwndTreeView, &tvins);

            // Add the keys for this ini to the TreeView

            FillIniKeys(hwndTreeView, 
                        hPrev, 
                        FindFile.cFileName,
                        iSectionKeyImage,
                        iSelectedEntryImage,
                        iNonSelectedEntryImage);
        }
        while (FindNextFile(hFindFile, &FindFile));

    } /* end if */

    // Sort TreeView Control
    //
    // Sort at the root, and recurse down the children levels.

//    TreeView_SortChildren(hwndTreeView, TVI_ROOT, TRUE);

    // Hourglass off!

    ReleaseCapture();
    SetCursor(LoadCursor(NULL, IDC_ARROW));
}

//
//  FUNCTION: bThisLineIsSection(LPSTR *pszFile, LPSTR szSectionName)
//
//  PURPOSE: Checks the next line of text in a file memory image
//           and it if is a [section], copies the section into
//           the szSectionName string and returns TRUE.
//
//  PARAMETERS:
//    pszFile      - Points to a LPSTR, which points to the start of
//                   a line of text in the file memory image.
//    szSectionNmae- String to copy section name to on success.
//
//  RETURN VALUE:
//    On success - TRUE
//    On Failure - FALSE
//
//  COMMENTS:
//
//    The pszFile pointer MUST point to the start of a line of
//    text in a file memory image. This function will move the
//    pointer to the start of the next line in the file memory
//    image.
//

BOOL bThisLineIsSection(LPSTR *pszFile, LPSTR szSectionName)
{
    int    iFilePtrPos;       // Character position in file mem image
    int    iSectionStringPtr; // Character position in szSectionName
    BOOL   bRetval = FALSE;   // Return value for function

    // Initialize position counters

    iFilePtrPos = iSectionStringPtr = 0;

    // Check to see if line starts with a '[', that would make
    // it a [section].

    if ('[' == (*pszFile)[iFilePtrPos])
    {
        // Bump char position in file by one to skip the
        // open square bracket.

        iFilePtrPos++;

        // Keep copying characters into the szSectionName string
        // until the closing square bracket is found.

        while ((*pszFile)[iFilePtrPos] && (']' != (*pszFile)[iFilePtrPos]))
        {
            szSectionName[iSectionStringPtr] = (*pszFile)[iFilePtrPos];
            iSectionStringPtr++;
            iFilePtrPos++;
        }
      
        // Add the null terminator to the end of szSectionName

        szSectionName[iSectionStringPtr] = 0;

        // Indicate sucess for the return value.

        bRetval = TRUE;
    }

    // Now, we must skip over the CRLF at the and of each text line
    // so that the next time this function is called, the pointer
    // will be at the end of the line

    while ( 
            ((*pszFile)[iFilePtrPos])          // While not EOF
            && 
            ('\n' != (*pszFile)[iFilePtrPos])  // While not LF char
          ) 
        iFilePtrPos++;

    // After the above while loop, the iFilePtrPos is still sitting
    // on the LF. Bump it to the next character, which will be the
    // first character of the next line.

    if ((*pszFile)[iFilePtrPos]) iFilePtrPos++;

    // Change the value of the pointer to the file memory image
    // to point to the next line.

    *pszFile += iFilePtrPos;

    // Return the results.

    return bRetval;
}

//
//  FUNCTION: FillIniKeys(HWND        hwndTreeView, 
//                        HTREEITEM   hParent, 
//                        LPSTR       szIniFileName,
//                        int         iSectionKeyImage,
//                        int         iSelectedEntryImage,
//                        int         iNonSelectedEntryImage)
//
//  PURPOSE: Adds all the [section] names of the specified
//           INI file to the TreeView control.
//
//  PARAMETERS:
//    hwndTreeView           - Handle fo TreeView Control.
//    hParent                - Handle to root TreeView Item which
//                             inidcates the name of the INI file.
//    szIniFileName          - Filename of the INI file.
//    iSectionKeyImage       - ImageList ID for "Folder" item.
//    iSelectedEntryImage    - ImageList ID for selected entry.
//    iNonSelectedEntryImage - ImageList ID for non-selected entry.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This function makes a memory image of the file by loading it
//    into memory. Then, a pointer to this image is repeatedly
//    passed to the bThisLineIsSection function, which then
//    determines if the line is a [section]. If so, the section
//    is added to the TreeView control, and the FillKeyItems
//    function is called to fill in the individual entries.
//

void FillIniKeys(HWND        hwndTreeView, 
                 HTREEITEM   hParent, 
                 LPSTR       szIniFileName,
                 int         iSectionKeyImage,
                 int         iSelectedEntryImage,
                 int         iNonSelectedEntryImage)
{
    static char      szIniFile[MAX_INIFILE_SIZE];  // Memory image of file

    char             szRawSection[MAX_SECTION_KEY_SIZE]; // Holds "Section".
    char             szSection[MAX_SECTION_KEY_SIZE+2];  // Holds "[Section]".
    int              iIniFileSize;                       // Size of INI file.
    HFILE            hFile;                              // File handle.
    OFSTRUCT         of;                                 // For OpenFile. 
    LPSTR            ptr;                                // Pointer to file
                                                         // image.
    TV_ITEM          tvi;                                // TreeView Item.
    TV_INSERTSTRUCT  tvins;                              // TreeView Insert
                                                         // struct.
    HTREEITEM        hPrev = NULL;                       // Previous Item
                                                         // added.

    // Load the INI file into the memory image, and count the
    // bytes.

    hFile        = OpenFile(szIniFileName, &of, OF_READ);
    iIniFileSize = _lread(hFile, szIniFile, MAX_INIFILE_SIZE-1);
    _lclose(hFile);

    // Terminate the EOF with a zero, this lets the bThisLineIsSection
    // function and the loop below know when to stop.

    szIniFile[iIniFileSize] = 0;

    // Set the pointer to the start of the memory image.

    ptr = szIniFile;

    // Loop through each line in the memory image

    while (*ptr)
    {
        // Check to see if this line is a [section]

        if (bThisLineIsSection(&ptr, szRawSection))
        {
            // Format the string to look like a [Section]

            wsprintf (szSection, "[%s]", szRawSection );

            // Add the string to the TreeView
            // Set up the structure for a text label, and use
            // the ImageList ID iSectionKeyImage for both images
            // and selected images. If we did not use the
            // TVIF_SELECTEDIMAGE mask, then the selected
            // image of the parent of this item would be used
            // for this image, which would look goofy.
 
            tvi.mask            = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
            tvi.iImage          =
            tvi.iSelectedImage  = iSectionKeyImage;

            tvi.pszText    = szSection;
            tvi.cchTextMax = sizeof(szSection);

            // Populate the TreeVeiw Insert Struct
            // The item is the one filled above.
            // Insert it after the last item inserted at this level.
            // And indicate this is a child entry from the hParent
            // item passed into this function.

            tvins.item         = tvi;
            tvins.hInsertAfter = hPrev;
            tvins.hParent      = hParent;

            // Add the item to the tree

            hPrev = TreeView_InsertItem(hwndTreeView, &tvins);

            // Add the entries for this section to the TreeView.

            FillKeyItems(hwndTreeView, 
                         hPrev, 
                         szIniFileName, 
                         szRawSection, 
                         iSelectedEntryImage,
                         iNonSelectedEntryImage);

        } /* end if */

    } /* end while */

}


//
//  FUNCTION: FillKeyItems(HWND        hwndTreeView, 
//                         HTREEITEM   hParent, 
//                         LPSTR       szIniFileName,
//                         LPSTR       szSection,
//                         int         iSelectedEntryImage,
//                         int         iNonSelectedEntryImage)
//
//  PURPOSE: Adds all the entries under the specified [section]
//           of the specified INI file to the TreeView control.
//
//  PARAMETERS:
//    hwndTreeView           - Handle fo TreeView Control.
//    hParent                - Handle to root TreeView Item which
//                             inidcates the name of the INI file.
//    szIniFileName          - Filename of the INI file.
//    szSection              - Name of the [section].
//    iSelectedEntryImage    - ImageList ID for selected entry.
//    iNonSelectedEntryImage - ImageList ID for non-selected entry.
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
//    This function uses the GetPrivateProfileSection API to
//    get a list of strings for the specified [section]. Each
//    string is then happily added to the TreeView.
//

void FillKeyItems(HWND        hwndTreeView, 
                  HTREEITEM   hParent, 
                  LPSTR       szIniFileName, 
                  LPSTR       szSection,
                  int         iSelectedEntryImage,
                  int         iNonSelectedEntryImage)
{
    static char  szSectionData[MAX_SECTION_SIZE];          // Place to
                                                           // put all the
                                                           // strings.

    LPSTR szEntryPtr;                               // Pointer to
                                                    // the current
                                                    // entry.

    TV_ITEM         tvi;                            // TreeView Item.
    TV_INSERTSTRUCT tvins;                          // TreeView Insert struct.
    HTREEITEM       hPrev = NULL;                   // Previous Item added.

    // Fill the szSectionData buffer with the entries. The API will
    // fill the buffer like this: "one=1\0two=2" for the following
    // entries:
    //
    // [Section]
    // one=1
    // two=2
    //

    // Currently use MyGetPrivateProfileSection, since 
    // GetPrivateProfileSection is busted in build 87.

    MyGetPrivateProfileSection(szSection, 
                               szSectionData, 
                               MAX_SECTION_SIZE, 
                               szIniFileName);

    // Set the entry to point to the first item in the above
    // string.

    szEntryPtr = szSectionData;

    // Loop through each entry, and add them to the TreeView.
   
    while (*szEntryPtr)
    {
        // Add the string to the TreeView
        // Set up the structure for a text label, and use
        // the ImageList ID iNonSelectedEntryImage for
        // non-selected images, and iSelectedEntryImage for
        // selected images. If we did not use the
        // TVIF_SELECTEDIMAGE mask, then the selected
        // image of the parent of this item would be used
        // for this image, which would look goofy.

        tvi.mask            = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
        tvi.iImage          = iNonSelectedEntryImage;
        tvi.iSelectedImage  = iSelectedEntryImage;

        tvi.pszText    = szEntryPtr;
        tvi.cchTextMax = lstrlen(szEntryPtr);

        // Populate the TreeVeiw Insert Struct
        // The item is the one filled above.
        // Insert it after the last item inserted at this level.
        // And indicate this is a child entry from the hParent
        // item passed into this function.

        tvins.item         = tvi;
        tvins.hInsertAfter = hPrev;
        tvins.hParent      = hParent;

        // Add the item to the tree

        hPrev = TreeView_InsertItem(hwndTreeView, &tvins);

        // Bump the pointer to the next string.

        szEntryPtr += lstrlen(szEntryPtr) + 1;

    } /* end while */
    
}

/**************************************************************

                STYLE POLICE WARNING!!!

      The code after this block is TEMPORARY hack code
      to get around the fact that GetPrivateProfileSection
      in build 87 returns a null string no matter what.

      It will come out AS SOON as the API is fixed,
      and therefore I am not going to comment/style-ize this
      unless we end up needing to ship it (which would
      be both bugs and sad).

**************************************************************/


BOOL bReadLine (LPSTR *pszFile, LPSTR szLine)
{
    int    i, j;

    i = j = 0;

    if (!(*pszFile)[i]) return FALSE;

    while ((i < 100) && ((*pszFile)[i]) && ('\r' != (*pszFile)[i]))
       {
       szLine[j] = (*pszFile)[i];
       j++;
       i++;
       }
    szLine[j] = 0;

    while ((i < 100) && ((*pszFile)[i]) && ('\n' != (*pszFile)[i])) i++;
    if ((*pszFile)[i]) i++;

    *pszFile += i;

    return TRUE;
}

// This hacky function does the same thing as GetPrivateProfileSection
// which seems to be broken in build 87

void MyGetPrivateProfileSection(LPSTR szSection, 
                                LPSTR szSectionData, 
                                int   cbSize, 
                                LPSTR szIniFileName
                               )
{
  char szSectionKey[256];
  static char       szIniFile[MAX_INIFILE_SIZE];
  HFILE      hFile;
  OFSTRUCT   of;
  LPSTR     ptr;
           char szLine[MAX_ENTRY_SIZE+MAX_STRING_SIZE+2];
  BOOL     bOk;
  int      i, len, iIniFileSize;

  *szSectionData = 0;

  wsprintf ( szSectionKey, "[%s]", szSection );

  hFile        = OpenFile(szIniFileName, &of, OF_READ);
  if (-1 == hFile) MessageBeep(0);
  iIniFileSize = _lread(hFile, szIniFile, MAX_INIFILE_SIZE-1);
  _lclose(hFile);

  szIniFile[iIniFileSize] = 0;

  ptr = szIniFile;

  bOk = FALSE;

  while (bReadLine (&ptr, szLine))
    {
    if ('[' == *szLine)
      {
      bOk = !lstrcmpi(szLine, szSectionKey);
      }
    else
      {
      if (bOk)
        {
        lstrcat (szSectionData, szLine);
        lstrcat (szSectionData, "");
        }
      }
    }

  len = lstrlen(szSectionData);
  for (i = 0; i < len; i++ ) if ('' == szSectionData[i]) szSectionData[i] = 0;
}



