1 /* Copyright (C) 2001-2008 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 // $Id: dwsetup.cpp 9404 2009-01-27 00:18:29Z giles $
15 //
16 //
17 // This is the setup program for Win32 GPL Ghostscript
18 //
19 // The starting point is a self extracting zip archive
20 // with the following contents:
21 //   setupgs.exe
22 //   uninstgs.exe
23 //   filelist.txt      (contains list of program files)
24 //   gs#.##\*          (files listed in filelist.txt)
25 // This is the same as the zip file created by Aladdin Enterprises,
26 // with the addition of setupgs.exe, uninstgs.exe, and filelist.txt
27 //
28 // The first line of the file filelist.txt
29 // contains the uninstall name to be used.
30 // The second line contains name of the main directory where
31 // uninstall log files are to be placed.
32 // Subsequent lines contain files to be copied (but not directories).
33 // For example, filelist.txt might contain:
34 //   GPL Ghostscript 8.55
35 //   gs8.55
36 //   gs8.55\bin\gsdll32.dll
37 //   gs8.55\lib\gs_init.ps
38 //
39 // The default install directory is c:\gs.
40 // The default Start Menu Folder is Ghostscript.
41 // These are set in the resources.
42 // The setup program will create the following uninstall log files
43 //   c:\gs\gs#.##\uninstal.txt
44 // The uninstall program (accessed through control panel) will not
45 // remove directories nor will it remove itself.
46 //
47 // If the install directory is the same as the current file
48 // location, no files will be copied, but the existence of each file
49 // will be checked.  This allows the archive to be unzipped, then
50 // configured in its current location.  Running the uninstall will not
51 // remove uninstgs.exe, setupgs.exe, or filelist.txt.
52 
53 
54 #define STRICT
55 #include <windows.h>
56 #include <shellapi.h>
57 #include <objbase.h>
58 #include <shlobj.h>
59 #include <stdio.h>
60 #include <direct.h>
61 
62 #ifdef MAX_PATH
63 #define MAXSTR MAX_PATH
64 #else
65 #define MAXSTR 256
66 #endif
67 
68 #include "dwsetup.h"
69 #include "dwinst.h"
70 
71 extern "C" {
72 typedef HRESULT (WINAPI *PFN_SHGetFolderPath)(
73   HWND hwndOwner,
74   int nFolder,
75   HANDLE hToken,
76   DWORD dwFlags,
77   LPSTR pszPath);
78 
79 typedef BOOL (WINAPI *PFN_SHGetSpecialFolderPath)(
80   HWND hwndOwner,
81   LPTSTR lpszPath,
82   int nFolder,
83   BOOL fCreate);
84 }
85 
86 //#define DEBUG
87 
88 #define UNINSTALLPROG "uninstgs.exe"
89 
90 
91 /////////////////////////////////
92 // Globals
93 
94 CInstall cinst;
95 
96 // TRUE = Place Start Menu items in All Users.
97 // FALSE = Current User
98 BOOL g_bUseCommon;
99 
100 // TRUE = Destination is the same as Source, so don't copy files.
101 BOOL g_bNoCopy;
102 
103 // Source directory, usually a temporary directory created by
104 // unzip self extractor.
105 CHAR g_szSourceDir[MAXSTR];
106 
107 // Target directory for program.
108 // Default loaded from resources
109 CHAR g_szTargetDir[MAXSTR];
110 
111 // Target Group for shortcut.
112 // Default loaded from resources
113 CHAR g_szTargetGroup[MAXSTR];
114 
115 // Setup application name, loaded from resources
116 CHAR g_szAppName[MAXSTR];
117 
118 BOOL g_bCJKFonts = FALSE;
119 BOOL g_bAllUsers = FALSE;
120 
121 
122 HWND g_hMain;		// Main install dialog
123 HWND g_hWndText;	// Install log dialog
124 HINSTANCE g_hInstance;
125 
126 // If a directory is listed on the command line, g_bBatch will
127 // be TRUE and a silent install will occur.
128 BOOL g_bBatch = FALSE;
129 
130 BOOL g_bQuit = FALSE;	// TRUE = Get out of message loop.
131 BOOL g_bError = FALSE;	// TRUE = Install was not successful
132 BOOL is_winnt = FALSE;	// Disable "All Users" if not NT.
133 
134 #ifdef _WIN64
135 #define DLGRETURN INT_PTR
136 #else
137 #define DLGRETURN BOOL
138 #endif
139 
140 // Prototypes
141 DLGRETURN CALLBACK MainDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
142 void gs_addmess_count(const char *str, int count);
143 void gs_addmess(const char *str);
144 void gs_addmess_update(void);
145 BOOL init();
146 BOOL install_all();
147 BOOL install_prog();
148 BOOL make_filelist(int argc, char *argv[]);
149 int get_font_path(char *path, unsigned int pathlen);
150 BOOL write_cidfmap(const char *gspath, const char *cidpath);
151 BOOL GetProgramFiles(LPTSTR path);
152 
153 
154 //////////////////////////////////////////////////////////////////////
155 // Entry point
156 //////////////////////////////////////////////////////////////////////
157 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)158 int APIENTRY WinMain(HINSTANCE hInstance,
159                      HINSTANCE hPrevInstance,
160                      LPSTR     lpCmdLine,
161                      int       nCmdShow)
162 {
163 	MSG msg;
164 	g_hInstance = hInstance;
165 
166 	if (!init()) {
167 		MessageBox(HWND_DESKTOP, "Initialisation failed",
168 			g_szAppName, MB_OK);
169 		return 1;
170 	}
171 
172 	if (!g_bBatch) {
173 		while (GetMessage(&msg, (HWND)NULL, 0, 0)) {
174 			if (!IsDialogMessage(g_hWndText, &msg) &&
175 				!IsDialogMessage(g_hMain, &msg)) {
176 				TranslateMessage(&msg);
177 				DispatchMessage(&msg);
178 			}
179 		}
180 		DestroyWindow(g_hMain);
181 	}
182 
183 	return (g_bError ? 1 : 0);
184 }
185 
186 
187 
188 
189 //////////////////////////////////////////////////////////////////////
190 // Text log window
191 //////////////////////////////////////////////////////////////////////
192 
193 
194 #define TWLENGTH 32768
195 #define TWSCROLL 1024
196 char twbuf[TWLENGTH];
197 int twend;
198 
199 // Modeless Dialog Box
200 DLGRETURN CALLBACK
TextWinDlgProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)201 TextWinDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
202 {
203     switch(message) {
204 	case WM_INITDIALOG:
205 		EnableWindow(g_hMain, FALSE);
206 		return TRUE;
207 	case WM_COMMAND:
208 		switch(LOWORD(wParam)) {
209 		case IDC_TEXTWIN_COPY:
210 			{HGLOBAL hglobal;
211 			LPSTR p;
212 			DWORD result;
213 			int start, end;
214 			result = SendDlgItemMessage(hwnd, IDC_TEXTWIN_MLE, EM_GETSEL, (WPARAM)0, (LPARAM)0);
215 			start = LOWORD(result);
216 			end   = HIWORD(result);
217 			if (start == end) {
218 				start = 0;
219 				end = twend;
220 			}
221 			hglobal = GlobalAlloc(GHND | GMEM_SHARE, end-start+1);
222 			if (hglobal == (HGLOBAL)NULL) {
223 				MessageBeep(-1);
224 				return(FALSE);
225 			}
226 			p = (char *)GlobalLock(hglobal);
227 			if (p == (LPSTR)NULL) {
228 				MessageBeep(-1);
229 				return(FALSE);
230 			}
231 			lstrcpyn(p, twbuf+start, end-start);
232 			GlobalUnlock(hglobal);
233 			OpenClipboard(hwnd);
234 			EmptyClipboard();
235 			SetClipboardData(CF_TEXT, hglobal);
236 			CloseClipboard();
237 			}
238 			break;
239 		case IDCANCEL:
240 			g_bQuit = TRUE;
241 			DestroyWindow(hwnd);
242 			return TRUE;
243 		}
244 		break;
245 	case WM_CLOSE:
246 			DestroyWindow(hwnd);
247 			return TRUE;
248 	case WM_DESTROY:
249 			g_bQuit = TRUE;
250 			g_hWndText = (HWND)NULL;
251 			EnableWindow(g_hMain, TRUE);
252 			PostQuitMessage(0);
253 			break;
254 	}
255 	return FALSE;
256 }
257 
258 
259 
260 // Add string to log window
261 void
gs_addmess_count(const char * str,int count)262 gs_addmess_count(const char *str, int count)
263 {
264 	const char *s;
265 	char *p;
266 	int i, lfcount;
267 	MSG msg;
268 
269 	// we need to add \r after each \n, so count the \n's
270 	lfcount = 0;
271 	s = str;
272 	for (i=0; i<count; i++) {
273 		if (*s == '\n')
274 			lfcount++;
275 		s++;
276 	}
277 
278 	if (count + lfcount >= TWSCROLL)
279 		return;		// too large
280 	if (count + lfcount + twend >= TWLENGTH-1) {
281 		// scroll buffer
282 		twend -= TWSCROLL;
283 		memmove(twbuf, twbuf+TWSCROLL, twend);
284 	}
285 	p = twbuf+twend;
286 	for (i=0; i<count; i++) {
287 		if (*str == '\n') {
288 			*p++ = '\r';
289 		}
290 		*p++ = *str++;
291 	}
292 	twend += (count + lfcount);
293 	*(twbuf+twend) = '\0';
294 
295 
296 	// Update the dialog box
297 	if (g_bBatch)
298 		return;
299 
300 	gs_addmess_update();
301 	while (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE)) {
302 		if (!IsDialogMessage(g_hWndText, &msg) &&
303 			!IsDialogMessage(g_hMain, &msg)) {
304 			TranslateMessage(&msg);
305 			DispatchMessage(&msg);
306 		}
307 	}
308 }
309 
310 void
gs_addmess(const char * str)311 gs_addmess(const char *str)
312 {
313 	gs_addmess_count(str, lstrlen(str));
314 
315 }
316 
317 
318 void
gs_addmess_update(void)319 gs_addmess_update(void)
320 {
321 	HWND hwndmess = g_hWndText;
322 
323 	if (g_bBatch)
324 		return;
325 
326 	if (IsWindow(hwndmess)) {
327 		HWND hwndtext = GetDlgItem(hwndmess, IDC_TEXTWIN_MLE);
328 		DWORD linecount;
329 		SendMessage(hwndtext, WM_SETREDRAW, FALSE, 0);
330 		SetDlgItemText(hwndmess, IDC_TEXTWIN_MLE, twbuf);
331 		linecount = SendDlgItemMessage(hwndmess, IDC_TEXTWIN_MLE, EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0);
332 		SendDlgItemMessage(hwndmess, IDC_TEXTWIN_MLE, EM_LINESCROLL, (WPARAM)0, (LPARAM)linecount-14);
333 		SendMessage(hwndtext, WM_SETREDRAW, TRUE, 0);
334 		InvalidateRect(hwndtext, (LPRECT)NULL, TRUE);
335 		UpdateWindow(hwndtext);
336 	}
337 }
338 
339 
340 //////////////////////////////////////////////////////////////////////
341 // Browse dialog box
342 //////////////////////////////////////////////////////////////////////
343 
344 // nasty GLOBALS
345 char szFolderName[MAXSTR];
346 char szDirName[MAXSTR];
347 
348 DLGRETURN CALLBACK
DirDlgProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)349 DirDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
350 {
351 	WORD notify_message;
352 
353 	switch(message) {
354 	case WM_INITDIALOG:
355 		DlgDirList(hwnd, szDirName, IDC_FILES, IDC_FOLDER,
356 			DDL_DRIVES | DDL_DIRECTORY);
357 		SetDlgItemText(hwnd, IDC_TARGET, szFolderName);
358 		return FALSE;
359     case WM_COMMAND:
360 		notify_message = HIWORD(wParam);
361 		switch (LOWORD(wParam)) {
362 		case IDC_FILES:
363 			if (notify_message == LBN_DBLCLK) {
364 				CHAR szPath[MAXSTR];
365 				DlgDirSelectEx(hwnd, szPath, sizeof(szPath), IDC_FILES);
366 				DlgDirList(hwnd, szPath, IDC_FILES, IDC_FOLDER,
367 					DDL_DRIVES | DDL_DIRECTORY);
368 			}
369 			return FALSE;
370 		case IDOK:
371 			GetDlgItemText(hwnd, IDC_FOLDER, szDirName, sizeof(szDirName));
372 			GetDlgItemText(hwnd, IDC_TARGET, szFolderName, sizeof(szFolderName));
373 			EndDialog(hwnd, TRUE);
374 			return TRUE;
375 		case IDCANCEL:
376 			EndDialog(hwnd, FALSE);
377 			return TRUE;
378 		}
379 		return FALSE;
380 	}
381 	return FALSE;
382 }
383 
384 
385 //////////////////////////////////////////////////////////////////////
386 // Initialisation and Main dialog box
387 //////////////////////////////////////////////////////////////////////
388 
389 void
message_box(const char * str)390 message_box(const char *str)
391 {
392 	MessageBox(HWND_DESKTOP, str, g_szAppName, MB_OK);
393 }
394 
395 
396 BOOL
init()397 init()
398 {
399 	DWORD dwVersion = GetVersion();
400 	// get source directory
401 	GetCurrentDirectory(sizeof(g_szSourceDir), g_szSourceDir);
402 
403 	// load strings
404 	LoadString(g_hInstance, IDS_APPNAME, g_szAppName, sizeof(g_szAppName));
405 	LoadString(g_hInstance, IDS_TARGET_GROUP,
406 		g_szTargetGroup, sizeof(g_szTargetGroup));
407 
408 	if (LOBYTE(LOWORD(dwVersion)) < 4) {
409         MessageBox(HWND_DESKTOP,
410 			"This install program needs Windows 4.0 or later",
411 			g_szAppName, MB_OK);
412 		return FALSE;
413 	}
414 	if ( (HIWORD(dwVersion) & 0x8000) == 0)
415 		is_winnt = TRUE;
416 
417 
418 	cinst.SetMessageFunction(message_box);
419 
420 #define MAXCMDTOKENS 128
421 
422 	int argc;
423 	LPSTR argv[MAXCMDTOKENS];
424 	LPSTR p;
425 	char command[256];
426 	char *args;
427 	char *d, *e;
428 
429 	p = GetCommandLine();
430 
431 	argc = 0;
432 	args = (char *)malloc(lstrlen(p)+1);
433 	if (args == (char *)NULL)
434 		return 1;
435 
436 	// Parse command line handling quotes.
437 	d = args;
438 	while (*p) {
439 		// for each argument
440 
441 		if (argc >= MAXCMDTOKENS - 1)
442 			break;
443 
444 		e = d;
445 		while ((*p) && (*p != ' ')) {
446 			if (*p == '\042') {
447 				// Remove quotes, skipping over embedded spaces.
448 				// Doesn't handle embedded quotes.
449 				p++;
450 				while ((*p) && (*p != '\042'))
451 					*d++ =*p++;
452 			}
453 			else
454 				*d++ = *p;
455 			if (*p)
456 				p++;
457 		}
458 		*d++ = '\0';
459 		argv[argc++] = e;
460 
461 		while ((*p) && (*p == ' '))
462 			p++;	// Skip over trailing spaces
463 	}
464 	argv[argc] = NULL;
465 
466 	if (strlen(argv[0]) == 0) {
467 		GetModuleFileName(g_hInstance, command, sizeof(command)-1);
468 		argv[0] = command;
469 	}
470 
471 	if (argc > 2) {
472 		// Probably creating filelist.txt
473 		return make_filelist(argc, argv);
474 	}
475 
476 
477 	// check if batch mode requested
478 	// get location of target directory from command line as argv[1]
479 	if (argc == 2) {
480 		strncpy(g_szTargetDir, argv[1], sizeof(g_szTargetDir));
481 		g_bBatch = TRUE;
482 		if (is_winnt)
483 			g_bAllUsers = TRUE;
484 	}
485 	if (g_bBatch) {
486 		if (!install_all()) {
487 			// display log showing error
488 			g_bBatch = FALSE;
489 			g_hWndText = CreateDialogParam(g_hInstance,
490 				MAKEINTRESOURCE(IDD_TEXTWIN),
491 				(HWND)HWND_DESKTOP, TextWinDlgProc,
492 				(LPARAM)NULL);
493 			gs_addmess_update();
494 		}
495 		return TRUE;
496 	}
497 
498 	// Interactive setup
499 	if (!GetProgramFiles(g_szTargetDir))
500 	    strcpy(g_szTargetDir, "C:\\Program Files");
501 	strcat(g_szTargetDir, "\\");
502 	LoadString(g_hInstance, IDS_TARGET_DIR,
503 	    g_szTargetDir+strlen(g_szTargetDir),
504 	    sizeof(g_szTargetDir)-strlen(g_szTargetDir));
505 
506 	// main dialog box
507 	g_hMain = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_MAIN), (HWND)NULL, MainDlgProc, (LPARAM)NULL);
508 	// centre dialog on screen
509 	int width = GetSystemMetrics(SM_CXFULLSCREEN);
510 	int height = GetSystemMetrics(SM_CYFULLSCREEN);
511 	RECT rect;
512 	GetWindowRect(g_hMain, &rect);
513 	MoveWindow(g_hMain, (width - (rect.right - rect.left))/2,
514 		(height - (rect.bottom - rect.top))/2,
515 		(rect.right - rect.left),
516 		(rect.bottom - rect.top), FALSE);
517 
518 	// initialize targets
519 	cinst.SetMessageFunction(message_box);
520 	if (!cinst.Init(g_szSourceDir, "filelist.txt"))
521 		return FALSE;
522 
523 	SetDlgItemText(g_hMain, IDC_TARGET_DIR, g_szTargetDir);
524 	SetDlgItemText(g_hMain, IDC_TARGET_GROUP, g_szTargetGroup);
525 	SetDlgItemText(g_hMain, IDC_PRODUCT_NAME, cinst.GetUninstallName());
526 	ShowWindow(g_hMain, SW_SHOWNORMAL);
527 
528 	return (g_hMain != (HWND)NULL); /* success */
529 }
530 
531 
532 // Main Modeless Dialog Box
533 DLGRETURN CALLBACK
MainDlgProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)534 MainDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
535 {
536 	switch(message) {
537 	case WM_INITDIALOG:
538 		EnableWindow(GetDlgItem(hwnd, IDC_ALLUSERS), is_winnt);
539 		return TRUE;
540 	case WM_COMMAND:
541 		switch(LOWORD(wParam)) {
542 		case IDC_README:
543 			{
544 			char buf[MAXSTR];
545 			sprintf(buf, "%s\\%s\\doc\\Readme.htm", g_szSourceDir,
546 				cinst.GetMainDir());
547 			ShellExecute(hwnd, NULL, buf, NULL, g_szSourceDir,
548 				SW_SHOWNORMAL);
549 			}
550 			return TRUE;
551 		case IDC_BROWSE_DIR:
552 			{ char dir[MAXSTR];
553 			char *p;
554 			GetDlgItemText(hwnd, IDC_TARGET_DIR, dir, sizeof(dir));
555 			strcpy(szDirName, dir);
556 			if ( (p = strrchr(szDirName, '\\')) != (char *)NULL ) {
557 				strcpy(szFolderName, p+1);
558 				if (p == szDirName+2)
559 					p++;	// step over c:\   //
560 				*p = '\0';
561 			}
562 			else {
563 				strcpy(szDirName, "c:\\");
564 				strcpy(szFolderName, dir);
565 			}
566 			if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DIRDLG),
567 				hwnd, DirDlgProc)) {
568 				strcpy(dir, szDirName);
569 				if (strlen(dir) && (dir[strlen(dir)-1] != '\\'))
570 					strcat(dir, "\\");
571 				strcat(dir, szFolderName);
572 				SetDlgItemText(hwnd, IDC_TARGET_DIR, dir);
573 			}
574 			}
575 			return TRUE;
576 		case IDC_BROWSE_GROUP:
577 			{ char dir[MAXSTR];
578 			char programs[MAXSTR];
579 			char *p;
580 			GetDlgItemText(hwnd, IDC_TARGET_GROUP, dir, sizeof(dir));
581 			cinst.GetPrograms(
582 				SendDlgItemMessage(hwnd, IDC_ALLUSERS,
583 				BM_GETCHECK, 0, 0) == BST_CHECKED,
584 				programs, sizeof(programs));
585 			strcpy(szDirName, programs);
586 			strcpy(szFolderName, dir);
587 			if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DIRDLG),
588 				hwnd, DirDlgProc)) {
589 				strcpy(dir, szFolderName);
590 				p = szDirName;
591 				if (strnicmp(szDirName, programs,
592 					strlen(programs)) == 0) {
593 					p += strlen(programs);
594 					if (*p == '\\')
595 						p++;
596 					strcpy(dir, p);
597 					if (strlen(dir) &&
598 						(dir[strlen(dir)-1] != '\\'))
599 						strcat(dir, "\\");
600 					strcat(dir, szFolderName);
601 				}
602 				SetDlgItemText(hwnd, IDC_TARGET_GROUP, dir);
603 			}
604 			}
605 			return TRUE;
606 		case IDCANCEL:
607 			PostQuitMessage(0);
608 			return TRUE;
609 		case IDC_INSTALL:
610 			GetDlgItemText(hwnd, IDC_TARGET_DIR,
611 				g_szTargetDir, sizeof(g_szTargetDir));
612 			GetDlgItemText(hwnd, IDC_TARGET_GROUP,
613 				g_szTargetGroup, sizeof(g_szTargetGroup));
614 			g_bCJKFonts = (SendDlgItemMessage(g_hMain,
615 				IDC_CJK_FONTS, BM_GETCHECK, 0, 0)
616 				== BST_CHECKED);
617 			g_bAllUsers = (SendDlgItemMessage(hwnd,
618 				IDC_ALLUSERS, BM_GETCHECK, 0, 0
619 				) == BST_CHECKED);
620 
621 			// install log dialog box
622 			g_hWndText = CreateDialogParam(g_hInstance,
623 				MAKEINTRESOURCE(IDD_TEXTWIN),
624 				(HWND)hwnd, TextWinDlgProc, (LPARAM)NULL);
625 			EnableWindow(GetDlgItem(hwnd, IDC_INSTALL), FALSE);
626 			if (install_all())
627 				PostQuitMessage(0);
628 			return TRUE;
629 		default:
630 			return(FALSE);
631 		}
632 		case WM_CLOSE:
633 			PostQuitMessage(0);
634 			return TRUE;
635     }
636     return FALSE;
637 }
638 
639 // install program and files
640 BOOL
install_all()641 install_all()
642 {
643 	gs_addmess("Source Directory=");
644 	gs_addmess(g_szSourceDir);
645 	gs_addmess("\n");
646 	gs_addmess("Target Directory=");
647 	gs_addmess(g_szTargetDir);
648 	gs_addmess("\n");
649 	gs_addmess("Target Shell Folder=");
650 	gs_addmess(g_szTargetGroup);
651 	gs_addmess("\n");
652 	gs_addmess(g_bAllUsers ? "  All users\n" : "  Current user\n");
653 
654 	if (stricmp(g_szSourceDir, g_szTargetDir) == 0) {
655 		// Don't copy files
656 		if (!g_bBatch)
657 			if (::MessageBox(g_hWndText, "Install location is the same as the current file location.  No files will be copied.", g_szAppName, MB_OKCANCEL)
658 				!= IDOK) {
659 				return FALSE;
660 			}
661 		g_bNoCopy = TRUE;
662 	}
663 
664 
665 	if (g_bQuit)
666 		return FALSE;
667 
668 	if (!install_prog()) {
669 		cinst.CleanUp();
670 		g_bError = TRUE;
671 		return FALSE;
672 	}
673 
674 	gs_addmess("Install successful\n");
675 
676 	// show start menu folder
677 	if (!g_bBatch) {
678 		char szFolder[MAXSTR];
679 		szFolder[0] = '\0';
680 		cinst.GetPrograms(g_bAllUsers, szFolder, sizeof(szFolder));
681 		strcat(szFolder, "\\");
682 		strcat(szFolder, g_szTargetGroup);
683 		ShellExecute(HWND_DESKTOP, "open", szFolder,
684 			NULL, NULL, SW_SHOWNORMAL);
685 	}
686 
687 #ifdef DEBUG
688 	return FALSE;
689 #endif
690 
691 	return TRUE;
692 }
693 
694 BOOL
install_prog()695 install_prog()
696 {
697 	char *regkey1 = "GPL Ghostscript";
698 	char regkey2[16];
699 	char szDLL[MAXSTR];
700 	char szLIB[MAXSTR+MAXSTR];
701 	char szProgram[MAXSTR];
702 	char szArguments[MAXSTR];
703 	char szDescription[MAXSTR];
704 	char szDotVersion[MAXSTR];
705 	char szPlatformSuffix[MAXSTR];
706 	const char *pSuffix = "";
707 
708 	if (g_bQuit)
709 		return FALSE;
710 
711 	cinst.SetMessageFunction(gs_addmess);
712 	cinst.SetTargetDir(g_szTargetDir);
713 	cinst.SetTargetGroup(g_szTargetGroup);
714 	cinst.SetAllUsers(g_bAllUsers);
715 	if (!cinst.Init(g_szSourceDir, "filelist.txt"))
716 		return FALSE;
717 
718 	// Get GS version number
719 	gs_addmess("Installing Program...\n");
720 	int nGSversion = 0;
721 	const char *p = cinst.GetMainDir();
722 	while (*p && !isdigit(*p))	// skip over "gs" prefix
723 		p++;
724 	if (strlen(p) == 4)
725 		nGSversion = (p[0]-'0')*100 + (p[2]-'0')*10 + (p[3]-'0');
726 	else if (strlen(p) == 3)
727 		nGSversion = (p[0]-'0')*100 + (p[2]-'0')*10;
728         strncpy(szDotVersion, p, sizeof(szDotVersion));
729 	strncpy(regkey2, szDotVersion, sizeof(regkey2));
730 
731 	// copy files
732 	if (!cinst.InstallFiles(g_bNoCopy, &g_bQuit)) {
733 		gs_addmess("Program install failed\n");
734 		return FALSE;
735 	}
736 
737 	if (g_bQuit)
738 		return FALSE;
739 
740 	// write registry entries
741 	gs_addmess("Updating Registry\n");
742 	if (!cinst.UpdateRegistryBegin()) {
743 		gs_addmess("Failed to begin registry update\n");
744 		return FALSE;
745 	}
746 	if (!cinst.UpdateRegistryKey(regkey1, regkey2)) {
747 		gs_addmess("Failed to open/create registry application key\n");
748 		return FALSE;
749 	}
750 	strcpy(szDLL, g_szTargetDir);
751 	strcat(szDLL, "\\");
752 	strcat(szDLL, cinst.GetMainDir());
753 	strcat(szDLL, "\\bin\\gsdll32.dll");
754 	if (!cinst.UpdateRegistryValue(regkey1, regkey2, "GS_DLL", szDLL)) {
755 		gs_addmess("Failed to add registry value\n");
756 		return FALSE;
757 	}
758 	strcpy(szLIB, g_szTargetDir);
759 	strcat(szLIB, "\\");
760 	strcat(szLIB, cinst.GetMainDir());
761 	strcat(szLIB, "\\lib;");
762 	strcat(szLIB, g_szTargetDir);
763 	strcat(szLIB, "\\fonts");
764 	if (g_bCJKFonts) {
765 	    strcat(szLIB, ";");
766 	    get_font_path(szLIB+strlen(szLIB), sizeof(szLIB)-strlen(szLIB)-1);
767 	}
768 	if (!cinst.UpdateRegistryValue(regkey1, regkey2, "GS_LIB", szLIB)) {
769 		gs_addmess("Failed to add registry value\n");
770 		return FALSE;
771 	}
772 	if (!cinst.UpdateRegistryEnd()) {
773 		gs_addmess("Failed to end registry update\n");
774 		return FALSE;
775 	}
776 	if (g_bQuit)
777 		return FALSE;
778 
779 	// Add Start Menu items
780 	gs_addmess("Adding Start Menu items\n");
781 
782         memset(szPlatformSuffix, 0, sizeof(szPlatformSuffix));
783 	if (GetProgramFiles(szPlatformSuffix)) {
784 	    /* If ProgramFiles has a suffix like " (x86)" then use
785 	     * it for Start menu entries to distinguish between
786 	     * 32-bit and 64-bit programs.
787 	     */
788 	    for (pSuffix = szPlatformSuffix; *pSuffix; pSuffix++)
789 		if ((pSuffix[0] == ' ') && (pSuffix[1] == '('))
790 		    break;
791 	}
792 	else {
793 	    pSuffix = "";
794 	}
795 
796 
797 	if (!cinst.StartMenuBegin()) {
798 		gs_addmess("Failed to begin Start Menu update\n");
799 		return FALSE;
800 	}
801 	strcpy(szProgram, g_szTargetDir);
802 	strcat(szProgram, "\\");
803 	strcat(szProgram, cinst.GetMainDir());
804 	strcat(szProgram, "\\bin\\gswin32.exe");
805 	strcpy(szArguments, "\042-I");
806 	strcat(szArguments, szLIB);
807 	strcat(szArguments, "\042");
808 	sprintf(szDescription, "Ghostscript %s%s", szDotVersion, pSuffix);
809 	if (!cinst.StartMenuAdd(szDescription, szProgram, szArguments)) {
810 		gs_addmess("Failed to add Start Menu item\n");
811 		return FALSE;
812 	}
813 	strcpy(szProgram, g_szTargetDir);
814 	strcat(szProgram, "\\");
815 	strcat(szProgram, cinst.GetMainDir());
816 	strcat(szProgram, "\\doc\\Readme.htm");
817 	sprintf(szDescription, "Ghostscript Readme %s%s",
818 		szDotVersion, pSuffix);
819 	if (!cinst.StartMenuAdd(szDescription, szProgram, NULL)) {
820 		gs_addmess("Failed to add Start Menu item\n");
821 		return FALSE;
822 	}
823 	if (!cinst.StartMenuEnd()) {
824 		gs_addmess("Failed to end Start Menu update\n");
825 		return FALSE;
826 	}
827 
828         /* Create lib/cidfmap */
829 	if (g_bCJKFonts) {
830 		char szCIDFmap[MAXSTR];
831 		char szCIDFmap_bak[MAXSTR];
832 		char szGSPATH[MAXSTR];
833 
834 		/* backup old cidfmap */
835 		strcpy(szCIDFmap, g_szTargetDir);
836 		strcat(szCIDFmap, "\\");
837 		strcat(szCIDFmap, cinst.GetMainDir());
838 		strcat(szCIDFmap, "\\lib\\cidfmap");
839 		strcpy(szCIDFmap_bak, szCIDFmap);
840 		strcat(szCIDFmap_bak, ".bak");
841 		gs_addmess("Backing up\n  ");
842 		gs_addmess(szCIDFmap);
843 		gs_addmess("\nto\n  ");
844 		gs_addmess(szCIDFmap_bak);
845 		gs_addmess("\n");
846 		rename(szCIDFmap, szCIDFmap_bak);
847 
848 		/* mark backup for uninstall */
849 		cinst.AppendFileNew(szCIDFmap_bak);
850 
851 		/* write new cidfmap */
852 		gs_addmess("Writing cidfmap\n   ");
853 		gs_addmess(szCIDFmap);
854 		gs_addmess("\n");
855 		strcpy(szGSPATH, g_szTargetDir);
856 		strcat(szGSPATH, "\\");
857 		strcat(szGSPATH, cinst.GetMainDir());
858 		if (!write_cidfmap(szGSPATH, szCIDFmap)) {
859 			gs_addmess("Failed to write cidfmap\n");
860 			return FALSE;
861 		}
862 	}
863 
864 	// consolidate logs into one uninstall file
865 	if (cinst.MakeLog()) {
866 		// add uninstall entry for "Add/Remove Programs"
867 		gs_addmess("Adding uninstall program\n");
868 		if (!cinst.WriteUninstall(UNINSTALLPROG, g_bNoCopy)) {
869 			gs_addmess("Failed to write uninstall entry\n");
870 			return FALSE;
871 		}
872 	}
873 	else {
874 		gs_addmess("Failed to write uninstall log\n");
875 		// If batch install, files might be on a server
876 		// in a write protected directory.
877 		// Don't return an error for batch install.
878 		if (g_bBatch)
879 			return TRUE;
880 		return FALSE;
881 	}
882 
883 	gs_addmess("Program install successful\n");
884 	return TRUE;
885 }
886 
887 
888 
889 //////////////////////////////////////////////////////////////////////
890 // Create lib/cidfmap based on installed fonts
891 //////////////////////////////////////////////////////////////////////
892 
893 /* Get the path to enumerate for fonts */
894 int
get_font_path(char * path,unsigned int pathlen)895 get_font_path(char *path, unsigned int pathlen)
896 {
897     int i;
898     int len = GetWindowsDirectory(path, pathlen);
899     if (len == 0)
900        return -1;
901     if (pathlen - strlen(path) < 8)
902        return -1;
903     strncat(path, "/fonts", pathlen - strlen(path) - 7);
904     for (i = strlen(path)-1; i >= 0; i--)
905        if (path[i] == '\\')
906            path[i] = '/';
907     return len;
908 }
909 
write_cidfmap(const char * gspath,const char * cidpath)910 BOOL write_cidfmap(const char *gspath, const char *cidpath)
911 {
912     char fontpath[MAXSTR];
913     char buf[4*MAXSTR];
914     STARTUPINFO siStartInfo;
915     PROCESS_INFORMATION piProcInfo;
916 
917     get_font_path(fontpath, sizeof(fontpath)-1);
918 
919     strcpy(buf, "\042");
920     strcat(buf, gspath);
921     strcat(buf, "\\bin\\gswin32c.exe\042 -q -dBATCH \042-sFONTDIR=");
922     strcat(buf, fontpath);
923     strcat(buf, "\042 \042");
924     strcat(buf, "-sCIDFMAP=");
925     strcat(buf, cidpath);
926     strcat(buf, "\042 \042");
927     strcat(buf, gspath);
928     strcat(buf, "\\lib\\mkcidfm.ps\042");
929 
930     siStartInfo.cb = sizeof(STARTUPINFO);
931     siStartInfo.lpReserved = NULL;
932     siStartInfo.lpDesktop = NULL;
933     siStartInfo.lpTitle = NULL;  /* use executable name as title */
934     siStartInfo.dwX = siStartInfo.dwY = CW_USEDEFAULT;		/* ignored */
935     siStartInfo.dwXSize = siStartInfo.dwYSize = CW_USEDEFAULT;	/* ignored */
936     siStartInfo.dwXCountChars = 80;
937     siStartInfo.dwYCountChars = 25;
938     siStartInfo.dwFillAttribute = 0;			/* ignored */
939     siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
940     siStartInfo.wShowWindow = SW_HIDE;
941     siStartInfo.cbReserved2 = 0;
942     siStartInfo.lpReserved2 = NULL;
943     siStartInfo.hStdInput = NULL;
944     siStartInfo.hStdOutput = NULL;
945     siStartInfo.hStdError = NULL;
946 
947     /* Create the child process. */
948     if (!CreateProcess(NULL,
949         (char *)buf,  /* command line                       */
950         NULL,          /* process security attributes        */
951         NULL,          /* primary thread security attributes */
952         FALSE,         /* handles are not inherited          */
953         0,             /* creation flags                     */
954         NULL,          /* environment                        */
955         NULL,          /* use parent's current directory     */
956         &siStartInfo,  /* STARTUPINFO pointer                */
957         &piProcInfo))  /* receives PROCESS_INFORMATION  */
958 	    return FALSE;
959 
960     /* We don't care if ghostscript fails, so just return */
961 
962     CloseHandle(piProcInfo.hProcess);
963     CloseHandle(piProcInfo.hThread);
964 
965     return TRUE;
966 }
967 
968 
969 //////////////////////////////////////////////////////////////////////
970 // Create file list
971 //////////////////////////////////////////////////////////////////////
972 
973 FILE *fList;
974 
975 typedef int (*PFN_dodir)(const char *name);
976 
977 /* Called once for each directory */
978 int
dodir(const char * filename)979 dodir(const char *filename)
980 {
981     return 0;
982 }
983 
984 /* Called once for each file */
985 int
dofile(const char * filename)986 dofile(const char *filename)
987 {
988     if (fList != (FILE *)NULL) {
989 		fputs(filename, fList);
990 		fputs("\n", fList);
991     }
992 
993     return 0;
994 }
995 
996 
997 /* Walk through directory 'path', calling dodir() for given directory
998  * and dofile() for each file.
999  * If recurse=1, recurse into subdirectories, calling dodir() for
1000  * each directory.
1001  */
1002 int
dirwalk(char * path,int recurse,PFN_dodir dodir,PFN_dodir dofile)1003 dirwalk(char *path, int recurse, PFN_dodir dodir, PFN_dodir dofile)
1004 {
1005 	WIN32_FIND_DATA find_data;
1006 	HANDLE find_handle;
1007 	char pattern[MAXSTR];	/* orig pattern + modified pattern */
1008 	char base[MAXSTR];
1009 	char name[MAXSTR];
1010 	BOOL bMore = TRUE;
1011 	char *p;
1012 
1013 
1014 	if (path) {
1015 		strcpy(pattern, path);
1016 		if (strlen(pattern) != 0)  {
1017 			p = pattern + strlen(pattern) -1;
1018 			if (*p == '\\')
1019 				*p = '\0';		// truncate trailing backslash
1020 		}
1021 
1022 		strcpy(base, pattern);
1023 		if (strchr(base, '*') != NULL) {
1024 			// wildcard already included
1025 			// truncate it from the base path
1026 			if ( (p = strrchr(base, '\\')) != NULL )
1027 				*(++p) = '\0';
1028 		}
1029 		else if (isalpha(pattern[0]) &&
1030 			pattern[1]==':' && pattern[2]=='\0')  {
1031 			strcat(pattern, "\\*");		// search entire disk
1032 			strcat(base, "\\");
1033 		}
1034 		else {
1035 			// wildcard NOT included
1036 			// check to see if path is a directory
1037 			find_handle = FindFirstFile(pattern, &find_data);
1038 			if (find_handle != INVALID_HANDLE_VALUE) {
1039 				FindClose(find_handle);
1040 				if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1041 					strcat(pattern, "\\*");		// yes, search files
1042 					strcat(base, "\\");
1043 				}
1044 				else {
1045 					dofile(path);				// no, return just this file
1046 					return 0;
1047 				}
1048 			}
1049 			else
1050 				return 1;	// path invalid
1051 		}
1052 	}
1053 	else {
1054 		base[0] = '\0';
1055 		strcpy(pattern, "*");
1056 	}
1057 
1058 	find_handle = FindFirstFile(pattern,  &find_data);
1059 	if (find_handle == INVALID_HANDLE_VALUE)
1060 		return 1;
1061 
1062 	while (bMore) {
1063 		strcpy(name, base);
1064 		strcat(name, find_data.cFileName);
1065 		if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1066 			if ( strcmp(find_data.cFileName, ".") &&
1067 				strcmp(find_data.cFileName, "..") ) {
1068 				dodir(name);
1069 				if (recurse)
1070 					dirwalk(name, recurse, dodir, dofile);
1071 			}
1072 		}
1073 		else {
1074 			dofile(name);
1075 		}
1076 		bMore = FindNextFile(find_handle, &find_data);
1077 	}
1078 	FindClose(find_handle);
1079 
1080 	return 0;
1081 }
1082 
1083 
1084 
1085 // This is used when creating a file list.
1086 
make_filelist(int argc,char * argv[])1087 BOOL make_filelist(int argc, char *argv[])
1088 {
1089     char *title = NULL;
1090     char *dir = NULL;
1091     char *list = NULL;
1092     int i;
1093     g_bBatch = TRUE;	// Don't run message loop
1094 
1095     for (i=1; i<argc; i++) {
1096 		if (strcmp(argv[i], "-title") == 0) {
1097 			i++;
1098 			title = argv[i];
1099 		}
1100 		else if (strcmp(argv[i], "-dir") == 0) {
1101 			i++;
1102 			dir = argv[i];
1103 		}
1104 		else if (strcmp(argv[i], "-list") == 0) {
1105 			i++;
1106 			list = argv[i];
1107 		}
1108 		else {
1109 		    if ((title == NULL) || (strlen(title) == 0) ||
1110 			(dir == NULL) || (strlen(dir) == 0) ||
1111 			(list == NULL) || (strlen(list) == 0)) {
1112 			message_box("Usage: setupgs -title \042GPL Ghostscript #.##\042 -dir \042gs#.##\042 -list \042filelist.txt\042 spec1 spec2 specn\n");
1113 			return FALSE;
1114 		    }
1115 		    if (fList == (FILE *)NULL) {
1116 			    if ( (fList = fopen(list, "w")) == (FILE *)NULL ) {
1117 					message_box("Can't write list file\n");
1118 					return FALSE;
1119 			    }
1120 			    fputs(title, fList);
1121 			    fputs("\n", fList);
1122 			    fputs(dir, fList);
1123 			    fputs("\n", fList);
1124 		    }
1125 		    if (argv[i][0] == '@') {
1126 			// Use @filename with list of files/directories
1127 			// to avoid DOS command line limit
1128 			FILE *f;
1129 			char buf[MAXSTR];
1130 			int j;
1131 			if ( (f = fopen(&(argv[i][1]), "r")) != (FILE *)NULL) {
1132 			    while (fgets(buf, sizeof(buf), f)) {
1133 				// remove trailing newline and spaces
1134 				while ( ((j = strlen(buf)-1) >= 0) &&
1135 				    ((buf[j] == '\n') || (buf[j] == ' ')) )
1136 				    buf[j] = '\0';
1137 			        dirwalk(buf, TRUE, &dodir, &dofile);
1138 			    }
1139 			    fclose(f);
1140 			}
1141 			else {
1142 				wsprintf(buf, "Can't open @ file \042%s\042",
1143 				    &argv[i][1]);
1144 				message_box(buf);
1145 			}
1146 		    }
1147 		    else
1148 		        dirwalk(argv[i], TRUE, &dodir, &dofile);
1149 		}
1150     }
1151 
1152     if (fList != (FILE *)NULL) {
1153         fclose(fList);
1154 	fList = NULL;
1155     }
1156     return TRUE;
1157 }
1158 
1159 //////////////////////////////////////////////////////////////////////
1160 
1161 #ifndef CSIDL_PROGRAM_FILES
1162 #define CSIDL_PROGRAM_FILES 0x0026
1163 #endif
1164 #ifndef CSIDL_FLAG_CREATE
1165 #define CSIDL_FLAG_CREATE 0x8000
1166 #endif
1167 #ifndef SHGFP_TYPE_CURRENT
1168 #define SHGFP_TYPE_CURRENT 0
1169 #endif
1170 
1171 BOOL
GetProgramFiles(LPTSTR path)1172 GetProgramFiles(LPTSTR path)
1173 {
1174     PFN_SHGetSpecialFolderPath PSHGetSpecialFolderPath = NULL;
1175     PFN_SHGetFolderPath PSHGetFolderPath = NULL;
1176     HMODULE hModuleShell32 = NULL;
1177     HMODULE hModuleShfolder = NULL;
1178     BOOL fOk = FALSE;
1179     hModuleShfolder = LoadLibrary("shfolder.dll");
1180     hModuleShell32 = LoadLibrary("shell32.dll");
1181 
1182     if (hModuleShfolder) {
1183 	PSHGetFolderPath = (PFN_SHGetFolderPath)
1184 	    GetProcAddress(hModuleShfolder, "SHGetFolderPathA");
1185 	if (PSHGetFolderPath) {
1186 	    fOk = (PSHGetFolderPath(HWND_DESKTOP,
1187 		CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE,
1188 		NULL, SHGFP_TYPE_CURRENT, path) == S_OK);
1189 	}
1190     }
1191 
1192     if (!fOk && hModuleShell32) {
1193 	PSHGetFolderPath = (PFN_SHGetFolderPath)
1194 	    GetProcAddress(hModuleShell32, "SHGetFolderPathA");
1195 	if (PSHGetFolderPath) {
1196 	    fOk = (PSHGetFolderPath(HWND_DESKTOP,
1197 		CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE,
1198 		NULL, SHGFP_TYPE_CURRENT, path) == S_OK);
1199 	}
1200     }
1201 
1202     if (!fOk && hModuleShell32) {
1203 	PSHGetSpecialFolderPath = (PFN_SHGetSpecialFolderPath)
1204 	    GetProcAddress(hModuleShell32, "SHGetSpecialFolderPathA");
1205 	if (PSHGetSpecialFolderPath) {
1206 	    fOk = PSHGetSpecialFolderPath(HWND_DESKTOP, path,
1207 		CSIDL_PROGRAM_FILES, TRUE);
1208 	}
1209     }
1210 
1211     if (!fOk) {
1212 	/* If all else fails (probably Win95), try the registry */
1213 	LONG rc;
1214 	HKEY hkey;
1215 	DWORD cbData;
1216 	DWORD keytype;
1217 	rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1218 	    "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, &hkey);
1219 	if (rc == ERROR_SUCCESS) {
1220 	    cbData = MAX_PATH;
1221 	    keytype =  REG_SZ;
1222 	    if (rc == ERROR_SUCCESS)
1223 		rc = RegQueryValueEx(hkey, "ProgramFilesDir", 0, &keytype,
1224 		    (LPBYTE)path, &cbData);
1225 	    RegCloseKey(hkey);
1226 	}
1227 	fOk = (rc == ERROR_SUCCESS);
1228     }
1229     return fOk;
1230 }
1231 
1232 
1233 //////////////////////////////////////////////////////////////////////
1234 
1235