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