/************************************************************************
 *                                                                      *
 *  PROGRAM: PROGSET.C                                                  *
 *                                                                      *
 *  PURPOSE: Allow user to safely modify WIN.INI file.                  *
 *                                                                      *     
 *  FUNCTIONS:                                                          *
 *                                                                      *
 *    WinMain()        - initialization, processes message loop         *
 *    MainWndProc()    - processes messages                             *
 *    CommandHandler() - handles WM_COMMAND messages                    *
 *    DoDialog()       - runs a dialog box                              *
 *    DefaultDlg()     - default dialog box procudure                   *
 *    ExtDlg()         -                                                *
 *    ListExts()       -                                                *
 *    ListApps()       -                                                *
 *    GetApps()        -                                                *
 *                                                                      *
 ************************************************************************/

#include "ps.h"
#include <string.h>

HANDLE  hInst;                    /* Handle to program instance      */
HWND    hwnd;                     /* Handle to main window           */
HANDLE  hHourGlass;               /* Handle to hourglass cursor      */
HANDLE  hSaveCursor;              /* Handle to saved cursor          */

int     nMode = EXTENSION;
char    szListBuf[1024];
char    szAppList[50][50];
PSTR    psListItem[50];
int     nAppQty;
char    str[255];
HWND    hListBox;

#define NUMCONST 9
#define LENCONST 14

char szConstList [NUMCONST][LENCONST] =
     { "WINDOWS", "DESKTOP", "EXTENSIONS", "INTL", "PORTS",
       "FONTS", "PRINTERPORTS", "DEVICES", "COLORS" };

/* function prototypes for MAIN */
int  FAR         DoDialog  (HANDLE, LPSTR, HWND, FARPROC, DWORD);
int  FAR  PASCAL DefaultDlg (HWND, unsigned, WORD, LONG);
int  FAR  PASCAL MainDlgProc (HWND, unsigned, WORD, LONG);
int  FAR  PASCAL ExtDlg (HWND, WORD, WORD, LONG);
int  NEAR PASCAL CommandHandler(HWND,WORD,LONG);
int  NEAR        ListExts();
int  NEAR        ListApps();
BOOL NEAR        GetApps(HWND);

/***********************************************************************
 *                                                                     *
 *   FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)                     *
 *                                                                     *
 *   PURPOSE: calls initialization functions, processes message loop   *
 *                                                                     *
 ***********************************************************************/
int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR  lpCmdLine;
int    nCmdShow;
{
    hInst = hInstance;

    /* get hourglass cursor */
    hHourGlass = LoadCursor(NULL, IDC_WAIT);

    if (hPrevInstance)
       {
       MessageBox (NULL, "ProgSet is already Running", "ProgSet",
                   MB_ICONEXCLAMATION | MB_OK);
       return (NULL);
       }
    else
       {
       DoDialog (hInst, IDD_MAIN, NULL, MainDlgProc, NULL);
       }

    return (NULL);
}


/***********************************************************************
 *                                                                     *
 *  FUNCTION: MainDlgProc(HWND, unsigned, WORD, LONG)                  *
 *                                                                     *
 *  PURPOSE:  Processes messages                                       *
 *                                                                     *
 *  MESSAGES:                                                          *
 *                                                                     *
 *                                                                     *
 *  COMMENTS:                                                          *
 *                                                                     *
 ***********************************************************************/
int FAR PASCAL MainDlgProc (hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    HMENU hMenu;

    switch (message) 
        {
        case WM_INITDIALOG:
             /* set global window handle */
             hwnd = hWnd;
             /* assign class icon */
             SetClassWord (hwnd, GCW_HICON, LoadIcon(hInst, "PROGSET"));
             /* modify system menu */
             hMenu = GetSystemMenu (hwnd, FALSE);
             AppendMenu (hMenu, MF_SEPARATOR, NULL, NULL);
             AppendMenu (hMenu, MF_ENABLED, IDM_ABOUT, "About ProgSet...");
             /* set file associations button */
             SendDlgItemMessage (hwnd, IDC_ASSOC, BM_SETCHECK, TRUE, NULL);
             /* simulate click of file associations button */
             SendMessage (hwnd, WM_COMMAND, IDC_ASSOC, NULL);
             /* set focus to list box */
             hListBox = GetDlgItem (hwnd, IDC_LIST_BOX);
             SetFocus (hListBox);
             return (FALSE); /* Focus set */
             break;

        case WM_COMMAND:
             return (CommandHandler(hwnd, wParam, lParam));
             break;

        case WM_CLOSE:
             EndDialog(hwnd, NULL);
             return(TRUE);
             break;

        case WM_SYSCOMMAND:
             if (wParam == IDM_ABOUT)
                {
                DoDialog (hInst, IDD_ABOUT, hWnd, NULL, NULL);
                break;
                }
             /* fall through */

        default:
             return (FALSE);
        }
}


/***********************************************************************
 *                                                                     *
 *  FUNCTION   : CommandHandler()                                      *
 *                                                                     *
 *  PURPOSE    : Processes all WM_COMMAND messages.                    *
 *                                                                     *
 ***********************************************************************/
int NEAR PASCAL CommandHandler(hWnd, wParam, lParam)
HWND hWnd;
WORD wParam;
LONG lParam;
{
    BOOL bItems;

    switch (wParam)
        {
        /* control ID's */

        case IDC_ASSOC:
             nMode = EXTENSION;
             lstrcpy (str, "File Associations");
             SetDlgItemText (hwnd, IDC_LIST_TITLE, str);
             bItems = ListExts();
             EnableWindow (GetDlgItem (hwnd, IDC_ADD), TRUE);
             EnableWindow (GetDlgItem (hwnd, IDC_MODIFY), bItems);
             EnableWindow (GetDlgItem (hwnd, IDC_REMOVE), bItems);
             SetFocus (hListBox);
             break;
 

        case IDC_CUSTOM:
             nMode = CUSTOM;
             lstrcpy (str, "Installed programs");
             SetDlgItemText (hwnd, IDC_LIST_TITLE, str);
             bItems = ListApps();
             EnableWindow (GetDlgItem (hwnd, IDC_ADD), FALSE);
             EnableWindow (GetDlgItem (hwnd, IDC_MODIFY), FALSE);
             EnableWindow (GetDlgItem (hwnd, IDC_REMOVE), bItems);
             SetFocus (hListBox);
             break;

        case IDC_ADD:
             {
             if (nMode == EXTENSION)
                {
                DoDialog(hInst, IDD_ADDEXT, hwnd, ExtDlg, ADD);
                ListExts();
                }
             SetFocus (hListBox);
             SendDlgItemMessage (hwnd, IDC_ADD, BM_SETSTYLE,
                                (WORD)BS_PUSHBUTTON, MAKELONG(NULL, TRUE));
             }
             break;

        case IDC_MODIFY:
             {
             if (nMode == EXTENSION)
                DoDialog(hInst, IDD_MODEXT, hwnd, ExtDlg, EDIT);
             SetFocus (hListBox);
             SendDlgItemMessage (hwnd, IDC_MODIFY, BM_SETSTYLE,
                                (WORD)BS_PUSHBUTTON, MAKELONG(NULL, TRUE));
             }
             break;

        case IDC_REMOVE:
             {
             if (nMode == EXTENSION)
                {
                char szExt[4];
                int nCurSel;
                int nResponse;

                nCurSel = SendDlgItemMessage (hwnd, IDC_LIST_BOX,
                                              LB_GETCURSEL, NULL,NULL);
                SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_GETTEXT,
                                   nCurSel, (long)(LPSTR)szExt);

                wsprintf(str, "Remove *.%s association from WIN.INI",
                         (LPSTR)szExt);
                nResponse = MessageBox(hWnd, str, "ProgSet",
                                       MB_YESNO | MB_ICONQUESTION);
                if (nResponse == IDYES)
                   {
                   WriteProfileString ("extensions",szExt,NULL);
                   ListExts();
                   }
                }
             else /* nMode == CUSTOM */
                {
                char szApp[50];
                int nCurSel;
                int nResponse;

                nCurSel = SendDlgItemMessage (hwnd, IDC_LIST_BOX,
                                              LB_GETCURSEL, NULL,NULL);
                SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_GETTEXT,
                                   nCurSel, (long)(LPSTR)szApp);

                wsprintf(str, "Remove [%s] section from WIN.INI",
                         (LPSTR)szApp);
                nResponse = MessageBox(hWnd, str, "ProgSet",
                                       MB_YESNO | MB_ICONQUESTION);
                if (nResponse == IDYES)
                   {
                   WriteProfileString (szApp,NULL,NULL);
                   ListApps();
                   }
                }
             SetFocus (hListBox);
             SendDlgItemMessage (hwnd, IDC_REMOVE, BM_SETSTYLE,
                                (WORD)BS_PUSHBUTTON, MAKELONG(NULL, TRUE));
             }
             break;

        case IDC_EXIT:
             SendMessage (hwnd, WM_CLOSE, NULL, NULL);
             break;

        default:
             return (FALSE);
        }

    return (TRUE);
}


/***********************************************************************
 *                                                                     *
 *  FUNCTION   : DoDialog(hInst, lpResName, hWnd, lpProc, dwInitParam) *
 *                                                                     *
 *  PURPOSE    : Runs a dialog box.  A dialog procedure and initial    *
 *               parameter are both optional.  If the dialog procedure *
 *               is NULL then DefaultDlg is used.  If the initial      *
 *               parameter is NULL then DialogBox() is called instead  *
 *               of DialogBoxParam().                                  *
 *                                                                     *
 ***********************************************************************/
int FAR DoDialog (hInst, lpResName, hWnd, lpProc, dwInitParam)
HANDLE   hInst;
LPSTR    lpResName;
HWND     hWnd;
FARPROC  lpProc;
DWORD    dwInitParam;
{
    FARPROC lpfn;
    int     ret;

    if (lpProc == NULL)  /* if no proc was specified, use default */
       lpProc = DefaultDlg;

    lpfn = MakeProcInstance(lpProc, hInst);

    if (!dwInitParam)  /*  if dwInitParam = 0, do not pass param */
       ret = DialogBox(hInst, lpResName, hWnd, lpfn);
    else
       ret = DialogBoxParam(hInst, lpResName, hWnd, lpfn, dwInitParam);

    FreeProcInstance(lpfn);

    return(ret);
}


/***********************************************************************
 *                                                                     *
 *   FUNCTION: DefaultDlg(HWND, unsigned, WORD, LONG)                  *
 *                                                                     *
 *   PURPOSE:  Processes only "OK" and "Cancel" messages for any       *
 *             dialog box.                                             *
 *                                                                     *
 *   MESSAGES:                                                         *
 *                                                                     *
 *       WM_INITDIALOG - initialize dialog box                         *
 *       WM_COMMAND    - Input received                                *
 *                                                                     *
 ***********************************************************************/
int FAR PASCAL DefaultDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message)
         {
         case WM_INITDIALOG:
              return (TRUE);

         case WM_CLOSE:
              SendMessage (hDlg, WM_COMMAND, IDCANCEL, NULL);
              return (TRUE);

         case WM_COMMAND:
              if (wParam == IDOK)
                 {
                 EndDialog(hDlg, IDOK);
                 return(TRUE);
                 }
              else if (wParam == IDCANCEL)
                 {
                 EndDialog(hDlg, IDCANCEL);
                 return (TRUE);
                 }
              break;
         }
    return (FALSE);
}


/***********************************************************************
 *                                                                     *
 *   FUNCTION: ExtDlg (HWND, WORD, WORD, LONG)                         *
 *                                                                     *
 *   PURPOSE:  Processes only "OK" and "Cancel" messages for any       *
 *             dialog box.                                             *
 *                                                                     *
 ***********************************************************************/
int FAR PASCAL ExtDlg (hDlg, message, wParam, lParam)
HWND hDlg;
WORD message;
WORD wParam;
LONG lParam;
{
    switch (message)
    {
    case WM_INITDIALOG:
         {
         /* initializes dialog for ADD or EDIT */
         switch (lParam)
              {
              case ADD:
                   SendDlgItemMessage (hDlg, IDC_EXTENSION,
                                       EM_LIMITTEXT, 3, NULL);
                   SendDlgItemMessage (hDlg, IDC_COMMAND,
                                       EM_LIMITTEXT, 127, NULL);
                   break;
              case EDIT:
                   {
                   int nCurSel;
                   char szExt[4];
                   char szCmd[128];
   
                   nCurSel = SendDlgItemMessage (hwnd, IDC_LIST_BOX,
                                                 LB_GETCURSEL, NULL,NULL);
                   SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_GETTEXT,
                                       nCurSel, (long)(LPSTR)szExt);
                   SetDlgItemText (hDlg, IDC_EXTENSION, szExt);
                   GetProfileString ("extensions", szExt, "", szCmd,128);
                   SendDlgItemMessage (hDlg, IDC_COMMAND,
                                       EM_LIMITTEXT, 127, NULL);
                   SetDlgItemText (hDlg, IDC_COMMAND, szCmd);
                   SendDlgItemMessage (hDlg, IDC_COMMAND, EM_SETSEL,
                                       NULL, MAKELONG(0,127));
                   }
                   break;
              }
         return (TRUE);
         }
         break;
    
    case WM_CLOSE:
         SendMessage (hDlg, WM_COMMAND, IDCANCEL, NULL);
         return (TRUE);

    case WM_COMMAND:
         switch (wParam)
         {
         case IDC_EXTENSION:
              EnableWindow(GetDlgItem(hDlg,IDOK), 
                  (GetWindowTextLength(GetDlgItem(hDlg,IDC_EXTENSION)) &&
                   GetWindowTextLength(GetDlgItem(hDlg,IDC_COMMAND))));
              break;
         case IDC_COMMAND:
              EnableWindow(GetDlgItem(hDlg,IDOK), 
                  (GetWindowTextLength(GetDlgItem(hDlg,IDC_EXTENSION)) &&
                   GetWindowTextLength(GetDlgItem(hDlg,IDC_COMMAND))));
              break;
         case IDOK:
              {
              char szExt[4];
              char szCmd[128];

              GetDlgItemText (hDlg, IDC_EXTENSION, szExt, 4);
              GetDlgItemText (hDlg, IDC_COMMAND, szCmd, 128);
              WriteProfileString ("extensions", szExt, szCmd);
              EndDialog(hDlg, IDOK);
              return(TRUE);
              break;
              }
         case IDCANCEL:
              {
              EndDialog(hDlg, IDCANCEL);
              return (TRUE);
              break;
              }
         case IDC_BROWSE:
              {
              DoDialog (hInst, IDD_BROWSE, hDlg, BrowseDlg, NULL);

              if (*szProgName)
                 {
                 char szExt[4];

                 GetDlgItemText (hDlg, IDC_EXTENSION, szExt, 4);
                 lstrcat (szProgName, " ^.");
                 lstrcat (szProgName, szExt);
                 SetDlgItemText (hDlg, IDC_COMMAND , szProgName);
                 }

              SendDlgItemMessage (hDlg, IDC_COMMAND, EM_SETSEL, 
                                  NULL, MAKELONG(0,128));
              SendDlgItemMessage (hDlg, IDOK,
                                  BM_SETSTYLE, (WORD) BS_DEFPUSHBUTTON,
                                  MAKELONG(NULL, TRUE));
              SendDlgItemMessage (hDlg, IDC_BROWSE,
                                  BM_SETSTYLE, (WORD) BS_PUSHBUTTON,
                                  MAKELONG(NULL, TRUE));
              SetFocus (GetDlgItem (hDlg, IDC_COMMAND));

              return (TRUE);
              break;
              }
         }
    }
    return (FALSE);
}


/***********************************************************************
 *                                                                     *
 *   FUNCTION: ListExts()                                              *
 *                                                                     *
 *   PURPOSE:  Lists file extensions with associations.                *
 *                                                                     *
 ***********************************************************************/
int NEAR ListExts()
{
    PSTR  psTemp=szListBuf;
    int   nBufSize=0;
    int   nIndex=0;
    int   nItemCount=0;

    /* empty the list box */
    SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_RESETCONTENT, NULL, NULL); 

    /* get list of [extension] entries as a series of null terminated  */
    /* strings packed into szListBuf.  nBufSize is set to the total    */
    /* number of characters packed into szListBuf.                     */
    nBufSize = GetProfileString ("extensions", NULL, NULL, szListBuf, 1024);

    /* if call failed, or no [extension] section found, return 0 */
    if (!nBufSize)
       return (0);

    /* set the psListItem array to point to the file associations */
    do
    {
    psListItem[nIndex] = psTemp;
    psTemp += lstrlen(psListItem[nIndex]) + 1;
    nIndex++;
    }
    while (psTemp < (szListBuf + nBufSize - 1));

    nItemCount = nIndex;
    nIndex = 0;

    /* turn off listbox drawing while adding items - stops flicker */
    SendDlgItemMessage (hwnd, IDC_LIST_BOX, WM_SETREDRAW, FALSE, NULL);

    /* add the file associations */
    while (nIndex < nItemCount)
       {
       AnsiUpper (psListItem[nIndex]);
       SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_ADDSTRING, NULL, 
                           (LONG)(LPSTR)psListItem[nIndex]);
       nIndex++;

       /* if it is the last item, turn drawing back on for display */
       if (nIndex == (nItemCount - 1))
          {
          SendDlgItemMessage (hwnd, IDC_LIST_BOX,
                              WM_SETREDRAW, TRUE, NULL);
          }
       }

    /* select the first item in the listbox initially */
    SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_SETCURSEL, 0, 0L);

    /* return the number of file associations processed */
    return (nItemCount);
}


/***********************************************************************
 *                                                                     *
 *   FUNCTION: ListApps()                                              *
 *                                                                     *
 *   PURPOSE:  Lists applications with profile data in WIN.INI.        *
 *                                                                     *
 ***********************************************************************/
int NEAR ListApps()
{
    int nIndex;

    /* empty the list box */
    SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_RESETCONTENT, NULL, NULL); 

    /* get the list of application headings */
    GetApps(hwnd);

    /* turn off listbox drawing while adding items - stops flicker */
    SendDlgItemMessage (hwnd, IDC_LIST_BOX, WM_SETREDRAW, FALSE, NULL);

    /* add the application headings found in GetApps() */
    for (nIndex = 0; nIndex < nAppQty; nIndex++)
       {
       SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_ADDSTRING, NULL, 
                           (LONG)(LPSTR)szAppList[nIndex]);

       /* if it is the last item, turn drawing back on for display */
       if (nIndex == (nAppQty - 1))
          {
          SendDlgItemMessage (hwnd, IDC_LIST_BOX,
                              WM_SETREDRAW, TRUE, NULL);
          }
       }

    /* select the first item in the listbox initially */
    SendDlgItemMessage (hwnd, IDC_LIST_BOX, LB_SETCURSEL, 0, 0L);

    /* return the number of application headings processed */
    return (nAppQty);
}


/***********************************************************************
 *                                                                     *
 *  FUNCTION   : GetApps (HWND)                                        *
 *                                                                     *
 *  PURPOSE    : Gets the list of applications from the WIN.INI file.  *
 *                                                                     *
 *  RETURNS    : TRUE if the file was successfully opened, else FALSE. *
 *                                                                     *
 **********************************************************************/
BOOL NEAR GetApps (hWnd)
HWND hWnd;
{
    char   szWinIni[255];    /* WIN.INI path & filename */
    int    hFile;            /* DOS file handle         */
    LONG   lLength;          /* length of file          */
    WORD   wStatus;          /* result of file I/O      */
    HANDLE hBuffer;          /* HANDLE to file buffer   */
    PSTR   pBuffer;          /* pointer to file buffer  */
    PSTR   pBuffPtr;         /* location in pBuffer     */
    PSTR   pStartKey =       /* start of app name '['   */
              (PSTR)TRUE;
    PSTR   pEndKey;          /* end of app name ']'     */
    int    nAppNum = 0;      /* holder for index        */
    BOOL   bConst;           /* TRUE if app is constant */
    int    nTemp;            /* general purpose temp    */

    /* get the path for WIN.INI */
    GetWindowsDirectory ((LPSTR)szWinIni, 255);

    /* add a '\' if not already there */
    if (szWinIni[lstrlen((LPSTR)szWinIni) - 1] != '\\')
       lstrcat((LPSTR)szWinIni,(LPSTR)"\\");

    /* add file name 'WIN.INI' to path */
    lstrcat((LPSTR)szWinIni,(LPSTR)"WIN.INI");

    /* open the file */
    hFile = _lopen (szWinIni, OF_READ);

    /* error if file could not be opened */
    if (hFile < 0)
       {
        wsprintf(str, "Cannot read file: %s.", (LPSTR)szWinIni);
        MessageBox(hWnd, str, "ProgSet", MB_OK | MB_ICONEXCLAMATION);
        return (FALSE);  /* error opening file */
       }

    /* get file length */
    lLength = _llseek (hFile,0L,2); 
    _llseek (hFile, 0L, 0);

    /* error if file is too large */
    if (lLength > MAXFILESIZE)
       {
        MessageBox(hWnd, "The WIN.INI file is too large", 
                   "ProgSet", MB_OK | MB_ICONHAND);
        return (FALSE);
       }
 
    /* allocate buffer to the length of the file + 1 */
    hBuffer = LocalAlloc(LHND, (WORD)lLength + 1);

    /* error if buffer could not be allocated */
    if (!hBuffer) 
       {
        MessageBox(hWnd, "Not enough memory to load WIN.INI", 
                   "ProgSet", MB_OK | MB_ICONHAND);
        return (FALSE);
       }

    /* set the hourglass cursor */
    hSaveCursor = SetCursor(hHourGlass);

    /* lock the buffer and receive a pointer to it */
    pBuffer = LocalLock(hBuffer);

    /* read the file into the buffer */
    wStatus = _lread(hFile, (LPSTR)pBuffer, (WORD)lLength);
    _lclose(hFile);

    /* error if # bytes read not equal to file size */
    if (wStatus != lLength)
       {
        wsprintf(str, "Error reading %s.", (LPSTR)szWinIni);
        MessageBox(hWnd, str, "ProgSet", MB_OK | MB_ICONEXCLAMATION);

        /* restore the cursor and return */
        SetCursor(hSaveCursor);
        return (FALSE);
       }

    /* zero terminate the buffer */
    pBuffer[lLength] = 0;

    /* fill application name array */
    pBuffPtr = pBuffer;
    nAppNum = 0;
    while (pStartKey)
       {
       /* This loop locates application headings */
       pStartKey = strchr(pBuffPtr, '[');       
       if (pStartKey)
          {
          /* start of application heading found, look for end */
          pStartKey++;
          for (pEndKey=pStartKey;*pEndKey!=']' && *pEndKey!='\n';pEndKey++);

          /* zero terminate the app heading for later string operations */
          *pEndKey = 0;

          /* see if this application heading should be protected */
          for (bConst=FALSE,nTemp = 0; nTemp < NUMCONST; nTemp++)
              {
              /* upshift application heading and compare to constant */
              AnsiUpper (pStartKey);
              if (lstrcmp (pStartKey, szConstList[nTemp]) == 0)
                 bConst = TRUE;
              }

          /* is the app heading protected AND does it start in col 1? */
          if ((!bConst) && (pStartKey-1==pBuffer || *(pStartKey-2)=='\n'))    
              {
              /* add the application heading to the list.     */
              lstrcpy(szAppList[nAppNum], pStartKey);         
              nAppNum++;
              }

          /* advance past the end of the previous application heading */
          pBuffPtr = pEndKey + 1;
          }
       }

    /* set the number of applications added to the list */
    nAppQty = nAppNum;

    /* unlock and release buffer */
    LocalUnlock (hBuffer);
    LocalFree (hBuffer);

    /* restore the cursor */
    SetCursor(hSaveCursor);

    return (TRUE);
}
