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