tWin32 installer updates - vaccinewars - be a doctor and try to vaccinate the world
 (HTM) git clone git://src.adamsgaard.dk/vaccinewars
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 40ec03f43f7910bd549c4324e545089c2c24cee4
 (DIR) parent a5f688bfb27f69264afa2f75ea6975f7ad2ab24b
 (HTM) Author: Ben Webb <ben@salilab.org>
       Date:   Mon, 24 Sep 2001 22:37:10 +0000
       
       Win32 installer updates
       
       
       Diffstat:
         M win32/Makefile                      |      27 +++++++++++++++++++--------
         M win32/contid.h                      |       7 +++++++
         A win32/dialogs.c                     |     169 +++++++++++++++++++++++++++++++
         M win32/dialogs.rc                    |      21 ++++++++++++++++++---
         M win32/filelist                      |      44 +++++++++++++++++++++++++++----
         A win32/guifunc.c                     |      70 +++++++++++++++++++++++++++++++
         A win32/guifunc.h                     |       4 ++++
         A win32/install.c                     |      37 +++++++++++++++++++++++++++++++
         A win32/install.rc                    |      21 +++++++++++++++++++++
         A win32/makeinstall.c                 |     228 +++++++++++++++++++++++++++++++
         A win32/setup.c                       |     656 +++++++++++++++++++++++++++++++
         A win32/uninstall.c                   |     290 +++++++++++++++++++++++++++++++
         M win32/uninstall.rc                  |      13 +++++++++++++
         A win32/util.c                        |     358 +++++++++++++++++++++++++++++++
         A win32/util.h                        |      65 +++++++++++++++++++++++++++++++
       
       15 files changed, 1994 insertions(+), 16 deletions(-)
       ---
 (DIR) diff --git a/win32/Makefile b/win32/Makefile
       t@@ -1,22 +1,33 @@
        all: setup makeinstall uninstall
        
       -setup: setup.o setup.res
       -        gcc -Wall -mno-cygwin -mwindows -o setup setup.o setup.res zlib/libz.a -lcomctl32 -lole32 -luuid
       +setup: setup.o util.o guifunc.o setup.res
       +        gcc -Wall -mno-cygwin -mwindows -o setup setup.o util.o guifunc.o setup.res zlib/libz.a -lcomctl32 -lole32 -luuid
       +        strip setup.exe
        
       -uninstall: uninstall.o uninstall.res
       -        gcc -Wall -mno-cygwin -o uninstall uninstall.o uninstall.res 
       +uninstall: uninstall.o util.o guifunc.o uninstall.res
       +        gcc -Wall -mno-cygwin -mwindows -o uninstall uninstall.o util.o guifunc.o uninstall.res 
       +        strip uninstall.exe
        
       -uninstall.o: uninstall.c
       +uninstall.o: uninstall.c util.h guifunc.h
                gcc -Wall -mno-cygwin -c uninstall.c
        
        uninstall.res: uninstall.rc
                windres -O coff -o uninstall.res uninstall.rc
        
       -setup.o: setup.c contid.h
       +setup.o: setup.c contid.h util.h guifunc.h
                gcc -Wall -mno-cygwin -c setup.c
        
       +util.o: util.c util.h
       +        gcc -Wall -mno-cygwin -c util.c
       +
       +guifunc.o: guifunc.c guifunc.h
       +        gcc -Wall -mno-cygwin -c guifunc.c
       +
        setup.res: setup.rc dialogs.rc contid.h manifest installfiles.gz
                windres -O coff -o setup.res setup.rc
        
       -makeinstall: makeinstall.c
       -        gcc -Wall -mno-cygwin -o makeinstall makeinstall.c zlib/libz.a
       +makeinstall: makeinstall.o util.o
       +        gcc -Wall -mno-cygwin -o makeinstall makeinstall.o util.o zlib/libz.a
       +
       +makeinstall.o: makeinstall.c util.h
       +        gcc -Wall -mno-cygwin -c makeinstall.c
 (DIR) diff --git a/win32/contid.h b/win32/contid.h
       t@@ -7,3 +7,10 @@
        #define ST_FILELIST 207
        #define ST_COMPLETE 208
        #define ST_EXIT     209
       +#define ED_FOLDER   210
       +#define LB_FOLDLIST 211
       +#define CB_DESKTOP  212
       +#define ST_INSTDIR  213
       +#define ST_DELSTAT  214
       +#define ST_DELDONE  215
       +#define BT_DELOK    216
 (DIR) diff --git a/win32/dialogs.c b/win32/dialogs.c
       t@@ -0,0 +1,169 @@
       +#include <windows.h>
       +#include <commctrl.h>
       +#include "contid.h"
       +
       +#define NUMDIALOGS 4
       +
       +HWND mainDlg[NUMDIALOGS];
       +
       +int CurrentDialog;
       +
       +HINSTANCE hInst=NULL;
       +
       +BOOL CALLBACK enumFunc(HWND hWnd,LPARAM lParam) {
       +  HFONT GuiFont;
       +  GuiFont=(HFONT)lParam;
       +  SendMessage(hWnd,WM_SETFONT,(WPARAM)GuiFont,MAKELPARAM(FALSE,0));
       +  return TRUE;
       +}
       +
       +void ShowNewDialog(int NewDialog) {
       +  RECT DeskRect,OurRect;
       +  int newX,newY;
       +  HWND hWnd;
       +  if (NewDialog<0 || NewDialog>=NUMDIALOGS) return;
       +
       +  hWnd=mainDlg[NewDialog];
       +  if (GetWindowRect(hWnd,&OurRect) &&
       +      GetWindowRect(GetDesktopWindow(),&DeskRect)) {
       +    newX = (DeskRect.left+DeskRect.right+OurRect.left-OurRect.right)/2;
       +    newY = (DeskRect.top+DeskRect.bottom+OurRect.top-OurRect.bottom)/2;
       +    SetWindowPos(hWnd,HWND_TOP,newX,newY,0,0,SWP_NOSIZE);
       +  }
       +  ShowWindow(hWnd,SW_SHOW);
       +
       +  if (CurrentDialog!=-1) ShowWindow(mainDlg[CurrentDialog],SW_HIDE);
       +  CurrentDialog=NewDialog;
       +}
       +
       +LRESULT CALLBACK GtkSepProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) {
       +   PAINTSTRUCT ps;
       +   HPEN oldpen,dkpen,ltpen;
       +   RECT rect;
       +   HDC hDC;
       +   if (msg==WM_PAINT) {
       +      if (GetUpdateRect(hwnd,NULL,TRUE)) {
       +         BeginPaint(hwnd,&ps);
       +         GetClientRect(hwnd,&rect);
       +         hDC=ps.hdc;
       +         ltpen=CreatePen(PS_SOLID,0,(COLORREF)GetSysColor(COLOR_3DHILIGHT));
       +         dkpen=CreatePen(PS_SOLID,0,(COLORREF)GetSysColor(COLOR_3DSHADOW));
       +
       +         if (rect.right > rect.bottom) {
       +            oldpen=SelectObject(hDC,dkpen);
       +            MoveToEx(hDC,rect.left,rect.top,NULL);
       +            LineTo(hDC,rect.right,rect.top);
       +
       +            SelectObject(hDC,ltpen);
       +            MoveToEx(hDC,rect.left,rect.top+1,NULL);
       +            LineTo(hDC,rect.right,rect.top+1);
       +         } else {
       +            oldpen=SelectObject(hDC,dkpen);
       +            MoveToEx(hDC,rect.left,rect.top,NULL);
       +            LineTo(hDC,rect.left,rect.bottom);
       +
       +            SelectObject(hDC,ltpen);
       +            MoveToEx(hDC,rect.left+1,rect.top,NULL);
       +            LineTo(hDC,rect.left+1,rect.bottom);
       +         }
       +
       +         SelectObject(hDC,oldpen);
       +         DeleteObject(ltpen); DeleteObject(dkpen);
       +         EndPaint(hwnd,&ps);
       +      }
       +      return TRUE;
       +   } else return DefWindowProc(hwnd,msg,wParam,lParam);
       +}
       +
       +void ConditionalExit(HWND hWnd) {
       +  if (MessageBox(hWnd,"This will exit without installing any new software on "
       +                 "your machine.\nAre you sure you want to quit?\n\n(You can "
       +                 "continue the installation at a\nlater date simply by "
       +                 "running this program again.)","Exit Install",
       +                 MB_YESNO|MB_ICONQUESTION)==IDYES) {
       +    PostQuitMessage(0);
       +  }
       +}
       +
       +BOOL CALLBACK MainDlgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) {
       +  switch(msg) {
       +    case WM_INITDIALOG:
       +      return TRUE;
       +    case WM_COMMAND:
       +      if (HIWORD(wParam)==BN_CLICKED && lParam) {
       +        switch(LOWORD(wParam)) {
       +          case BT_CANCEL: ConditionalExit(hWnd); break;
       +          case BT_NEXT:   ShowNewDialog(CurrentDialog+1); break;
       +          case BT_BACK:   ShowNewDialog(CurrentDialog-1); break;
       +          case BT_FINISH: PostQuitMessage(0); break;
       +//        case BT_BROWSE: SelectInstDir(); break;
       +        }
       +      }
       +      break;
       +    case WM_CLOSE:
       +      ConditionalExit(hWnd);
       +      return TRUE;
       +  }
       +  return FALSE;
       +}
       +
       +int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
       +                     LPSTR lpszCmdParam,int nCmdShow) {
       +  MSG msg;
       +  HWND mainWin;
       +  DWORD err;
       +  LPVOID lpMsgBuf;
       +  WNDCLASS wc;
       +  int i;
       +  BOOL Handled;
       +  HFONT GuiFont;
       +//InitCommonControls();
       +
       +  hInst = hInstance;
       +
       +  if (!hPrevInstance) {
       +    wc.style         = CS_HREDRAW|CS_VREDRAW;
       +    wc.lpfnWndProc   = GtkSepProc;
       +    wc.cbClsExtra    = 0;
       +    wc.cbWndExtra    = 0;
       +    wc.hInstance     = hInstance;
       +    wc.hIcon         = NULL;
       +    wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
       +    wc.hbrBackground = (HBRUSH)(1+COLOR_BTNFACE);
       +    wc.lpszMenuName  = NULL;
       +    wc.lpszClassName = "WC_GTKSEP";
       +    RegisterClass(&wc);
       +  }
       +
       +  for (i=0;i<NUMDIALOGS;i++) {
       +    mainDlg[i] = CreateDialog(hInst,MAKEINTRESOURCE(i+1),NULL,MainDlgProc);
       +  }
       +
       +  GuiFont=(HFONT)GetStockObject(DEFAULT_GUI_FONT);
       +  if (GuiFont) for (i=0;i<NUMDIALOGS;i++) {
       +    EnumChildWindows(mainDlg[i],enumFunc,(LPARAM)GuiFont);
       +  }
       +  CurrentDialog=-1;
       +  ShowNewDialog(0);
       +
       +/*if (!mainWin) {
       +    err=GetLastError();
       +    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
       +                  NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
       +                  (LPTSTR)&lpMsgBuf,0,NULL);
       +    MessageBox(NULL,lpMsgBuf,"Error",MB_OK | MB_ICONSTOP);
       +    LocalFree(lpMsgBuf);
       +    return 0;
       +  }*/
       +
       +  while (GetMessage(&msg,NULL,0,0)) {
       +    Handled=FALSE;
       +    for (i=0;i<NUMDIALOGS && !Handled;i++) {
       +      Handled=IsDialogMessage(mainDlg[i],&msg);
       +    }
       +    if (!Handled) {
       +      TranslateMessage(&msg);
       +      DispatchMessage(&msg);
       +    }
       +  }
       +}
 (DIR) diff --git a/win32/dialogs.rc b/win32/dialogs.rc
       t@@ -23,7 +23,7 @@ BEGIN
                CONTROL "", ED_LICENCE, "EDIT", ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_VSCROLL, 22, 45, 195, 75
        END
        
       -3 DIALOG 17, 40, 239, 162
       +4 DIALOG 17, 40, 239, 162
        STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
        CAPTION "Installation directory"
        BEGIN
       t@@ -35,12 +35,12 @@ BEGIN
                LTEXT "To install in this directory, select ""Next"".", -1, 22, 33, 195, 9, WS_CHILD | WS_VISIBLE | WS_GROUP
                LTEXT "To install in a different directory, select ""Browse"" and select the desired location.", -1, 22, 51, 195, 18, WS_CHILD | WS_VISIBLE | WS_GROUP
                GROUPBOX "Destination directory", 105, 22, 84, 195, 35, WS_CHILD | WS_VISIBLE | WS_TABSTOP
       -        LTEXT "C:\\Windows\\Program Files\\dopewars-1.5.2\\", -1, 28, 100, 142, 8, WS_CHILD | WS_VISIBLE | WS_GROUP
       +        LTEXT "", ST_INSTDIR, 28, 100, 142, 8, WS_CHILD | WS_VISIBLE | WS_GROUP
                PUSHBUTTON "B&rowse...", BT_BROWSE, 171, 98, 39, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP
        END
        
        
       -4 DIALOG 17, 40, 239, 162
       +5 DIALOG 17, 40, 239, 162
        STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
        CAPTION "Installing..."
        BEGIN
       t@@ -56,3 +56,18 @@ END
        
        mainicon ICON "setup.ico"
        
       +3 DIALOG 17, 40, 239, 162
       +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
       +CAPTION "Shortcuts"
       +BEGIN
       +        LTEXT "Program icons will be added to the Program Folder on the Start Menu shown below. You can enter a new folder name, or select one from the Existing Folders list.", -1, 22, 7, 195, 25, WS_CHILD | WS_VISIBLE | WS_GROUP
       +        LTEXT "&Program Folder:", -1, 22, 35, 68, 8, WS_CHILD | WS_VISIBLE | WS_GROUP
       +        CONTROL "", ED_FOLDER, "EDIT", ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 22, 45, 195, 10
       +        LTEXT "E&xisting Folders:", -1, 22, 61, 92, 8, WS_CHILD | WS_VISIBLE | WS_GROUP
       +        CONTROL "", LB_FOLDLIST, "LISTBOX", LBS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP, 22, 71, 195, 46
       +        CONTROL "Create desktop icons as well", CB_DESKTOP, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 22, 121, 110, 10
       +        CONTROL "", 101, "WC_GTKSEP", 2 | WS_CHILD | WS_VISIBLE, 7, 135, 225, 2
       +        PUSHBUTTON "< &Back", BT_BACK, 105, 143, 39, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP
       +        PUSHBUTTON "&Next >", BT_NEXT, 144, 143, 39, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP
       +        PUSHBUTTON "Cancel", BT_CANCEL, 191, 143, 39, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP
       +END
 (DIR) diff --git a/win32/filelist b/win32/filelist
       t@@ -1,3 +1,10 @@
       +[product]
       +dopewars-1.5.2
       +
       +[instdir]
       +C:\Program Files\dopewars-1.5.2
       +
       +[install]
        licence.txt
        aiplayer.html
        clientplay.html
       t@@ -5,16 +12,43 @@ commandline.html
        configfile.html
        credits.html
        developer.html
       -dopewars-config.txt
       -dopewars.exe
       -example-cfg.txt
       -glib-1.3.dll
        i18n.html
        index.html
        installation.html
        metaserver.html
       -readme.txt
        server.html
        servercommands.html
        windows.html
       +dopewars-config.txt
       +dopewars.exe
       +example-cfg.txt
       +glib-1.3.dll
       +readme.txt
        uninstall.exe
       +
       +[extrafiles]
       +dopewars-log.txt
       +dopewars.sco
       +
       +[startmenudir]
       +dopewars-1.5.2
       +
       +[startmenu]
       +dopewars.lnk
       +dopewars.exe
       +
       +dopewars server.lnk
       +dopewars.exe
       +-s
       +
       +dopewars help.lnk
       +index.html
       +
       +
       +[desktop]
       +dopewars.lnk
       +dopewars.exe
       +
       +dopewars server.lnk
       +dopewars.exe
       +-s
 (DIR) diff --git a/win32/guifunc.c b/win32/guifunc.c
       t@@ -0,0 +1,70 @@
       +#include <windows.h>
       +#include "guifunc.h"
       +
       +static LRESULT CALLBACK GtkSepProc(HWND hwnd,UINT msg,WPARAM wParam,
       +                                   LPARAM lParam) {
       +   PAINTSTRUCT ps;
       +   HPEN oldpen,dkpen,ltpen;
       +   RECT rect;
       +   HDC hDC;
       +   if (msg==WM_PAINT) {
       +      if (GetUpdateRect(hwnd,NULL,TRUE)) {
       +         BeginPaint(hwnd,&ps);
       +         GetClientRect(hwnd,&rect);
       +         hDC=ps.hdc;
       +         ltpen=CreatePen(PS_SOLID,0,(COLORREF)GetSysColor(COLOR_3DHILIGHT));
       +         dkpen=CreatePen(PS_SOLID,0,(COLORREF)GetSysColor(COLOR_3DSHADOW));
       +
       +         if (rect.right > rect.bottom) {
       +            oldpen=SelectObject(hDC,dkpen);
       +            MoveToEx(hDC,rect.left,rect.top,NULL);
       +            LineTo(hDC,rect.right,rect.top);
       +
       +            SelectObject(hDC,ltpen);
       +            MoveToEx(hDC,rect.left,rect.top+1,NULL);
       +            LineTo(hDC,rect.right,rect.top+1);
       +         } else {
       +            oldpen=SelectObject(hDC,dkpen);
       +            MoveToEx(hDC,rect.left,rect.top,NULL);
       +            LineTo(hDC,rect.left,rect.bottom);
       +
       +            SelectObject(hDC,ltpen);
       +            MoveToEx(hDC,rect.left+1,rect.top,NULL);
       +            LineTo(hDC,rect.left+1,rect.bottom);
       +         }
       +
       +         SelectObject(hDC,oldpen);
       +         DeleteObject(ltpen); DeleteObject(dkpen);
       +         EndPaint(hwnd,&ps);
       +      }
       +      return TRUE;
       +   } else return DefWindowProc(hwnd,msg,wParam,lParam);
       +}
       +
       +void RegisterSepClass(HINSTANCE hInstance) {
       +  WNDCLASS wc;
       +  wc.style         = CS_HREDRAW|CS_VREDRAW;
       +  wc.lpfnWndProc   = GtkSepProc;
       +  wc.cbClsExtra    = 0;
       +  wc.cbWndExtra    = 0;
       +  wc.hInstance     = hInstance;
       +  wc.hIcon         = NULL;
       +  wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
       +  wc.hbrBackground = (HBRUSH)(1+COLOR_BTNFACE);
       +  wc.lpszMenuName  = NULL;
       +  wc.lpszClassName = "WC_GTKSEP";
       +  RegisterClass(&wc);
       +}
       +
       +static BOOL CALLBACK enumFunc(HWND hWnd,LPARAM lParam) {
       +  HFONT GuiFont;
       +  GuiFont=(HFONT)lParam;
       +  SendMessage(hWnd,WM_SETFONT,(WPARAM)GuiFont,MAKELPARAM(FALSE,0));
       +  return TRUE;
       +}
       +
       +void SetGuiFont(HWND hWnd) {
       +  HFONT GuiFont;
       +  GuiFont=(HFONT)GetStockObject(DEFAULT_GUI_FONT);
       +  if (GuiFont) EnumChildWindows(hWnd,enumFunc,(LPARAM)GuiFont);
       +}
 (DIR) diff --git a/win32/guifunc.h b/win32/guifunc.h
       t@@ -0,0 +1,4 @@
       +#include <windows.h>
       +
       +void RegisterSepClass(HINSTANCE hInstance);
       +void SetGuiFont(HWND hWnd);
 (DIR) diff --git a/win32/install.c b/win32/install.c
       t@@ -0,0 +1,37 @@
       +#include <stdio.h>
       +#include <windows.h>
       +
       +int main() {
       +  HRSRC hrsrc;
       +  HGLOBAL hglobal;
       +  DWORD ressize,i;
       +  LPVOID respt;
       +  HANDLE fout;
       +  DWORD byteswritten;
       +
       +  printf("Looking for resource...\n");
       +
       +  hrsrc = FindResource(NULL,MAKEINTRESOURCE(10),"INSTFILE");
       +  if (!hrsrc) { printf("Could not find resource!\n"); return 1; }
       +
       +  ressize=SizeofResource(NULL,hrsrc);
       +  printf("Resource found; size returned = %ld bytes\n",ressize);
       +
       +  hglobal = LoadResource(NULL,hrsrc);
       +  if (!hglobal) { printf("Could not load resource!\n"); return 1; }
       +
       +  respt = LockResource(hglobal);
       +  if (!respt) { printf("Could not lock resource!\n"); return 1; }
       +
       +  fout = CreateFile("windowsout.html",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
       +                    0,NULL);
       +  if (!fout) { printf("Could not open file\n"); return 1; }
       +
       +  if (!WriteFile(fout,respt,ressize,&byteswritten,NULL)) {
       +    printf("Write to file failed - %ld of %ld bytes written\n",
       +           byteswritten,ressize);
       +  }
       +  CloseHandle(fout);
       +  
       +  return 0;
       +}
 (DIR) diff --git a/win32/install.rc b/win32/install.rc
       t@@ -0,0 +1,21 @@
       +0   INSTLIST "install-list"
       +
       +1   INSTFILE "aiplayer.html"
       +2   INSTFILE "clientplay.html"
       +3   INSTFILE "commandline.html"
       +4   INSTFILE "configfile.html"
       +5   INSTFILE "credits.html"
       +6   INSTFILE "developer.html"
       +7   INSTFILE "i18n.html"
       +8   INSTFILE "index.html"
       +9   INSTFILE "dopewars-config.txt"
       +10  INSTFILE "dopewars.exe"
       +11  INSTFILE "example-cfg.txt"
       +12  INSTFILE "glib-1.3.dll"
       +13  INSTFILE "installation.html"
       +14  INSTFILE "metaserver.html"
       +15  INSTFILE "server.html"
       +16  INSTFILE "servercommands.html"
       +17  INSTFILE "windows.html"
       +18  INSTFILE "licence.txt"
       +19  INSTFILE "readme.txt"
 (DIR) diff --git a/win32/makeinstall.c b/win32/makeinstall.c
       t@@ -0,0 +1,228 @@
       +#include <windows.h>
       +#include <stdio.h>
       +#include <zlib.h>
       +#include "util.h"
       +
       +char *read_line(HANDLE hin) {
       +  char *buf;
       +  int bufsize=32,strind=0;
       +  DWORD bytes_read;
       +  buf = bmalloc(bufsize);
       +
       +  while (1) {
       +    if (!ReadFile(hin,&buf[strind],1,&bytes_read,NULL)) {
       +      printf("Read error\n"); break;
       +    }
       +    if (bytes_read==0) { buf[strind]='\0'; break; }
       +    else if (buf[strind]=='\r') continue;
       +    else if (buf[strind]=='\n') { buf[strind++]='\0'; break; }
       +    else {
       +      strind++;
       +      if (strind>=bufsize) {
       +        bufsize*=2;
       +        buf = brealloc(buf,bufsize);
       +      }
       +    }
       +  }
       +  if (strind==0) { bfree(buf); return NULL; }
       +  else return buf;
       +}
       +
       +InstData *ReadInstallData() {
       +  HANDLE fin;
       +  char *line,*line2,*line3;
       +  InstFiles *lastinst=NULL,*lastextra=NULL;
       +  InstLink *lastmenu=NULL,*lastdesktop=NULL;
       +  InstData *idata;
       +  int i;
       +  enum {
       +    S_PRODUCT=0,S_INSTDIR,S_INSTALL,S_EXTRA,S_STARTMENUDIR,
       +    S_STARTMENU,S_DESKTOP,
       +    S_NONE
       +  } section=S_NONE;
       +  char *titles[S_NONE] = {
       +    "[product]","[instdir]", "[install]","[extrafiles]","[startmenudir]",
       +    "[startmenu]","[desktop]"
       +  };
       +
       +  idata = bmalloc(sizeof(InstData));
       +  idata->installdir = idata->startmenudir = NULL;
       +  idata->instfiles = idata->extrafiles = NULL;
       +  idata->startmenu = idata->desktop = NULL;
       +
       +  fin = CreateFile("filelist",GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
       +
       +  if (fin) {
       +    while (1) {
       +      line=read_line(fin);
       +      if (!line) break;
       +      if (line[0]=='\0') { bfree(line); continue; }
       +
       +      for (i=S_PRODUCT;i<S_NONE;i++) {
       +        if (strcmp(line,titles[i])==0) {
       +          section=i; break;
       +        }
       +      }
       +      if (i<S_NONE) { bfree(line); continue; }
       +
       +      switch(section) {
       +        case S_NONE:
       +          printf("Bad line %s\n",line); exit(1);
       +        case S_PRODUCT:
       +printf("product ID = %s\n",line);
       +          idata->product = line; break;
       +        case S_INSTDIR:
       +printf("install dir = %s\n",line);
       +          idata->installdir = line; break;
       +        case S_STARTMENUDIR:
       +printf("start menu dir = %s\n",line);
       +          idata->startmenudir = line; break;
       +        case S_INSTALL:
       +          AddInstFiles(line,0,&lastinst,&idata->instfiles);
       +          break;
       +        case S_EXTRA:
       +          AddInstFiles(line,0,&lastextra,&idata->extrafiles);
       +          break;
       +        case S_STARTMENU:
       +          line2=read_line(fin); line3=read_line(fin);
       +printf("start menu entry = %s/%s/%s\n",line,line2,line3);
       +          AddInstLink(line,line2,line3,&lastmenu,&idata->startmenu);
       +          break;
       +        case S_DESKTOP:
       +          line2=read_line(fin); line3=read_line(fin);
       +printf("desktop entry = %s/%s/%s\n",line,line2,line3);
       +          AddInstLink(line,line2,line3,&lastdesktop,&idata->desktop);
       +          break;
       +      }
       +    }
       +    CloseHandle(fin);
       +  }
       +
       +  if (idata->installdir && idata->startmenudir && idata->product) {
       +    return idata;
       +  } else {
       +    printf("No directories specified\n");
       +    exit(1);
       +  }
       +}
       +
       +#define BUFFER_SIZE (32*1024)
       +#define COMPRESSION Z_BEST_COMPRESSION
       +
       +void OpenNextFile(InstFiles *filelist,InstFiles **listpt,HANDLE *fin) {
       +  if (*fin) CloseHandle(*fin);
       +  *fin=NULL;
       +
       +  if (!*listpt) *listpt = filelist;
       +  else *listpt = (*listpt)->next;
       +
       +  if (*listpt) *fin = CreateFile((*listpt)->filename,GENERIC_READ,0,NULL,
       +                                 OPEN_EXISTING,0,NULL);
       +}
       +
       +int main() {
       +  HANDLE fout,fin;
       +  DWORD bytes_read,bytes_written;
       +  InstData *idata;
       +  InstFiles *filept;
       +  char *inbuf,*outbuf;
       +  int status,count;
       +  bstr *str;
       +  z_stream z;
       +
       +  idata=ReadInstallData();
       +
       +  fout = CreateFile("installfiles.gz",GENERIC_WRITE,0,NULL,
       +                    CREATE_ALWAYS,0,NULL);
       +
       +  outbuf = bmalloc(BUFFER_SIZE);
       +  inbuf = bmalloc(BUFFER_SIZE);
       +
       +  z.zalloc = Z_NULL;
       +  z.zfree = Z_NULL;
       +  z.opaque = Z_NULL;
       +  deflateInit(&z,COMPRESSION);
       +  z.avail_in=0;
       +  z.next_out = outbuf;
       +  z.avail_out = BUFFER_SIZE;
       +
       +  filept=NULL;
       +  fin=NULL;
       +  OpenNextFile(idata->instfiles,&filept,&fin);
       +  if (!fin) { printf("Cannot open file\n"); return 1; }
       +
       +  while (fin) {
       +    if (z.avail_in==0) {
       +      z.next_in = inbuf;
       +      bytes_read=0;
       +      while (!bytes_read && fin) {
       +        if (!ReadFile(fin,inbuf,BUFFER_SIZE,&bytes_read,NULL)) {
       +          printf("Read error\n"); break;
       +        }
       +        filept->filesize+=bytes_read;
       +        if (!bytes_read) OpenNextFile(idata->instfiles,&filept,&fin);
       +      }
       +      z.avail_in = bytes_read;
       +    }
       +    if (z.avail_in==0) {
       +      status = deflate(&z,Z_FINISH);
       +      count = BUFFER_SIZE - z.avail_out;
       +      if (!WriteFile(fout,outbuf,count,&bytes_written,NULL)) {
       +        printf("Write error\n");
       +      }
       +      break;
       +    }
       +    status = deflate(&z,Z_NO_FLUSH);
       +    count = BUFFER_SIZE - z.avail_out;
       +    if (!WriteFile(fout,outbuf,count,&bytes_written,NULL)) {
       +      printf("Write error\n");
       +    }
       +    z.next_out = outbuf;
       +    z.avail_out = BUFFER_SIZE;
       +  }
       +
       +  printf("Written compressed data: raw %lu, compressed %lu\n",
       +         z.total_in,z.total_out);
       +  bytes_written = z.total_out;
       +  deflateEnd(&z);
       +
       +  CloseHandle(fout);
       +
       +  fout = CreateFile("manifest",GENERIC_WRITE,0,NULL,
       +                    CREATE_ALWAYS,0,NULL);
       +  if (!fout) printf("Cannot create file list\n");
       +
       +  str=bstr_new();
       +  bstr_setlength(str,0);
       +  bstr_append_long(str,bytes_written);
       +  if (!WriteFile(fout,str->text,str->length+1,&bytes_written,NULL)) {
       +    printf("Write error\n");
       +  }
       +
       +  if (!WriteFile(fout,idata->product,strlen(idata->product)+1,
       +                 &bytes_written,NULL)) {
       +    printf("Write error\n");
       +  }
       +  if (!WriteFile(fout,idata->installdir,strlen(idata->installdir)+1,
       +                 &bytes_written,NULL)) {
       +    printf("Write error\n");
       +  }
       +  if (!WriteFile(fout,idata->startmenudir,strlen(idata->startmenudir)+1,
       +                 &bytes_written,NULL)) {
       +    printf("Write error\n");
       +  }
       +
       +  WriteFileList(fout,idata->instfiles);
       +  WriteFileList(fout,idata->extrafiles);
       +
       +  WriteLinkList(fout,idata->startmenu);
       +  WriteLinkList(fout,idata->desktop);
       +
       +  CloseHandle(fout);
       +  bfree(inbuf);
       +  bfree(outbuf);
       +
       +  FreeInstData(idata,TRUE);
       +
       +  return 0;
       +}
 (DIR) diff --git a/win32/setup.c b/win32/setup.c
       t@@ -0,0 +1,656 @@
       +#include <windows.h>
       +#include <commctrl.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <zlib.h>
       +#include <shlobj.h>
       +#include "contid.h"
       +#include "guifunc.h"
       +#include "util.h"
       +
       +typedef enum {
       +  DL_INTRO=0, DL_LICENCE,DL_SHORTCUTS,DL_INSTALLDIR,DL_DOINSTALL,
       +  DL_NUM
       +} DialogType;
       +
       +InstData *idata;
       +HWND mainDlg[DL_NUM];
       +DialogType CurrentDialog;
       +HINSTANCE hInst=NULL;
       +
       +DWORD WINAPI DoInstall(LPVOID lpParam);
       +
       +void ShowNewDialog(DialogType NewDialog) {
       +  RECT DeskRect,OurRect;
       +  int newX,newY;
       +  HWND hWnd;
       +  HANDLE hThread;
       +  DWORD threadID;
       +  if (NewDialog<0 || NewDialog>=DL_NUM) return;
       +
       +  hWnd=mainDlg[NewDialog];
       +  if (GetWindowRect(hWnd,&OurRect) &&
       +      GetWindowRect(GetDesktopWindow(),&DeskRect)) {
       +    newX = (DeskRect.left+DeskRect.right+OurRect.left-OurRect.right)/2;
       +    newY = (DeskRect.top+DeskRect.bottom+OurRect.top-OurRect.bottom)/2;
       +    SetWindowPos(hWnd,HWND_TOP,newX,newY,0,0,SWP_NOSIZE);
       +  }
       +  ShowWindow(hWnd,SW_SHOW);
       +
       +  if (CurrentDialog!=DL_NUM) ShowWindow(mainDlg[CurrentDialog],SW_HIDE);
       +  CurrentDialog=NewDialog;
       +
       +  if (NewDialog==DL_DOINSTALL) {
       +    hThread = CreateThread(NULL,0,DoInstall,NULL,0,&threadID);
       +  }
       +}
       +
       +void SelectInstDir(HWND parent) {
       +  BROWSEINFO bi = { 0 };
       +  TCHAR path[MAX_PATH];
       +  LPITEMIDLIST pidl;
       +  IMalloc *imalloc=0;
       +
       +  bi.lpszTitle = "Pick a directory";
       +  bi.pszDisplayName = path;
       +  bi.ulFlags = BIF_STATUSTEXT|BIF_RETURNONLYFSDIRS|BIF_DONTGOBELOWDOMAIN;
       +  pidl = SHBrowseForFolder(&bi);
       +  if (pidl) {
       +    if (SUCCEEDED(SHGetMalloc(&imalloc))) {
       +//    imalloc->free(pidl); imalloc->release();
       +    }
       +  }
       +/*OPENFILENAME ofn;
       +  char lpstrFile[200]="";
       +
       +  ofn.lStructSize = sizeof(OPENFILENAME);
       +  ofn.hwndOwner = parent;
       +  ofn.hInstance = hInst;
       +  ofn.lpstrFilter = NULL;
       +  ofn.lpstrCustomFilter = NULL;
       +  ofn.nMaxCustFilter = 0;
       +  ofn.nFilterIndex = 0;
       +  ofn.lpstrFile = lpstrFile;
       +  ofn.nMaxFile = 200;
       +  ofn.lpstrFileTitle = NULL;
       +  ofn.nMaxFileTitle = 0;
       +  ofn.lpstrInitialDir = NULL;
       +  ofn.lpstrTitle = NULL;
       +  ofn.Flags = OFN_HIDEREADONLY;
       +  ofn.lpstrDefExt = NULL;
       +  ofn.lCustData = 0;
       +  ofn.lpfnHook = NULL;
       +  ofn.lpTemplateName = NULL;
       +
       +  GetOpenFileName(&ofn);*/
       +}
       +
       +void ConditionalExit(HWND hWnd) {
       +  if (MessageBox(hWnd,"This will exit without installing any new software on "
       +                 "your machine.\nAre you sure you want to quit?\n\n(You can "
       +                 "continue the installation at a\nlater date simply by "
       +                 "running this program again.)","Exit Install",
       +                 MB_YESNO|MB_ICONQUESTION)==IDYES) {
       +    PostQuitMessage(0);
       +  }
       +}
       +
       +void UpdateStartMenuFolder(void) {
       +  char *buf;
       +  HWND folderlist;
       +  LRESULT lres;
       +  int selind;
       +
       +  folderlist = GetDlgItem(mainDlg[DL_SHORTCUTS],LB_FOLDLIST);
       +  if (!folderlist) return;
       +
       +  lres=SendMessage(folderlist,LB_GETCURSEL,0,0);
       +  if (lres==LB_ERR) return;
       +
       +  selind=(int)lres;
       +  lres=SendMessage(folderlist,LB_GETTEXTLEN,(WPARAM)selind,0);
       +  if (lres==LB_ERR) return;
       +
       +  buf = bmalloc(lres+1);
       +  lres=SendMessage(folderlist,LB_GETTEXT,(WPARAM)selind,(LPARAM)buf);
       +  if (lres!=LB_ERR) {
       +    SendDlgItemMessage(mainDlg[DL_SHORTCUTS],ED_FOLDER,WM_SETTEXT,
       +                       0,(LPARAM)buf);
       +  }
       +  bfree(buf);
       +}
       +
       +BOOL CALLBACK MainDlgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) {
       +  switch(msg) {
       +    case WM_INITDIALOG:
       +      return TRUE;
       +    case WM_COMMAND:
       +      if (HIWORD(wParam)==BN_CLICKED && lParam) {
       +        switch(LOWORD(wParam)) {
       +          case BT_CANCEL: ConditionalExit(hWnd); break;
       +          case BT_NEXT:   ShowNewDialog(CurrentDialog+1); break;
       +          case BT_BACK:   ShowNewDialog(CurrentDialog-1); break;
       +          case BT_FINISH: PostQuitMessage(0); break;
       +          case BT_BROWSE: SelectInstDir(hWnd); break;
       +        }
       +      } else if (HIWORD(wParam)==LBN_SELCHANGE && lParam &&
       +                 LOWORD(wParam)==LB_FOLDLIST) {
       +        UpdateStartMenuFolder();
       +      }
       +      break;
       +    case WM_CLOSE:
       +      ConditionalExit(hWnd);
       +      return TRUE;
       +  }
       +  return FALSE;
       +}
       +
       +LPVOID GetResource(LPCTSTR resname,LPCTSTR restype) {
       +  HRSRC hrsrc;
       +  HGLOBAL hglobal;
       +  LPVOID respt;
       +
       +  hrsrc = FindResource(NULL,resname,restype);
       +  if (!hrsrc) DisplayError("Could not find resource: ",TRUE,TRUE);
       +
       +  hglobal = LoadResource(NULL,hrsrc);
       +  if (!hglobal) DisplayError("Could not load resource: ",TRUE,TRUE);
       +
       +  respt = LockResource(hglobal);
       +  if (!respt) DisplayError("Could not lock resource: ",TRUE,TRUE);
       +
       +  return respt;
       +}
       +
       +InstData *ReadInstData() {
       +  InstFiles *lastinst=NULL,*lastextra=NULL;
       +  InstLink *lastmenu=NULL,*lastdesktop=NULL;
       +  char *instdata,*pt,*filename,*line2,*line3;
       +  DWORD filesize;
       +  InstData *idata;
       +
       +  instdata = GetResource(MAKEINTRESOURCE(0),"INSTLIST");
       +  if (!instdata) return NULL;
       +
       +  pt=instdata;
       +
       +  idata = bmalloc(sizeof(InstData));
       +  idata->totalsize = atol(pt);
       +  pt += strlen(pt)+1;
       +
       +  idata->product = bstrdup(pt);
       +  pt += strlen(pt)+1;
       +
       +  idata->installdir = bstrdup(pt);
       +  pt += strlen(pt)+1;
       +
       +  idata->startmenudir = bstrdup(pt);
       +  pt += strlen(pt)+1;
       +
       +  while (1) {
       +    filename=pt;
       +    pt += strlen(pt)+1;
       +    if (filename[0]) {
       +      filesize=atol(pt);
       +      pt += strlen(pt)+1;
       +      AddInstFiles(filename,filesize,&lastinst,&idata->instfiles);
       +    } else break;
       +  }
       +  while (1) {
       +    filename=pt;
       +    pt += strlen(pt)+1;
       +    if (filename[0]) {
       +      filesize=atol(pt);
       +      pt += strlen(pt)+1;
       +      AddInstFiles(filename,filesize,&lastextra,&idata->extrafiles);
       +    } else break;
       +  }
       +  while (1) {
       +    filename=pt;
       +    pt += strlen(pt)+1;
       +    if (filename[0]) {
       +      line2=pt; pt += strlen(pt)+1;
       +      line3=pt; pt += strlen(pt)+1;
       +      AddInstLink(filename,line2,line3,&lastmenu,&idata->startmenu);
       +    } else break;
       +  }
       +  while (1) {
       +    filename=pt;
       +    pt += strlen(pt)+1;
       +    if (filename[0]) {
       +      line2=pt; pt += strlen(pt)+1;
       +      line3=pt; pt += strlen(pt)+1;
       +      AddInstLink(filename,line2,line3,&lastdesktop,&idata->desktop);
       +    } else break;
       +  }
       +
       +  return idata;
       +}
       +
       +#define BUFFER_SIZE (32*1024)
       +
       +char *GetFirstFile(InstFiles *filelist,DWORD totalsize) {
       +  DWORD bufsiz;
       +  char *inbuf,*outbuf;
       +  int status;
       +  z_stream z;
       +
       +  if (!filelist) return NULL;
       +
       +  inbuf = GetResource(MAKEINTRESOURCE(1),"INSTFILE");
       +  if (!inbuf) return NULL;
       +
       +  bufsiz = filelist->filesize;
       +  outbuf = bmalloc(bufsiz+1);
       +
       +  z.zalloc = Z_NULL;
       +  z.zfree = Z_NULL;
       +  z.opaque = Z_NULL;
       +  z.next_in = inbuf;
       +  z.avail_in = totalsize;
       +
       +  inflateInit(&z);
       +  z.next_out = outbuf;
       +  z.avail_out = bufsiz;
       +
       +  while (1) {
       +    status = inflate(&z,Z_SYNC_FLUSH);
       +    if ((status!=Z_OK && status!=Z_STREAM_END) || z.avail_out==0) break;
       +  }
       +  inflateEnd(&z);
       +
       +  outbuf[bufsiz]='\0';
       +  return outbuf;
       +}
       +
       +BOOL OpenNextOutput(HANDLE *fout,InstFiles *filelist,InstFiles **listpt,
       +                    DWORD *fileleft,HANDLE logf) {
       +  bstr *str;
       +  DWORD bytes_written;
       +
       +  if (*fout) CloseHandle(*fout);
       +  *fout = NULL;
       +
       +  str=bstr_new();
       +
       +  if (*listpt) {
       +    if (!WriteFile(logf,(*listpt)->filename,strlen((*listpt)->filename)+1,
       +                   &bytes_written,NULL)) {
       +      printf("Write error\n");
       +    }
       +
       +    bstr_setlength(str,0);
       +    bstr_append_long(str,(*listpt)->filesize);
       +    if (!WriteFile(logf,str->text,str->length+1,&bytes_written,NULL)) {
       +      printf("Write error\n");
       +    }
       +    *listpt=(*listpt)->next;
       +  } else *listpt = filelist;
       +
       +  if (*listpt) {
       +    *fout = CreateFile((*listpt)->filename,GENERIC_WRITE,0,NULL,
       +                       CREATE_ALWAYS,0,NULL);
       +    *fileleft = (*listpt)->filesize;
       +    bstr_assign(str,"Installing file: ");
       +    bstr_append(str,(*listpt)->filename);
       +    bstr_append(str," (size ");
       +    bstr_append_long(str,(*listpt)->filesize);
       +    bstr_append(str,")");
       +    SendDlgItemMessage(mainDlg[DL_DOINSTALL],ST_FILELIST,
       +                       WM_SETTEXT,0,(LPARAM)str->text);
       +  }
       +
       +  bstr_free(str,TRUE);
       +
       +  return (*fout!=NULL);
       +}
       +
       +HRESULT CreateLink(LPCSTR origPath,LPSTR linkArgs,LPSTR workDir,
       +                   LPSTR linkPath,LPSTR linkDesc) {
       +  HRESULT hres;
       +  IShellLink *psl;
       +  IPersistFile *ppf;
       +  WORD wsz[MAX_PATH];
       +
       +  hres = CoCreateInstance(&CLSID_ShellLink,NULL,CLSCTX_INPROC_SERVER,
       +                          &IID_IShellLink,(void **)&psl);
       +  if (SUCCEEDED(hres)) {
       +    psl->lpVtbl->SetPath(psl,origPath);
       +    if (workDir) psl->lpVtbl->SetWorkingDirectory(psl,workDir);
       +    if (linkDesc) psl->lpVtbl->SetDescription(psl,linkDesc);
       +    if (linkArgs) psl->lpVtbl->SetArguments(psl,linkArgs);
       +    hres = psl->lpVtbl->QueryInterface(psl,&IID_IPersistFile,(void **)&ppf);
       +    if (SUCCEEDED(hres)) {
       +      MultiByteToWideChar(CP_ACP,0,linkPath,-1,wsz,MAX_PATH);
       +      hres = ppf->lpVtbl->Save(ppf,wsz,TRUE);
       +      ppf->lpVtbl->Release(ppf);
       +    } else {
       +      DisplayError("Cannot write shortcut",FALSE,FALSE);
       +    }
       +    psl->lpVtbl->Release(psl);
       +  } else {
       +    DisplayError("Cannot create shortcut",FALSE,FALSE);
       +  }
       +  return hres;
       +}
       +
       +void GetWinText(char **text,HWND hWnd) {
       +  int textlen;
       +  bfree(*text);
       +  *text=NULL;
       +  if (!hWnd) return;
       +
       +  textlen=GetWindowTextLength(hWnd)+1;
       +  if (!textlen) return;
       +
       +  *text=bmalloc(textlen);
       +  if (!GetWindowText(hWnd,*text,textlen)) {
       +    bfree(*text); *text=NULL;
       +  }
       +}
       +
       +void CreateLinks(char *linkdir,InstLink *linkpt) {
       +  bstr *linkpath,*origfile;
       +
       +  linkpath=bstr_new();
       +  origfile=bstr_new();
       +
       +  for (;linkpt;linkpt=linkpt->next) {
       +    bstr_assign(linkpath,linkdir);
       +    bstr_appendpath(linkpath,linkpt->linkfile);
       +
       +    bstr_assign(origfile,idata->installdir);
       +    bstr_appendpath(origfile,linkpt->origfile);
       +
       +    CreateLink(origfile->text,linkpt->args,idata->installdir,
       +               linkpath->text,NULL);
       +
       +/*bstr_append_c(origfile,',');
       +bstr_append(origfile,linkpt->args);
       +bstr_append_c(origfile,',');
       +bstr_append(origfile,idata->installdir);
       +bstr_append_c(origfile,',');
       +bstr_append(origfile,linkpath->text);
       +bstr_append(origfile,", NULL");
       +MessageBox(NULL,origfile->text,NULL,MB_OK);*/
       +  }
       +}
       +
       +void SetupShortcuts(HANDLE fout) {
       +  char *startmenu,*desktop;
       +  BOOL dodesktop;
       +
       +  startmenu = GetStartMenuDir(idata);
       +  desktop = GetDesktopDir();
       +
       +  dodesktop=(IsDlgButtonChecked(mainDlg[DL_SHORTCUTS],CB_DESKTOP)==BST_CHECKED);
       +
       +  if (startmenu) {
       +    CreateDirectory(startmenu,NULL);
       +
       +    CreateLinks(startmenu,idata->startmenu);
       +    WriteLinkList(fout,idata->startmenu);
       +  } else {
       +    WriteLinkList(fout,NULL);
       +  }
       +
       +  if (dodesktop && desktop) {
       +    CreateLinks(desktop,idata->desktop);
       +    WriteLinkList(fout,idata->desktop);
       +  } else {
       +    WriteLinkList(fout,NULL);
       +  }
       +
       +  bfree(startmenu); bfree(desktop);
       +}
       +
       +void SetupUninstall() {
       +  HKEY key;
       +  DWORD disp;
       +  bstr *str,*uninstexe,*link;
       +  BOOL haveuninstall=FALSE;
       +  char *startmenu;
       +  InstFiles *listpt;
       +
       +  for (listpt=idata->instfiles;listpt;listpt=listpt->next) {
       +    if (strcmp(listpt->filename,"uninstall.exe")==0) {
       +      haveuninstall=TRUE;
       +      break;
       +    }
       +  }
       +
       +  if (!haveuninstall) return;
       +
       +  str=bstr_new();
       +  uninstexe=bstr_new();
       +  link=bstr_new();
       +
       +  bstr_assign(str,UninstallKey);
       +  bstr_appendpath(str,idata->product);
       +  
       +  if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,str->text,0,NULL,0,
       +                     KEY_ALL_ACCESS,NULL,&key,&disp)==ERROR_SUCCESS) {
       +    RegSetValueEx(key,"DisplayName",0,REG_SZ,idata->product,
       +                  strlen(idata->product));
       +    bstr_assign_windir(str);
       +    bstr_appendpath(str,UninstallEXE);
       +    bstr_append_c(str,' ');
       +    bstr_append(str,idata->product);
       +    RegSetValueEx(key,"UninstallString",0,REG_SZ,str->text,str->length);
       +    bstr_assign(str,idata->installdir);
       +    RegSetValueEx(key,"InstallDirectory",0,REG_SZ,str->text,str->length);
       +    RegCloseKey(key);
       +  } else {
       +    DisplayError("Cannot create registry key: ",TRUE,FALSE);
       +  }
       +
       +  bstr_assign_windir(str);
       +  bstr_appendpath(str,"bw-uninstall.exe");
       +  DeleteFile(str->text);
       +
       +  bstr_assign(uninstexe,idata->installdir);
       +  bstr_appendpath(uninstexe,"uninstall.exe");
       +
       +  if (!MoveFile(uninstexe->text,str->text)) {
       +    DisplayError("Unable to create uninstall program: ",TRUE,FALSE);
       +  }
       +  DeleteFile(uninstexe->text);
       +
       +  startmenu = GetStartMenuDir(idata);
       +  bstr_assign(link,startmenu);
       +  bstr_appendpath(link,"Uninstall ");
       +  bstr_append(link,idata->product);
       +  bstr_append(link,".LNK");
       +
       +  CreateLink(str->text,idata->product,NULL,link->text,NULL);
       +/*bstr_append_c(str,',');
       +bstr_append(str,idata->product);
       +bstr_append(str,",NULL,");
       +bstr_append(str,link->text);
       +bstr_append(str,",NULL");
       +MessageBox(NULL,str->text,NULL,MB_OK);*/
       +
       +  bstr_free(link,TRUE);
       +  bstr_free(uninstexe,TRUE);
       +  bstr_free(str,TRUE);
       +  bfree(startmenu);
       +}
       +
       +DWORD WINAPI DoInstall(LPVOID lpParam) {
       +  HANDLE fout,logf;
       +  DWORD bytes_written,fileleft;
       +  char *inbuf,*outbuf;
       +  int status,count;
       +  z_stream z;
       +  InstFiles *listpt;
       +
       +  inbuf = GetResource(MAKEINTRESOURCE(1),"INSTFILE");
       +  if (!inbuf) return 0;
       +
       +  GetWinText(&idata->startmenudir,GetDlgItem(mainDlg[DL_SHORTCUTS],ED_FOLDER));
       +
       +  CreateDirectory(idata->installdir,NULL);
       +  SetCurrentDirectory(idata->installdir);
       +  logf = CreateFile("install.log",GENERIC_WRITE,0,NULL,
       +                    CREATE_ALWAYS,0,NULL);
       +
       +  if (!WriteFile(logf,idata->startmenudir,strlen(idata->startmenudir)+1,
       +                 &bytes_written,NULL)) {
       +    printf("Write error\n");
       +  }
       +
       +  fout = NULL;
       +  listpt=NULL;
       +  OpenNextOutput(&fout,idata->instfiles,&listpt,&fileleft,logf);
       +
       +  outbuf = bmalloc(BUFFER_SIZE);
       +
       +  z.zalloc = Z_NULL;
       +  z.zfree = Z_NULL;
       +  z.opaque = Z_NULL;
       +  z.next_in = inbuf;
       +  z.avail_in = idata->totalsize;
       +
       +  inflateInit(&z);
       +  z.next_out = outbuf;
       +  z.avail_out = BUFFER_SIZE;
       +
       +  while (1) {
       +    status = inflate(&z,Z_SYNC_FLUSH);
       +    if (status==Z_OK || status==Z_STREAM_END) {
       +      count = BUFFER_SIZE - z.avail_out;
       +      z.next_out = outbuf;
       +      while (count >= fileleft) {
       +        if (fileleft &&
       +            !WriteFile(fout,z.next_out,fileleft,&bytes_written,NULL)) {
       +          printf("Write error\n");
       +        }
       +        count-=fileleft;
       +        z.next_out+=fileleft;
       +        if (!OpenNextOutput(&fout,idata->instfiles,&listpt,
       +                            &fileleft,logf)) break;
       +      }
       +      if (!fout) break;
       +      if (count && !WriteFile(fout,z.next_out,count,&bytes_written,NULL)) {
       +        printf("Write error\n");
       +      }
       +      fileleft-=count;
       +      z.next_out = outbuf;
       +      z.avail_out = BUFFER_SIZE;
       +    }
       +    if (status!=Z_OK) break;
       +  }
       +
       +  inflateEnd(&z);
       +  CloseHandle(fout);
       +
       +  outbuf[0]='\0';
       +  if (!WriteFile(logf,outbuf,1,&bytes_written,NULL)) {
       +    printf("Write error\n");
       +  }
       +  bfree(outbuf);
       +
       +  WriteFileList(logf,idata->extrafiles);
       +
       +  CoInitialize(NULL);
       +  SetupShortcuts(logf);
       +  SetupUninstall();
       +  CoUninitialize();
       +
       +  CloseHandle(logf);
       +
       +  SetFileAttributes("install.log",FILE_ATTRIBUTE_HIDDEN);
       +
       +  ShowWindow(GetDlgItem(mainDlg[DL_DOINSTALL],ST_COMPLETE),SW_SHOW);
       +  ShowWindow(GetDlgItem(mainDlg[DL_DOINSTALL],ST_EXIT),SW_SHOW);
       +  return 0;
       +}
       +
       +void FillFolderList(void) {
       +  HANDLE findfile;
       +  WIN32_FIND_DATA finddata;
       +  bstr *str;
       +  HWND folderlist;
       +
       +  folderlist = GetDlgItem(mainDlg[DL_SHORTCUTS],LB_FOLDLIST);
       +  if (!folderlist) return;
       +
       +  str=bstr_new();
       +
       +  bstr_assign_windir(str);
       +  bstr_appendpath(str,"Start Menu\\Programs\\*");
       +
       +  findfile = FindFirstFile(str->text,&finddata);
       +  if (findfile) {
       +    while(1) {
       +      if (finddata.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY &&
       +          strcmp(finddata.cFileName,".")!=0 &&
       +          strcmp(finddata.cFileName,"..")!=0) {
       +        SendMessage(folderlist,LB_ADDSTRING,0,(LPARAM)finddata.cFileName);
       +      }
       +      if (!FindNextFile(findfile,&finddata)) break;
       +    }
       +    FindClose(findfile);
       +  }
       +  bstr_free(str,TRUE);
       +}
       +
       +int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
       +                     LPSTR lpszCmdParam,int nCmdShow) {
       +  MSG msg;
       +  int i;
       +  BOOL Handled;
       +  char *licence;
       +
       +  InitCommonControls();
       +
       +  hInst = hInstance;
       +
       +  if (!hPrevInstance) RegisterSepClass(hInstance);
       +
       +  for (i=0;i<DL_NUM;i++) {
       +    mainDlg[i] = CreateDialog(hInst,MAKEINTRESOURCE(i+1),NULL,MainDlgProc);
       +  }
       +
       +  CheckDlgButton(mainDlg[DL_SHORTCUTS],CB_DESKTOP,BST_CHECKED);
       +
       +  FillFolderList();
       +
       +  ShowWindow(GetDlgItem(mainDlg[DL_DOINSTALL],ST_COMPLETE),SW_HIDE);
       +  ShowWindow(GetDlgItem(mainDlg[DL_DOINSTALL],ST_EXIT),SW_HIDE);
       +
       +  idata=ReadInstData();
       +
       +  SendDlgItemMessage(mainDlg[DL_SHORTCUTS],ED_FOLDER,WM_SETTEXT,
       +                     0,(LPARAM)idata->startmenudir);
       +  SendDlgItemMessage(mainDlg[DL_INSTALLDIR],ST_INSTDIR,WM_SETTEXT,
       +                     0,(LPARAM)idata->installdir);
       +  
       +  licence=GetFirstFile(idata->instfiles,idata->totalsize);
       +
       +  if (licence) {
       +    SendDlgItemMessage(mainDlg[DL_LICENCE],ED_LICENCE,WM_SETTEXT,
       +                       0,(LPARAM)licence);
       +    bfree(licence);
       +  }
       +
       +  for (i=0;i<DL_NUM;i++) SetGuiFont(mainDlg[i]);
       +
       +  CurrentDialog=DL_NUM;
       +  ShowNewDialog(DL_INTRO);
       +
       +  while (GetMessage(&msg,NULL,0,0)) {
       +    Handled=FALSE;
       +    for (i=0;i<DL_NUM && !Handled;i++) {
       +      Handled=IsDialogMessage(mainDlg[i],&msg);
       +    }
       +    if (!Handled) {
       +      TranslateMessage(&msg);
       +      DispatchMessage(&msg);
       +    }
       +  }
       +  FreeInstData(idata,FALSE);
       +
       +  return 0;
       +}
 (DIR) diff --git a/win32/uninstall.c b/win32/uninstall.c
       t@@ -0,0 +1,290 @@
       +#include <windows.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include "contid.h"
       +#include "guifunc.h"
       +#include "util.h"
       +
       +HINSTANCE hInst;
       +HWND mainDlg;
       +char *product;
       +
       +char *read_line0(HANDLE hin) {
       +  char *buf;
       +  int bufsize=32,strind=0;
       +  DWORD bytes_read;
       +  buf = bmalloc(bufsize);
       +
       +  while (1) {
       +    if (!ReadFile(hin,&buf[strind],1,&bytes_read,NULL)) {
       +      printf("Read error\n"); break;
       +    }
       +    if (bytes_read==0) { buf[strind]='\0'; break; } 
       +    else if (buf[strind]=='\0') break;
       +    else {
       +      strind++;
       +      if (strind>=bufsize) {
       +        bufsize*=2;
       +        buf = brealloc(buf,bufsize);
       +      }
       +    }
       +  }
       +  if (strind==0) { bfree(buf); return NULL; }
       +  else return buf;
       +}
       +
       +InstLink *ReadLinkList(HANDLE fin) {
       +  InstLink *first=NULL,*listpt=NULL,*newpt;
       +  char *linkfile,*origfile,*args;
       +
       +  while (1) {
       +    linkfile=read_line0(fin);
       +    if (!linkfile) break;
       +    origfile=read_line0(fin);
       +    args=read_line0(fin);
       +    if (!origfile) DisplayError("Corrupt install.log",FALSE,TRUE);
       +    newpt = bmalloc(sizeof(InstLink));
       +    if (listpt) listpt->next = newpt;
       +    else first = newpt;
       +    listpt = newpt;
       +    newpt->next=NULL;
       +    newpt->linkfile=linkfile;
       +    newpt->origfile=origfile;
       +    newpt->args=args;
       +  }
       +  return first;
       +}
       +
       +InstFiles *ReadFileList(HANDLE fin) {
       +  InstFiles *first=NULL,*listpt=NULL,*newpt;
       +  char *filename,*filesize;
       +
       +  while (1) {
       +    filename=read_line0(fin);
       +    if (!filename) break;
       +    filesize=read_line0(fin);
       +    if (!filesize) DisplayError("Corrupt install.log",FALSE,TRUE);
       +    newpt = bmalloc(sizeof(InstFiles));
       +    if (listpt) listpt->next = newpt;
       +    else first = newpt;
       +    listpt = newpt;
       +
       +    newpt->next=NULL;
       +    newpt->filename=filename;
       +    newpt->filesize=atol(filesize);
       +    bfree(filesize);
       +  }
       +  return first;
       +}
       +
       +char *GetProduct(void) {
       +  char *product;
       +  product = strrchr(GetCommandLine(),' ');
       +  if (product) return bstrdup(++product);
       +  else {
       +    DisplayError("This program should be called with a product ID",FALSE,TRUE);
       +    ExitProcess(1);
       +  }
       +}
       +
       +char *GetInstallDir(char *product) {
       +  HKEY key;
       +  bstr *str;
       +  DWORD keytype,keylen;
       +  char *installdir;
       +
       +  str=bstr_new();
       +  bstr_assign(str,UninstallKey);
       +  bstr_appendpath(str,product);
       +  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,str->text,0,
       +                   KEY_ALL_ACCESS,&key)!=ERROR_SUCCESS) {
       +    DisplayError("Could not open registry",FALSE,TRUE);
       +  }
       +
       +  if (RegQueryValueEx(key,"InstallDirectory",NULL,
       +                      &keytype,NULL,&keylen)!=ERROR_SUCCESS ||
       +      keytype!=REG_SZ) {
       +    DisplayError("Could not query registry key",FALSE,TRUE);
       +  }
       +
       +  installdir = bmalloc(keylen);
       +  if (RegQueryValueEx(key,"InstallDirectory",NULL,
       +                      &keytype,installdir,&keylen)!=ERROR_SUCCESS) {
       +    DisplayError("Could not get registry key value",FALSE,TRUE);
       +  }
       +
       +  bstr_free(str,TRUE);
       +  return installdir;
       +}
       +
       +InstData *ReadInstData(HANDLE fin,char *product,char *installdir) {
       +  InstData *idata;
       +
       +  idata=bmalloc(sizeof(InstData));
       +
       +  idata->product=product;
       +  idata->installdir=installdir;
       +  idata->startmenudir=read_line0(fin);
       +
       +  idata->instfiles = ReadFileList(fin);
       +  idata->extrafiles = ReadFileList(fin);
       +
       +  idata->startmenu = ReadLinkList(fin);
       +  idata->desktop = ReadLinkList(fin);
       +  return idata;
       +}
       +
       +void DeleteFileList(InstFiles *listpt) {
       +  bstr *str;
       +  str=bstr_new();
       +  for (;listpt;listpt=listpt->next) {
       +    bstr_assign(str,"Deleting file: ");
       +    bstr_append(str,listpt->filename);
       +    SendDlgItemMessage(mainDlg,ST_DELSTAT,WM_SETTEXT,0,(LPARAM)str->text);
       +    DeleteFile(listpt->filename);
       +  }
       +  bstr_free(str,TRUE);
       +}
       +
       +void DeleteLinkList(char *dir,InstLink *listpt) {
       +  bstr *str;
       +  str=bstr_new();
       +  SetCurrentDirectory(dir);
       +  for (;listpt;listpt=listpt->next) {
       +    bstr_assign(str,"Deleting link: ");
       +    bstr_append(str,listpt->linkfile);
       +    SendDlgItemMessage(mainDlg,ST_DELSTAT,WM_SETTEXT,0,(LPARAM)str->text);
       +    DeleteFile(listpt->linkfile);
       +  }
       +  bstr_free(str,TRUE);
       +}
       +
       +void RemoveUninstall(char *startmenu,char *product) {
       +  bstr *inipath,*uninstpath,*uninstlink;
       +
       +  inipath=bstr_new();
       +  uninstpath=bstr_new();
       +  uninstlink=bstr_new();
       +
       +  bstr_assign(uninstlink,startmenu);
       +  bstr_appendpath(uninstlink,"Uninstall ");
       +  bstr_append(uninstlink,product);
       +  bstr_append(uninstlink,".LNK");
       +  DeleteFile(uninstlink->text);
       +
       +  bstr_assign_windir(inipath);
       +  bstr_assign(uninstpath,inipath->text);
       +
       +  bstr_appendpath(inipath,"wininit.ini");
       +  bstr_appendpath(uninstpath,UninstallEXE);
       +
       +  if (!WritePrivateProfileString("Renane","NUL",uninstpath->text,
       +                                 inipath->text)) {
       +    DisplayError("Cannot write to wininit.ini: ",TRUE,FALSE);
       +  }
       +
       +  bstr_free(uninstlink,TRUE);
       +  bstr_free(uninstpath,TRUE);
       +  bstr_free(inipath,TRUE);
       +}
       +
       +DWORD WINAPI DoUninstall(LPVOID lpParam) {
       +  InstData *idata;
       +  HANDLE fin;
       +  bstr *str;
       +  char *startmenu,*desktop,*installdir;
       +
       +  installdir=GetInstallDir(product);
       +
       +  SetCurrentDirectory(installdir);
       +  fin = CreateFile("install.log",GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
       +
       +  if (fin) {
       +    idata = ReadInstData(fin,product,installdir);
       +    CloseHandle(fin);
       +    DeleteFile("install.log");
       +    DeleteFileList(idata->instfiles);
       +    DeleteFileList(idata->extrafiles);
       +
       +    startmenu = GetStartMenuDir(idata);
       +    desktop = GetDesktopDir();
       +    DeleteLinkList(startmenu,idata->startmenu);
       +    DeleteLinkList(desktop,idata->desktop);
       +
       +    RemoveUninstall(startmenu,product);
       +
       +    SetCurrentDirectory(desktop); /* Just make sure we're not in the install
       +                                     directory any more */
       +    RemoveDirectory(installdir);
       +    RemoveDirectory(startmenu);
       +
       +    str=bstr_new();
       +    bstr_assign(str,UninstallKey);
       +    bstr_appendpath(str,product);
       +    RegDeleteKey(HKEY_LOCAL_MACHINE,str->text);
       +    bstr_free(str,TRUE);
       +
       +    bfree(startmenu); bfree(desktop);
       +    FreeInstData(idata,TRUE);
       +  } else {
       +    bfree(product); bfree(installdir); /* Normally FreeInstData frees these */
       +  }
       +  ShowWindow(GetDlgItem(mainDlg,ST_DELDONE),SW_SHOW);
       +  EnableWindow(GetDlgItem(mainDlg,BT_DELOK),TRUE);
       +  return 0;
       +}
       +
       +BOOL CALLBACK MainDlgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) {
       +  HANDLE hThread;
       +  DWORD threadID;
       +  switch(msg) {
       +    case WM_INITDIALOG:
       +      hThread = CreateThread(NULL,0,DoUninstall,NULL,0,&threadID);
       +      return TRUE;
       +    case WM_COMMAND:
       +      if (HIWORD(wParam)==BN_CLICKED && LOWORD(wParam)==BT_DELOK && lParam) {
       +        PostQuitMessage(0);
       +        return TRUE;
       +      }
       +      break;
       +    case WM_CLOSE:
       +      PostQuitMessage(0);
       +      return TRUE;
       +  }
       +  return FALSE;
       +}
       +
       +int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
       +                     LPSTR lpszCmdParam,int nCmdShow) {
       +  MSG msg;
       +  bstr *str;
       +
       +  product=GetProduct();
       +
       +  str=bstr_new();
       +  bstr_assign(str,"Are you sure you want to uninstall ");
       +  bstr_append(str,product);
       +  bstr_append(str," ?");
       +  if (MessageBox(NULL,str->text,"Uninstall",MB_YESNO)==IDNO) return(1);
       +  bstr_free(str,TRUE);
       +
       +  hInst = hInstance;
       +  if (!hPrevInstance) RegisterSepClass(hInstance);
       +
       +  mainDlg = CreateDialog(hInstance,MAKEINTRESOURCE(1),NULL,MainDlgProc);
       +  SetGuiFont(mainDlg);
       +
       +  EnableWindow(GetDlgItem(mainDlg,BT_DELOK),FALSE);
       +  ShowWindow(mainDlg,SW_SHOW);
       +  ShowWindow(GetDlgItem(mainDlg,ST_DELDONE),SW_HIDE);
       +
       +  while (GetMessage(&msg,NULL,0,0)) {
       +    if (!IsDialogMessage(mainDlg,&msg)) {
       +      TranslateMessage(&msg);
       +      DispatchMessage(&msg);
       +    }
       +  }
       +
       +  return 0;
       +}
 (DIR) diff --git a/win32/uninstall.rc b/win32/uninstall.rc
       t@@ -1 +1,14 @@
       +#include <windows.h>
       +#include "contid.h"
       +
        mainicon ICON "setup.ico"
       +
       +1 DIALOG 17, 40, 190, 115
       +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
       +CAPTION "Uninstall"
       +BEGIN
       +        CONTROL "", 101, "WC_GTKSEP", 2 | WS_CHILD | WS_VISIBLE, 8, 88, 174, 2
       +        DEFPUSHBUTTON "OK", BT_DELOK, 75, 96, 39, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP
       +        LTEXT "Deleting file: dopewars.exe", ST_DELSTAT, 15, 14, 159, 17, WS_CHILD | WS_VISIBLE | WS_GROUP
       +        LTEXT "All files successfully uninstalled.", ST_DELDONE, 15, 51, 159, 25, WS_CHILD | WS_VISIBLE | WS_GROUP
       +END
 (DIR) diff --git a/win32/util.c b/win32/util.c
       t@@ -0,0 +1,358 @@
       +#include <windows.h>
       +#include <stdio.h>
       +#include <string.h>
       +#include "util.h"
       +
       +const char *UninstallKey=
       +  "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
       +const char *UninstallEXE="bw-uninstall.exe";
       +
       +static void bstr_append_dir(bstr *str,BOOL windir);
       +
       +void *bmalloc(UINT numbytes) {
       +  HLOCAL localpt;
       +
       +  if (numbytes<=0) numbytes=1;
       +
       +  localpt = LocalAlloc(LMEM_FIXED,numbytes);
       +  if (localpt) return (void *)localpt;
       +  else {
       +    DisplayError("Could not allocate memory: ",TRUE,TRUE);
       +    ExitProcess(1);
       +  }
       +}
       +
       +void bfree(void *pt) {
       +  if (!pt) return;
       +  if (LocalFree((HLOCAL)pt)) {
       +    DisplayError("Could not free memory: ",TRUE,TRUE);
       +  }
       +}
       +
       +void *brealloc(void *pt,UINT numbytes) {
       +  HLOCAL localpt;
       +  UINT numcp;
       +  void *newpt;
       +  if (!pt && numbytes) return bmalloc(numbytes);
       +  else if (pt && !numbytes) bfree(pt);
       +  else if (pt && numbytes) {
       +    localpt = LocalReAlloc((HLOCAL)pt,numbytes,0);
       +    if (localpt) return (void *)localpt;
       +    else if (GetLastError()==ERROR_LOCKED) { /* OK, we'll do it the hard way */
       +      newpt=bmalloc(numbytes);
       +      memset(newpt,0,numbytes);
       +      numcp = LocalSize((HLOCAL)pt);
       +      if (numbytes < numcp) numcp = numbytes;
       +      memcpy((char *)newpt,(char *)pt,numcp);
       +      bfree(pt);
       +      return newpt;
       +    } else {
       +      DisplayError("Could not reallocate memory: ",TRUE,TRUE);
       +      ExitProcess(1);
       +    }
       +  }
       +  return NULL;
       +}
       +
       +char *bstrdup(char *str) {
       +  char *newstr;
       +  if (str) {
       +    newstr = bmalloc(strlen(str)+1);
       +    strcpy(newstr,str);
       +  } else {
       +    newstr = bmalloc(1);
       +    newstr[0]='\0';
       +  }
       +  return newstr;
       +}
       +
       +bstr *bstr_new(void) {
       +  bstr *str;
       +  str = bmalloc(sizeof(bstr));
       +  str->bufsiz=64;
       +  str->length=0;
       +  str->text= bmalloc(str->bufsiz);
       +  str->text[0]='\0';
       +  return str;
       +}
       +
       +void bstr_free(bstr *str,BOOL free_text) {
       +  if (free_text) bfree(str->text);
       +  bfree(str);
       +}
       +
       +void bstr_expandby(bstr *str,unsigned extralength) {
       +  if (str->bufsiz >= str->length+1+extralength) return;
       +
       +  while (str->bufsiz < str->length+1+extralength) str->bufsiz*=2;
       +  str->text = brealloc(str->text,str->bufsiz);
       +}
       +
       +void bstr_setlength(bstr *str,unsigned length) {
       +  if (length+1 <= str->bufsiz) {
       +    if (length < str->length) {
       +      str->length = length;
       +      str->text[length]='\0';
       +    }
       +  } else {
       +    bstr_expandby(str,length+1-str->bufsiz);
       +  }
       +}
       +
       +void bstr_assign(bstr *str,const char *text) {
       +  if (text) {
       +    bstr_setlength(str,strlen(text));
       +    strcpy(str->text,text);
       +    str->length=strlen(text);
       +  } else {
       +    bstr_setlength(str,0);
       +  }
       +}
       +
       +void bstr_append(bstr *str,const char *text) {
       +  if (!text) return;
       +  bstr_expandby(str,strlen(text));
       +  strcat(str->text,text);
       +  str->length+=strlen(text);
       +}
       +
       +void bstr_appendpath(bstr *str,const char *text) {
       +  if (str->length && str->text[str->length-1]!='\\') {
       +    bstr_append_c(str,'\\');
       +  }
       +  bstr_append(str,text);
       +}
       +
       +void bstr_append_c(bstr *str,char ch) {
       +  bstr_expandby(str,1);
       +  str->text[str->length]=ch;
       +  str->length++;
       +  str->text[str->length]='\0';
       +}
       +
       +/* We can be pretty confident that this is enough space for an integer */
       +#define MAX_LENGTH_INT 80
       +
       +void bstr_append_long(bstr *str,long val) {
       +  char tmpbuf[MAX_LENGTH_INT];
       +  sprintf(tmpbuf,"%ld",val);
       +  bstr_append(str,tmpbuf);
       +}
       +
       +void bstr_append_windir(bstr *str) {
       +  bstr_append_dir(str,TRUE);
       +}
       +
       +void bstr_append_curdir(bstr *str) {
       +  bstr_append_dir(str,FALSE);
       +}
       +
       +void bstr_assign_windir(bstr *str) {
       +  bstr_setlength(str,0);
       +  bstr_append_windir(str);
       +}
       +
       +void bstr_assign_curdir(bstr *str) {
       +  bstr_setlength(str,0);
       +  bstr_append_curdir(str);
       +}
       +
       +void bstr_append_dir(bstr *str,BOOL windir) {
       +  unsigned spaceleft;
       +  UINT retval;
       +  int tries;
       +
       +  spaceleft = str->bufsiz-str->length; /* spaceleft includes the null */
       +
       +  for (tries=0;tries<5;tries++) {
       +    if (windir) {
       +      retval = GetWindowsDirectory(str->text+str->length,spaceleft);
       +    } else {
       +      retval = GetCurrentDirectory(spaceleft,str->text+str->length);
       +    }
       +    if (retval==0) DisplayError("Cannot get directory: ",TRUE,TRUE);
       +    if (retval <= spaceleft-1) {
       +      str->length += retval;
       +      break;
       +    }
       +    bstr_expandby(str,retval); /* Let's err on the side of caution */
       +    spaceleft = str->bufsiz-str->length;
       +  }
       +  if (tries>=5) DisplayError("Cannot get directory: ",TRUE,TRUE);
       +}
       +
       +void DisplayError(const char *errtext,BOOL addsyserr,BOOL fatal) {
       +  DWORD syserr;
       +  bstr *str;
       +  LPTSTR lpMsgBuf;
       +
       +  syserr=GetLastError();
       +
       +  str=bstr_new();
       +  bstr_assign(str,errtext);
       +
       +  if (addsyserr) {
       +    bstr_append_long(str,syserr);
       +    bstr_append_c(str,' ');
       +    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
       +                  NULL,syserr,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
       +                  (LPTSTR)&lpMsgBuf,0,NULL);
       +    bstr_append(str,lpMsgBuf);
       +    LocalFree(lpMsgBuf);
       +  }
       +
       +  MessageBox(NULL,str->text,fatal ? "Fatal Error" : "Error",
       +             MB_OK | MB_ICONSTOP);
       +  if (fatal) ExitProcess(1);
       +}
       +
       +void AddInstFiles(char *filename,DWORD filesize,InstFiles **lastpt,
       +                  InstFiles **firstpt) {
       +  InstFiles *newpt;
       +
       +  newpt = bmalloc(sizeof(InstFiles));
       +  if (*lastpt) {
       +    (*lastpt)->next = newpt;
       +  } else {
       +    *firstpt = newpt;
       +  }
       +  *lastpt = newpt;
       +  newpt->next=NULL;
       +  newpt->filename=filename;
       +  newpt->filesize=filesize;
       +}
       +
       +void AddInstLink(char *linkfile,char *origfile,char *args,InstLink **lastpt,
       +                 InstLink **firstpt) {
       +  InstLink *newpt;
       +
       +  newpt = bmalloc(sizeof(InstLink));
       +  if (*lastpt) {
       +    (*lastpt)->next = newpt;
       +  } else {
       +    *firstpt = newpt;
       +  }
       +  *lastpt = newpt;
       +  newpt->next=NULL;
       +  newpt->linkfile=linkfile;
       +  newpt->origfile=origfile;
       +  newpt->args=args;
       +}
       +
       +void FreeLinkList(InstLink *linklist,BOOL freepts) {
       +  InstLink *thispt,*nextpt;
       +
       +  thispt=linklist;
       +  while (thispt) {
       +    nextpt=thispt->next;
       +
       +    if (freepts) {
       +      bfree(thispt->linkfile);
       +      bfree(thispt->origfile);
       +      bfree(thispt->args);
       +    }
       +    bfree(thispt);
       +
       +    thispt=nextpt;
       +  }
       +}
       +
       +void FreeFileList(InstFiles *filelist,BOOL freepts) {
       +  InstFiles *thispt,*nextpt;
       +
       +  thispt=filelist;
       +  while (thispt) {
       +    nextpt=thispt->next;
       +
       +    if (freepts) bfree(thispt->filename);
       +    bfree(thispt);
       +
       +    thispt=nextpt;
       +  }
       +}
       +
       +void FreeInstData(InstData *idata,BOOL freepts) {
       +  FreeFileList(idata->instfiles,freepts);
       +  FreeFileList(idata->extrafiles,freepts);
       +
       +  FreeLinkList(idata->startmenu,freepts);
       +  FreeLinkList(idata->desktop,freepts);
       +
       +  bfree(idata->product);
       +  bfree(idata->installdir);
       +  bfree(idata->startmenudir);
       +
       +  bfree(idata);
       +}
       +
       +void WriteLinkList(HANDLE fout,InstLink *listpt) {
       +  char str[]="";
       +  DWORD bytes_written;
       +  for (;listpt;listpt=listpt->next) {
       +    if (!WriteFile(fout,listpt->linkfile,strlen(listpt->linkfile)+1,
       +                   &bytes_written,NULL)) {
       +      printf("Write error\n");
       +    }
       +    if (!WriteFile(fout,listpt->origfile,strlen(listpt->origfile)+1,
       +                   &bytes_written,NULL)) {
       +      printf("Write error\n");
       +    }
       +    if (!WriteFile(fout,listpt->args,strlen(listpt->args)+1,
       +                   &bytes_written,NULL)) {
       +      printf("Write error\n");
       +    }
       +  }
       +  if (!WriteFile(fout,str,strlen(str)+1,&bytes_written,NULL)) {
       +    printf("Write error\n");
       +  }
       +}
       +
       +void WriteFileList(HANDLE fout,InstFiles *listpt) {
       +  bstr *str;
       +  DWORD bytes_written;
       +  str=bstr_new();
       +
       +  for (;listpt;listpt=listpt->next) {
       +    if (!WriteFile(fout,listpt->filename,strlen(listpt->filename)+1,
       +                   &bytes_written,NULL)) {
       +      printf("Write error\n");
       +    }
       +    bstr_setlength(str,0);
       +    bstr_append_long(str,listpt->filesize);
       +    if (!WriteFile(fout,str->text,str->length+1,&bytes_written,NULL)) {
       +      printf("Write error\n");
       +    }
       +  }
       +  bstr_assign(str,"");
       +  if (!WriteFile(fout,str->text,str->length+1,&bytes_written,NULL)) {
       +    printf("Write error\n");
       +  }
       +  bstr_free(str,TRUE);
       +}
       +
       +char *GetStartMenuDir(InstData *idata) {
       +  bstr *str;
       +  char *retval;
       +
       +  str = bstr_new();
       +  bstr_assign_windir(str);
       +  bstr_appendpath(str,"Start Menu\\Programs");
       +  bstr_appendpath(str,idata->startmenudir);
       +
       +  retval = str->text;
       +  bstr_free(str,FALSE);
       +  return retval;
       +}
       +
       +char *GetDesktopDir(void) {
       +  bstr *str;
       +  char *retval;
       +
       +  str = bstr_new();
       +  bstr_assign_windir(str);
       +  bstr_appendpath(str,"Desktop");
       +
       +  retval = str->text;
       +  bstr_free(str,FALSE);
       +  return retval;
       +}
 (DIR) diff --git a/win32/util.h b/win32/util.h
       t@@ -0,0 +1,65 @@
       +#include <windows.h>
       +
       +typedef struct _bstr {
       +  char *text;
       +  unsigned length; /* Length of current text, NOT including terminating null */
       +  unsigned bufsiz; /* Size of the allocated memory buffer */
       +} bstr;
       +
       +typedef struct _InstFiles {
       +  char *filename;
       +  DWORD filesize;
       +  struct _InstFiles *next;
       +} InstFiles;
       +
       +typedef struct _InstLink {
       +  char *linkfile;
       +  char *origfile;
       +  char *args;
       +  struct _InstLink *next;
       +} InstLink;
       +
       +typedef struct _InstData {
       +  char *product;
       +  char *installdir,*startmenudir;
       +  DWORD totalsize;
       +  InstFiles *instfiles;
       +  InstFiles *extrafiles;
       +  InstLink *startmenu;
       +  InstLink *desktop;
       +} InstData;
       +
       +extern const char *UninstallKey;
       +extern const char *UninstallEXE;
       +
       +void *bmalloc(UINT numbytes);
       +void bfree(void *pt);
       +void *brealloc(void *pt,UINT numbytes);
       +char *bstrdup(char *str);
       +
       +bstr *bstr_new(void);
       +void bstr_free(bstr *str,BOOL free_text);
       +void bstr_expandby(bstr *str,unsigned extralength);
       +void bstr_setlength(bstr *str,unsigned length);
       +void bstr_assign(bstr *str,const char *text);
       +void bstr_append(bstr *str,const char *text);
       +void bstr_appendpath(bstr *str,const char *text);
       +void bstr_append_c(bstr *str,const char ch);
       +void bstr_append_long(bstr *str,const long val);
       +void bstr_append_windir(bstr *str);
       +void bstr_append_curdir(bstr *str);
       +void bstr_assign_windir(bstr *str);
       +void bstr_assign_curdir(bstr *str);
       +
       +void DisplayError(const char *errtext,BOOL addsyserr,BOOL fatal);
       +void AddInstFiles(char *filename,DWORD filesize,InstFiles **lastpt,
       +                  InstFiles **firstpt);
       +void AddInstLink(char *linkfile,char *origfile,char *args,InstLink **lastpt,
       +                 InstLink **firstpt);
       +void FreeLinkList(InstLink *linklist,BOOL freepts);
       +void FreeFileList(InstFiles *filelist,BOOL freepts);
       +void FreeInstData(InstData *idata,BOOL freepts);
       +void WriteLinkList(HANDLE fout,InstLink *listpt);
       +void WriteFileList(HANDLE fout,InstFiles *listpt);
       +char *GetStartMenuDir(InstData *idata);
       +char *GetDesktopDir(void);