1 /* Copyright (C) 1999, 2000, Ghostgum Software Pty Ltd.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 // $Id: dwsetup.cpp,v 1.5.6.2.2.1 2003/01/17 00:49:00 giles Exp $
20 //
21 //
22 // This is the setup program for Win32 GNU Ghostscript
23 //
24 // The starting point is a self extracting zip archive
25 // with the following contents:
26 //   setupgs.exe
27 //   uninstgs.exe
28 //   filelist.txt      (contains list of program files)
29 //   fontlist.txt      (contains list of font files)
30 //   gs#.##\*          (files listed in filelist.txt)
31 //   fonts\*           (fonts listed in fontlist.txt)
32 // This is the same as the zip file created by Aladdin Enterprises,
33 // with the addition of setupgs.exe, uninstgs.exe, filelist.txt and
34 // fontlist.txt.
35 //
36 // The first line of the files filelist.txt and fontlist.txt
37 // contains the uninstall name to be used.
38 // The second line contains name of the main directory where
39 // uninstall log files are to be placed.
40 // Subsequent lines contain files to be copied (but not directories).
41 // For example, filelist.txt might contain:
42 //   GNU Ghostscript 6.50
43 //   gs6.50
44 //   gs6.50\bin\gsdll32.dll
45 //   gs6.50\lib\gs_init.ps
46 // The file fontlist.txt might contain:
47 //   GNU Ghostscript Fonts
48 //   fonts
49 //   fonts\n019003l.pfb
50 //   fonts\n019023l.pfb
51 //
52 // The default install directory is c:\gs.
53 // The default Start Menu Folder is Ghostscript.
54 // These are set in the resources.
55 // The setup program will create the following uninstall log files
56 //   c:\gs\gs#.##\uninstal.txt
57 //   c:\gs\fonts\uninstal.txt
58 // The uninstall program (accessed through control panel) will not
59 // remove directories nor will it remove itself.
60 //
61 // If the install directory is the same as the current file
62 // location, no files will be copied, but the existence of each file
63 // will be checked.  This allows the archive to be unzipped, then
64 // configured in its current location.  Running the uninstall will not
65 // remove uninstgs.exe, setupgs.exe, filelist.txt or fontlist.txt.
66 
67 
68 #define STRICT
69 #include <windows.h>
70 #include <shellapi.h>
71 #include <objbase.h>
72 #include <shlobj.h>
73 #include <stdio.h>
74 #include <direct.h>
75 
76 #ifdef MAX_PATH
77 #define MAXSTR MAX_PATH
78 #else
79 #define MAXSTR 256
80 #endif
81 
82 #include "dwsetup.h"
83 #include "dwinst.h"
84 
85 //#define DEBUG
86 
87 #define UNINSTALLPROG "uninstgs.exe"
88 
89 
90 /////////////////////////////////
91 // Globals
92 
93 CInstall cinst;
94 
95 // TRUE = Place Start Menu items in All Users.
96 // FALSE = Current User
97 BOOL g_bUseCommon;
98 
99 // TRUE = Destination is the same as Source, so don't copy files.
100 BOOL g_bNoCopy;
101 
102 // Source directory, usually a temporary directory created by
103 // unzip self extractor.
104 CHAR g_szSourceDir[MAXSTR];
105 
106 // Target directory for program and fonts.
107 // Default loaded from resources
108 CHAR g_szTargetDir[MAXSTR];
109 
110 // Target Group for shortcut.
111 // Default loaded from resources
112 CHAR g_szTargetGroup[MAXSTR];
113 
114 // Setup application name, loaded from resources
115 CHAR g_szAppName[MAXSTR];
116 
117 BOOL g_bInstallFonts = TRUE;
118 BOOL g_bAllUsers = FALSE;
119 
120 
121 HWND g_hMain;		// Main install dialog
122 HWND g_hWndText;	// Install log dialog
123 HINSTANCE g_hInstance;
124 
125 // If a directory is listed on the command line, g_bBatch will
126 // be TRUE and a silent install will occur.
127 BOOL g_bBatch = FALSE;
128 
129 BOOL g_bQuit = FALSE;	// TRUE = Get out of message loop.
130 BOOL g_bError = FALSE;	// TRUE = Install was not successful
131 BOOL is_winnt = FALSE;	// Disable "All Users" if not NT.
132 
133 
134 // Prototypes
135 BOOL CALLBACK MainDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
136 void gs_addmess_count(const char *str, int count);
137 void gs_addmess(const char *str);
138 void gs_addmess_update(void);
139 BOOL init();
140 BOOL install_all();
141 BOOL install_prog();
142 BOOL install_fonts();
143 BOOL make_filelist(int argc, char *argv[]);
144 
145 
146 //////////////////////////////////////////////////////////////////////
147 // Entry point
148 //////////////////////////////////////////////////////////////////////
149 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)150 int APIENTRY WinMain(HINSTANCE hInstance,
151                      HINSTANCE hPrevInstance,
152                      LPSTR     lpCmdLine,
153                      int       nCmdShow)
154 {
155 	MSG msg;
156 	g_hInstance = hInstance;
157 
158 	if (!init()) {
159 		MessageBox(HWND_DESKTOP, "Initialisation failed",
160 			g_szAppName, MB_OK);
161 		return 1;
162 	}
163 
164 	if (!g_bBatch) {
165 		while (GetMessage(&msg, (HWND)NULL, 0, 0)) {
166 			if (!IsDialogMessage(g_hWndText, &msg) &&
167 				!IsDialogMessage(g_hMain, &msg)) {
168 				TranslateMessage(&msg);
169 				DispatchMessage(&msg);
170 			}
171 		}
172 		DestroyWindow(g_hMain);
173 	}
174 
175 	return (g_bError ? 1 : 0);
176 }
177 
178 
179 
180 
181 //////////////////////////////////////////////////////////////////////
182 // Text log window
183 //////////////////////////////////////////////////////////////////////
184 
185 
186 #define TWLENGTH 32768
187 #define TWSCROLL 1024
188 char twbuf[TWLENGTH];
189 int twend;
190 
191 // Modeless Dialog Box
192 BOOL CALLBACK
TextWinDlgProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)193 TextWinDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
194 {
195     switch(message) {
196 	case WM_INITDIALOG:
197 		EnableWindow(g_hMain, FALSE);
198 		return TRUE;
199 	case WM_COMMAND:
200 		switch(LOWORD(wParam)) {
201 		case IDC_TEXTWIN_COPY:
202 			{HGLOBAL hglobal;
203 			LPSTR p;
204 			DWORD result;
205 			int start, end;
206 			result = SendDlgItemMessage(hwnd, IDC_TEXTWIN_MLE, EM_GETSEL, (WPARAM)0, (LPARAM)0);
207 			start = LOWORD(result);
208 			end   = HIWORD(result);
209 			if (start == end) {
210 				start = 0;
211 				end = twend;
212 			}
213 			hglobal = GlobalAlloc(GHND | GMEM_SHARE, end-start+1);
214 			if (hglobal == (HGLOBAL)NULL) {
215 				MessageBeep(-1);
216 				return(FALSE);
217 			}
218 			p = (char *)GlobalLock(hglobal);
219 			if (p == (LPSTR)NULL) {
220 				MessageBeep(-1);
221 				return(FALSE);
222 			}
223 			lstrcpyn(p, twbuf+start, end-start);
224 			GlobalUnlock(hglobal);
225 			OpenClipboard(hwnd);
226 			EmptyClipboard();
227 			SetClipboardData(CF_TEXT, hglobal);
228 			CloseClipboard();
229 			}
230 			break;
231 		case IDCANCEL:
232 			g_bQuit = TRUE;
233 			DestroyWindow(hwnd);
234 			return TRUE;
235 		}
236 		break;
237 	case WM_CLOSE:
238 			DestroyWindow(hwnd);
239 			return TRUE;
240 	case WM_DESTROY:
241 			g_bQuit = TRUE;
242 			g_hWndText = (HWND)NULL;
243 			EnableWindow(g_hMain, TRUE);
244 			PostQuitMessage(0);
245 			break;
246 	}
247 	return FALSE;
248 }
249 
250 
251 
252 // Add string to log window
253 void
gs_addmess_count(const char * str,int count)254 gs_addmess_count(const char *str, int count)
255 {
256 	const char *s;
257 	char *p;
258 	int i, lfcount;
259 	MSG msg;
260 
261 	// we need to add \r after each \n, so count the \n's
262 	lfcount = 0;
263 	s = str;
264 	for (i=0; i<count; i++) {
265 		if (*s == '\n')
266 			lfcount++;
267 		s++;
268 	}
269 
270 	if (count + lfcount >= TWSCROLL)
271 		return;		// too large
272 	if (count + lfcount + twend >= TWLENGTH-1) {
273 		// scroll buffer
274 		twend -= TWSCROLL;
275 		memmove(twbuf, twbuf+TWSCROLL, twend);
276 	}
277 	p = twbuf+twend;
278 	for (i=0; i<count; i++) {
279 		if (*str == '\n') {
280 			*p++ = '\r';
281 		}
282 		*p++ = *str++;
283 	}
284 	twend += (count + lfcount);
285 	*(twbuf+twend) = '\0';
286 
287 
288 	// Update the dialog box
289 	if (g_bBatch)
290 		return;
291 
292 	gs_addmess_update();
293 	while (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE)) {
294 		if (!IsDialogMessage(g_hWndText, &msg) &&
295 			!IsDialogMessage(g_hMain, &msg)) {
296 			TranslateMessage(&msg);
297 			DispatchMessage(&msg);
298 		}
299 	}
300 }
301 
302 void
gs_addmess(const char * str)303 gs_addmess(const char *str)
304 {
305 	gs_addmess_count(str, lstrlen(str));
306 
307 }
308 
309 
310 void
gs_addmess_update(void)311 gs_addmess_update(void)
312 {
313 	HWND hwndmess = g_hWndText;
314 
315 	if (g_bBatch)
316 		return;
317 
318 	if (IsWindow(hwndmess)) {
319 		HWND hwndtext = GetDlgItem(hwndmess, IDC_TEXTWIN_MLE);
320 		DWORD linecount;
321 		SendMessage(hwndtext, WM_SETREDRAW, FALSE, 0);
322 		SetDlgItemText(hwndmess, IDC_TEXTWIN_MLE, twbuf);
323 		linecount = SendDlgItemMessage(hwndmess, IDC_TEXTWIN_MLE, EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0);
324 		SendDlgItemMessage(hwndmess, IDC_TEXTWIN_MLE, EM_LINESCROLL, (WPARAM)0, (LPARAM)linecount-14);
325 		SendMessage(hwndtext, WM_SETREDRAW, TRUE, 0);
326 		InvalidateRect(hwndtext, (LPRECT)NULL, TRUE);
327 		UpdateWindow(hwndtext);
328 	}
329 }
330 
331 
332 //////////////////////////////////////////////////////////////////////
333 // Browse dialog box
334 //////////////////////////////////////////////////////////////////////
335 
336 // nasty GLOBALS
337 char szFolderName[MAXSTR];
338 char szDirName[MAXSTR];
339 
340 BOOL CALLBACK
DirDlgProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)341 DirDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
342 {
343 	WORD notify_message;
344 
345 	switch(message) {
346 	case WM_INITDIALOG:
347 		DlgDirList(hwnd, szDirName, IDC_FILES, IDC_FOLDER,
348 			DDL_DRIVES | DDL_DIRECTORY);
349 		SetDlgItemText(hwnd, IDC_TARGET, szFolderName);
350 		return FALSE;
351     case WM_COMMAND:
352 		notify_message = HIWORD(wParam);
353 		switch (LOWORD(wParam)) {
354 		case IDC_FILES:
355 			if (notify_message == LBN_DBLCLK) {
356 				CHAR szPath[MAXSTR];
357 				DlgDirSelectEx(hwnd, szPath, sizeof(szPath), IDC_FILES);
358 				DlgDirList(hwnd, szPath, IDC_FILES, IDC_FOLDER,
359 					DDL_DRIVES | DDL_DIRECTORY);
360 			}
361 			return FALSE;
362 		case IDOK:
363 			GetDlgItemText(hwnd, IDC_FOLDER, szDirName, sizeof(szDirName));
364 			GetDlgItemText(hwnd, IDC_TARGET, szFolderName, sizeof(szFolderName));
365 			EndDialog(hwnd, TRUE);
366 			return TRUE;
367 		case IDCANCEL:
368 			EndDialog(hwnd, FALSE);
369 			return TRUE;
370 		}
371 		return FALSE;
372 	}
373 	return FALSE;
374 }
375 
376 
377 //////////////////////////////////////////////////////////////////////
378 // Initialisation and Main dialog box
379 //////////////////////////////////////////////////////////////////////
380 
381 void
message_box(const char * str)382 message_box(const char *str)
383 {
384 	MessageBox(HWND_DESKTOP, str, g_szAppName, MB_OK);
385 }
386 
387 
388 BOOL
init()389 init()
390 {
391 	DWORD dwVersion = GetVersion();
392 	// get source directory
393 	GetCurrentDirectory(sizeof(g_szSourceDir), g_szSourceDir);
394 
395 	// load strings
396 	LoadString(g_hInstance, IDS_APPNAME, g_szAppName, sizeof(g_szAppName));
397 	LoadString(g_hInstance, IDS_TARGET_GROUP,
398 		g_szTargetGroup, sizeof(g_szTargetGroup));
399 
400 	if (LOBYTE(LOWORD(dwVersion)) < 4) {
401         MessageBox(HWND_DESKTOP,
402 			"This install program needs Windows 4.0 or later",
403 			g_szAppName, MB_OK);
404 		return FALSE;
405 	}
406 	if ( (HIWORD(dwVersion) & 0x8000) == 0)
407 		is_winnt = TRUE;
408 
409 
410 	cinst.SetMessageFunction(message_box);
411 
412 #define MAXCMDTOKENS 128
413 
414 	int argc;
415 	LPSTR argv[MAXCMDTOKENS];
416 	LPSTR p;
417 	char command[256];
418 	char *args;
419 	char *d, *e;
420 
421 	p = GetCommandLine();
422 
423 	argc = 0;
424 	args = (char *)malloc(lstrlen(p)+1);
425 	if (args == (char *)NULL)
426 		return 1;
427 
428 	// Parse command line handling quotes.
429 	d = args;
430 	while (*p) {
431 		// for each argument
432 
433 		if (argc >= MAXCMDTOKENS - 1)
434 			break;
435 
436 		e = d;
437 		while ((*p) && (*p != ' ')) {
438 			if (*p == '\042') {
439 				// Remove quotes, skipping over embedded spaces.
440 				// Doesn't handle embedded quotes.
441 				p++;
442 				while ((*p) && (*p != '\042'))
443 					*d++ =*p++;
444 			}
445 			else
446 				*d++ = *p;
447 			if (*p)
448 				p++;
449 		}
450 		*d++ = '\0';
451 		argv[argc++] = e;
452 
453 		while ((*p) && (*p == ' '))
454 			p++;	// Skip over trailing spaces
455 	}
456 	argv[argc] = NULL;
457 
458 	if (strlen(argv[0]) == 0) {
459 		GetModuleFileName(g_hInstance, command, sizeof(command)-1);
460 		argv[0] = command;
461 	}
462 
463 	if (argc > 2) {
464 		// Probably creating filelist.txt
465 		return make_filelist(argc, argv);
466 	}
467 
468 
469 	// check if batch mode requested
470 	// get location of target directory from command line as argv[1]
471 	if (argc == 2) {
472 		strncpy(g_szTargetDir, argv[1], sizeof(g_szTargetDir));
473 		g_bBatch = TRUE;
474 		if (is_winnt)
475 			g_bAllUsers = TRUE;
476 	}
477 	if (g_bBatch) {
478 		if (!install_all()) {
479 			// display log showing error
480 			g_bBatch = FALSE;
481 			g_hWndText = CreateDialogParam(g_hInstance,
482 				MAKEINTRESOURCE(IDD_TEXTWIN),
483 				(HWND)HWND_DESKTOP, TextWinDlgProc,
484 				(LPARAM)NULL);
485 			gs_addmess_update();
486 		}
487 		return TRUE;
488 	}
489 
490 	// Interactive setup
491 	LoadString(g_hInstance, IDS_TARGET_DIR,
492 		g_szTargetDir, sizeof(g_szTargetDir));
493 
494 	// main dialog box
495 	g_hMain = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_MAIN), (HWND)NULL, MainDlgProc, (LPARAM)NULL);
496 	// centre dialog on screen
497 	int width = GetSystemMetrics(SM_CXFULLSCREEN);
498 	int height = GetSystemMetrics(SM_CYFULLSCREEN);
499 	RECT rect;
500 	GetWindowRect(g_hMain, &rect);
501 	MoveWindow(g_hMain, (width - (rect.right - rect.left))/2,
502 		(height - (rect.bottom - rect.top))/2,
503 		(rect.right - rect.left),
504 		(rect.bottom - rect.top), FALSE);
505 
506 	// initialize targets
507 	cinst.SetMessageFunction(message_box);
508 	if (!cinst.Init(g_szSourceDir, "filelist.txt"))
509 		return FALSE;
510 
511 	SetDlgItemText(g_hMain, IDC_TARGET_DIR, g_szTargetDir);
512 	SetDlgItemText(g_hMain, IDC_TARGET_GROUP, g_szTargetGroup);
513 	SetDlgItemText(g_hMain, IDC_PRODUCT_NAME, cinst.GetUninstallName());
514 	SendDlgItemMessage(g_hMain, IDC_INSTALL_FONTS, BM_SETCHECK, BST_CHECKED, 0);
515 	ShowWindow(g_hMain, SW_SHOWNORMAL);
516 
517 	return (g_hMain != (HWND)NULL); /* success */
518 }
519 
520 
521 // Main Modeless Dialog Box
522 BOOL CALLBACK
MainDlgProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)523 MainDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
524 {
525 	switch(message) {
526 	case WM_INITDIALOG:
527 		EnableWindow(GetDlgItem(hwnd, IDC_ALLUSERS), is_winnt);
528 		return TRUE;
529 	case WM_COMMAND:
530 		switch(LOWORD(wParam)) {
531 		case IDC_README:
532 			{
533 			char buf[MAXSTR];
534 			sprintf(buf, "%s\\%s\\doc\\Readme.htm", g_szSourceDir,
535 				cinst.GetMainDir());
536 			ShellExecute(hwnd, NULL, buf, NULL, g_szSourceDir,
537 				SW_SHOWNORMAL);
538 			}
539 			return TRUE;
540 		case IDC_BROWSE_DIR:
541 			{ char dir[MAXSTR];
542 			char *p;
543 			GetDlgItemText(hwnd, IDC_TARGET_DIR, dir, sizeof(dir));
544 			strcpy(szDirName, dir);
545 			if ( (p = strrchr(szDirName, '\\')) != (char *)NULL ) {
546 				strcpy(szFolderName, p+1);
547 				if (p == szDirName+2)
548 					p++;	// step over c:\   //
549 				*p = '\0';
550 			}
551 			else {
552 				strcpy(szDirName, "c:\\");
553 				strcpy(szFolderName, dir);
554 			}
555 			if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DIRDLG),
556 				hwnd, DirDlgProc)) {
557 				strcpy(dir, szDirName);
558 				if (strlen(dir) && (dir[strlen(dir)-1] != '\\'))
559 					strcat(dir, "\\");
560 				strcat(dir, szFolderName);
561 				SetDlgItemText(hwnd, IDC_TARGET_DIR, dir);
562 			}
563 			}
564 			return TRUE;
565 		case IDC_BROWSE_GROUP:
566 			{ char dir[MAXSTR];
567 			char programs[MAXSTR];
568 			char *p;
569 			GetDlgItemText(hwnd, IDC_TARGET_GROUP, dir, sizeof(dir));
570 			cinst.GetPrograms(
571 				SendDlgItemMessage(hwnd, IDC_ALLUSERS,
572 				BM_GETCHECK, 0, 0) == BST_CHECKED,
573 				programs, sizeof(programs));
574 			strcpy(szDirName, programs);
575 			strcpy(szFolderName, dir);
576 			if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DIRDLG),
577 				hwnd, DirDlgProc)) {
578 				strcpy(dir, szFolderName);
579 				p = szDirName;
580 				if (strnicmp(szDirName, programs,
581 					strlen(programs)) == 0) {
582 					p += strlen(programs);
583 					if (*p == '\\')
584 						p++;
585 					strcpy(dir, p);
586 					if (strlen(dir) &&
587 						(dir[strlen(dir)-1] != '\\'))
588 						strcat(dir, "\\");
589 					strcat(dir, szFolderName);
590 				}
591 				SetDlgItemText(hwnd, IDC_TARGET_GROUP, dir);
592 			}
593 			}
594 			return TRUE;
595 		case IDCANCEL:
596 			PostQuitMessage(0);
597 			return TRUE;
598 		case IDC_INSTALL:
599 			GetDlgItemText(hwnd, IDC_TARGET_DIR,
600 				g_szTargetDir, sizeof(g_szTargetDir));
601 			GetDlgItemText(hwnd, IDC_TARGET_GROUP,
602 				g_szTargetGroup, sizeof(g_szTargetGroup));
603 			g_bInstallFonts = (SendDlgItemMessage(g_hMain,
604 				IDC_INSTALL_FONTS, BM_GETCHECK, 0, 0)
605 				== BST_CHECKED);
606 			g_bAllUsers = (SendDlgItemMessage(hwnd,
607 				IDC_ALLUSERS, BM_GETCHECK, 0, 0
608 				) == BST_CHECKED);
609 
610 			// install log dialog box
611 			g_hWndText = CreateDialogParam(g_hInstance,
612 				MAKEINTRESOURCE(IDD_TEXTWIN),
613 				(HWND)hwnd, TextWinDlgProc, (LPARAM)NULL);
614 			EnableWindow(GetDlgItem(hwnd, IDC_INSTALL), FALSE);
615 			if (install_all())
616 				PostQuitMessage(0);
617 			return TRUE;
618 		default:
619 			return(FALSE);
620 		}
621 		case WM_CLOSE:
622 			PostQuitMessage(0);
623 			return TRUE;
624     }
625     return FALSE;
626 }
627 
628 // install program and files
629 BOOL
install_all()630 install_all()
631 {
632 	gs_addmess("Source Directory=");
633 	gs_addmess(g_szSourceDir);
634 	gs_addmess("\n");
635 	gs_addmess("Target Directory=");
636 	gs_addmess(g_szTargetDir);
637 	gs_addmess("\n");
638 	gs_addmess("Target Shell Folder=");
639 	gs_addmess(g_szTargetGroup);
640 	gs_addmess("\n");
641 	gs_addmess(g_bAllUsers ? "  All users\n" : "  Current user\n");
642 
643 	if (stricmp(g_szSourceDir, g_szTargetDir) == 0) {
644 		// Don't copy files
645 		if (!g_bBatch)
646 			if (::MessageBox(g_hWndText, "Install location is the same as the current file location.  No files will be copied.", g_szAppName, MB_OKCANCEL)
647 				!= IDOK) {
648 				return FALSE;
649 			}
650 		g_bNoCopy = TRUE;
651 	}
652 
653 
654 	if (g_bQuit)
655 		return FALSE;
656 
657 	if (!install_prog()) {
658 		cinst.CleanUp();
659 		g_bError = TRUE;
660 		return FALSE;
661 	}
662 	if (g_bInstallFonts && !install_fonts()) {
663 		cinst.CleanUp();
664 		g_bError = TRUE;
665 		return FALSE;
666 	}
667 
668 	gs_addmess("Install successful\n");
669 
670 	// show start menu folder
671 	if (!g_bBatch) {
672 		char szFolder[MAXSTR];
673 		szFolder[0] = '\0';
674 		cinst.GetPrograms(g_bAllUsers, szFolder, sizeof(szFolder));
675 		strcat(szFolder, "\\");
676 		strcat(szFolder, g_szTargetGroup);
677 		ShellExecute(HWND_DESKTOP, "open", szFolder,
678 			NULL, NULL, SW_SHOWNORMAL);
679 	}
680 
681 #ifdef DEBUG
682 	return FALSE;
683 #endif
684 
685 	return TRUE;
686 }
687 
688 BOOL
install_prog()689 install_prog()
690 {
691 	char *regkey1 = "GNU Ghostscript";
692 	char regkey2[16];
693 	char szDLL[MAXSTR];
694 	char szLIB[MAXSTR];
695 	char szProgram[MAXSTR];
696 	char szArguments[MAXSTR];
697 	char szDescription[MAXSTR];
698 	char szDotVersion[MAXSTR];
699 
700 	if (g_bQuit)
701 		return FALSE;
702 
703 	cinst.SetMessageFunction(gs_addmess);
704 	cinst.SetTargetDir(g_szTargetDir);
705 	cinst.SetTargetGroup(g_szTargetGroup);
706 	cinst.SetAllUsers(g_bAllUsers);
707 	if (!cinst.Init(g_szSourceDir, "filelist.txt"))
708 		return FALSE;
709 
710 	// Get GS version number
711 	gs_addmess("Installing Program...\n");
712 	int nGSversion = 0;
713 	const char *p = cinst.GetMainDir();
714 	while (*p && !isdigit(*p))	// skip over "gs" prefix
715 		p++;
716 	if (strlen(p) == 4)
717 		nGSversion = (p[0]-'0')*100 + (p[2]-'0')*10 + (p[3]-'0');
718 	else if (strlen(p) == 3)
719 		nGSversion = (p[0]-'0')*100 + (p[2]-'0')*10;
720         strncpy(szDotVersion, p, sizeof(szDotVersion));
721 	strncpy(regkey2, szDotVersion, sizeof(regkey2));
722 
723 	// copy files
724 	if (!cinst.InstallFiles(g_bNoCopy, &g_bQuit)) {
725 		gs_addmess("Program install failed\n");
726 		return FALSE;
727 	}
728 
729 	if (g_bQuit)
730 		return FALSE;
731 
732 	// write registry entries
733 	gs_addmess("Updating Registry\n");
734 	if (!cinst.UpdateRegistryBegin()) {
735 		gs_addmess("Failed to begin registry update\n");
736 		return FALSE;
737 	}
738 	if (!cinst.UpdateRegistryKey(regkey1, regkey2)) {
739 		gs_addmess("Failed to open/create registry application key\n");
740 		return FALSE;
741 	}
742 	strcpy(szDLL, g_szTargetDir);
743 	strcat(szDLL, "\\");
744 	strcat(szDLL, cinst.GetMainDir());
745 	strcat(szDLL, "\\bin\\gsdll32.dll");
746 	if (!cinst.UpdateRegistryValue(regkey1, regkey2, "GS_DLL", szDLL)) {
747 		gs_addmess("Failed to add registry value\n");
748 		return FALSE;
749 	}
750 	strcpy(szLIB, g_szTargetDir);
751 	strcat(szLIB, "\\");
752 	strcat(szLIB, cinst.GetMainDir());
753 	strcat(szLIB, "\\lib;");
754 	strcat(szLIB, g_szTargetDir);
755 	strcat(szLIB, "\\fonts");
756 	if (!cinst.UpdateRegistryValue(regkey1, regkey2, "GS_LIB", szLIB)) {
757 		gs_addmess("Failed to add registry value\n");
758 		return FALSE;
759 	}
760 	if (!cinst.UpdateRegistryEnd()) {
761 		gs_addmess("Failed to end registry update\n");
762 		return FALSE;
763 	}
764 	if (g_bQuit)
765 		return FALSE;
766 
767 	// Add Start Menu items
768 	gs_addmess("Adding Start Menu items\n");
769 	if (!cinst.StartMenuBegin()) {
770 		gs_addmess("Failed to begin Start Menu update\n");
771 		return FALSE;
772 	}
773 	strcpy(szProgram, g_szTargetDir);
774 	strcat(szProgram, "\\");
775 	strcat(szProgram, cinst.GetMainDir());
776 	strcat(szProgram, "\\bin\\gswin32.exe");
777 	strcpy(szArguments, "\042-I");
778 	strcat(szArguments, szLIB);
779 	strcat(szArguments, "\042");
780 	sprintf(szDescription, "Ghostscript %s", szDotVersion);
781 	if (!cinst.StartMenuAdd(szDescription, szProgram, szArguments)) {
782 		gs_addmess("Failed to add Start Menu item\n");
783 		return FALSE;
784 	}
785 	strcpy(szProgram, g_szTargetDir);
786 	strcat(szProgram, "\\");
787 	strcat(szProgram, cinst.GetMainDir());
788 	strcat(szProgram, "\\doc\\Readme.htm");
789 	sprintf(szDescription, "Ghostscript Readme %s", szDotVersion);
790 	if (!cinst.StartMenuAdd(szDescription, szProgram, NULL)) {
791 		gs_addmess("Failed to add Start Menu item\n");
792 		return FALSE;
793 	}
794 	if (!cinst.StartMenuEnd()) {
795 		gs_addmess("Failed to end Start Menu update\n");
796 		return FALSE;
797 	}
798 
799 	// consolidate logs into one uninstall file
800 	if (cinst.MakeLog()) {
801 		// add uninstall entry for "Add/Remove Programs"
802 		gs_addmess("Adding uninstall program\n");
803 		if (!cinst.WriteUninstall(UNINSTALLPROG, g_bNoCopy)) {
804 			gs_addmess("Failed to write uninstall entry\n");
805 			return FALSE;
806 		}
807 	}
808 	else {
809 		gs_addmess("Failed to write uninstall log\n");
810 		// If batch install, files might be on a server
811 		// in a write protected directory.
812 		// Don't return an error for batch install.
813 		if (g_bBatch)
814 			return TRUE;
815 		return FALSE;
816 	}
817 
818 	gs_addmess("Program install successful\n");
819 	return TRUE;
820 }
821 
822 
823 BOOL
install_fonts()824 install_fonts()
825 {
826 	cinst.SetMessageFunction(gs_addmess);
827 	cinst.SetTargetDir(g_szTargetDir);
828 	cinst.SetTargetGroup(g_szTargetGroup);
829 	cinst.SetAllUsers(g_bAllUsers);
830 	if (!cinst.Init(g_szSourceDir, "fontlist.txt"))
831 		return FALSE;
832 
833 	// copy files
834 	if (!cinst.InstallFiles(g_bNoCopy, &g_bQuit)) {
835 		gs_addmess("Font install failed\n");
836 		return FALSE;
837 	}
838 
839 	if (g_bQuit)
840 		return FALSE;
841 
842 	if (g_bNoCopy) {
843 		// Don't write uninstall log or entry
844 		// since we didn't copy any files.
845 		cinst.CleanUp();
846 	}
847 	else {
848 		// consolidate logs into one uninstall file
849 		if (cinst.MakeLog()) {
850 			// add uninstall entry for "Add/Remove Programs"
851 			gs_addmess("Adding uninstall program\n");
852 			if (!cinst.WriteUninstall(UNINSTALLPROG, g_bNoCopy)) {
853 				gs_addmess("Failed to write uninstall entry\n");
854 				return FALSE;
855 			}
856 		}
857 		else {
858 			gs_addmess("Failed to write uninstall log\n");
859 			// If batch install, files might be on a server
860 			// in a write protected directory.
861 			// Don't return an error for batch install.
862 			if (g_bBatch)
863 				return TRUE;
864 			return FALSE;
865 		}
866 	}
867 
868 	gs_addmess("Font install successful\n");
869 	return TRUE;
870 }
871 
872 
873 
874 //////////////////////////////////////////////////////////////////////
875 // Create file list
876 //////////////////////////////////////////////////////////////////////
877 
878 FILE *fList;
879 
880 typedef int (*PFN_dodir)(const char *name);
881 
882 /* Called once for each directory */
883 int
dodir(const char * filename)884 dodir(const char *filename)
885 {
886     return 0;
887 }
888 
889 /* Called once for each file */
890 int
dofile(const char * filename)891 dofile(const char *filename)
892 {
893     if (fList != (FILE *)NULL) {
894 		fputs(filename, fList);
895 		fputs("\n", fList);
896     }
897 
898     return 0;
899 }
900 
901 
902 /* Walk through directory 'path', calling dodir() for given directory
903  * and dofile() for each file.
904  * If recurse=1, recurse into subdirectories, calling dodir() for
905  * each directory.
906  */
907 int
dirwalk(char * path,int recurse,PFN_dodir dodir,PFN_dodir dofile)908 dirwalk(char *path, int recurse, PFN_dodir dodir, PFN_dodir dofile)
909 {
910 	WIN32_FIND_DATA find_data;
911 	HANDLE find_handle;
912 	char pattern[MAXSTR];	/* orig pattern + modified pattern */
913 	char base[MAXSTR];
914 	char name[MAXSTR];
915 	BOOL bMore = TRUE;
916 	char *p;
917 
918 
919 	if (path) {
920 		strcpy(pattern, path);
921 		if (strlen(pattern) != 0)  {
922 			p = pattern + strlen(pattern) -1;
923 			if (*p == '\\')
924 				*p = '\0';		// truncate trailing backslash
925 		}
926 
927 		strcpy(base, pattern);
928 		if (strchr(base, '*') != NULL) {
929 			// wildcard already included
930 			// truncate it from the base path
931 			if ( (p = strrchr(base, '\\')) != NULL )
932 				*(++p) = '\0';
933 		}
934 		else if (isalpha(pattern[0]) &&
935 			pattern[1]==':' && pattern[2]=='\0')  {
936 			strcat(pattern, "\\*");		// search entire disk
937 			strcat(base, "\\");
938 		}
939 		else {
940 			// wildcard NOT included
941 			// check to see if path is a directory
942 			find_handle = FindFirstFile(pattern, &find_data);
943 			if (find_handle != INVALID_HANDLE_VALUE) {
944 				FindClose(find_handle);
945 				if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
946 					strcat(pattern, "\\*");		// yes, search files
947 					strcat(base, "\\");
948 				}
949 				else {
950 					dofile(path);				// no, return just this file
951 					return 0;
952 				}
953 			}
954 			else
955 				return 1;	// path invalid
956 		}
957 	}
958 	else {
959 		base[0] = '\0';
960 		strcpy(pattern, "*");
961 	}
962 
963 	find_handle = FindFirstFile(pattern,  &find_data);
964 	if (find_handle == INVALID_HANDLE_VALUE)
965 		return 1;
966 
967 	while (bMore) {
968 		strcpy(name, base);
969 		strcat(name, find_data.cFileName);
970 		if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
971 			if ( strcmp(find_data.cFileName, ".") &&
972 				strcmp(find_data.cFileName, "..") ) {
973 				dodir(name);
974 				if (recurse)
975 					dirwalk(name, recurse, dodir, dofile);
976 			}
977 		}
978 		else {
979 			dofile(name);
980 		}
981 		bMore = FindNextFile(find_handle, &find_data);
982 	}
983 	FindClose(find_handle);
984 
985 	return 0;
986 }
987 
988 
989 
990 // This is used when creating a file list.
991 
make_filelist(int argc,char * argv[])992 BOOL make_filelist(int argc, char *argv[])
993 {
994     char *title = NULL;
995     char *dir = NULL;
996     char *list = NULL;
997     int i;
998     g_bBatch = TRUE;	// Don't run message loop
999 
1000     for (i=1; i<argc; i++) {
1001 		if (strcmp(argv[i], "-title") == 0) {
1002 			i++;
1003 			title = argv[i];
1004 		}
1005 		else if (strcmp(argv[i], "-dir") == 0) {
1006 			i++;
1007 			dir = argv[i];
1008 		}
1009 		else if (strcmp(argv[i], "-list") == 0) {
1010 			i++;
1011 			list = argv[i];
1012 		}
1013 		else {
1014 		    if ((title == NULL) || (strlen(title) == 0) ||
1015 			(dir == NULL) || (strlen(dir) == 0) ||
1016 			(list == NULL) || (strlen(list) == 0)) {
1017 			message_box("Usage: setupgs -title \042GNU Ghostscript #.##\042 -dir \042gs#.##\042 -list \042filelist.txt\042 spec1 spec2 specn\n");
1018 			return FALSE;
1019 		    }
1020 		    if (fList == (FILE *)NULL) {
1021 			    if ( (fList = fopen(list, "w")) == (FILE *)NULL ) {
1022 					message_box("Can't write list file\n");
1023 					return FALSE;
1024 			    }
1025 			    fputs(title, fList);
1026 			    fputs("\n", fList);
1027 			    fputs(dir, fList);
1028 			    fputs("\n", fList);
1029 		    }
1030 		    if (argv[i][0] == '@') {
1031 			// Use @filename with list of files/directories
1032 			// to avoid DOS command line limit
1033 			FILE *f;
1034 			char buf[MAXSTR];
1035 			int j;
1036 			if ( (f = fopen(&(argv[i][1]), "r")) != (FILE *)NULL) {
1037 			    while (fgets(buf, sizeof(buf), f)) {
1038 				// remove trailing newline and spaces
1039 				while ( ((j = strlen(buf)-1) >= 0) &&
1040 				    ((buf[j] == '\n') || (buf[j] == ' ')) )
1041 				    buf[j] = '\0';
1042 			        dirwalk(buf, TRUE, &dodir, &dofile);
1043 			    }
1044 			    fclose(f);
1045 			}
1046 			else {
1047 				wsprintf(buf, "Can't open @ file \042%s\042",
1048 				    &argv[i][1]);
1049 				message_box(buf);
1050 			}
1051 		    }
1052 		    else
1053 		        dirwalk(argv[i], TRUE, &dodir, &dofile);
1054 		}
1055     }
1056 
1057     if (fList != (FILE *)NULL) {
1058         fclose(fList);
1059 	fList = NULL;
1060     }
1061     return TRUE;
1062 }
1063 
1064 //////////////////////////////////////////////////////////////////////
1065