Added translation using Weblate (Japanese)
[cygwin-setup.git] / main.cc
blob1d65d536a8d1c2b05fb74229b7c0908ea6c86232
1 /*
2 * Copyright (c) 2000, Red Hat, Inc.
3 * Copyright (c) 2003, Robert Collins <rbtcollins@hotmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * A copy of the GNU General Public License can be found at
11 * http://www.gnu.org/
13 * Written by DJ Delorie <dj@cygnus.com>
14 * Robert Collins <rbtcollins@hotmail.com>
19 /* OK, here's how this works. Each of the steps needed for install -
20 dialogs, downloads, installs - are in their own files and have some
21 "do_*" function (prototype in dialog.h) and a resource id (IDD_* or
22 IDD_S_* in resource.h) for that step. Each step is responsible for
23 selecting the next step! See the NEXT macro in dialog.h. Note
24 that the IDD_S_* ids are fake; those are for steps that don't
25 really have a controlling dialog (some have progress dialogs, but
26 those don't count, although they could). Replace the IDD_S_* with
27 IDD_* if you create a real dialog for those steps. */
29 #include "win32.h"
30 #include <commctrl.h>
31 #include <shellapi.h>
32 #include "shlobj.h"
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include "resource.h"
37 #include "dialog.h"
38 #include "state.h"
39 #include "msg.h"
40 #include "find.h"
41 #include "mount.h"
42 #include "LogFile.h"
43 #include "setup_version.h"
45 #include "proppage.h"
46 #include "propsheet.h"
48 // Page class headers
49 #include "splash.h"
50 #include "AntiVirus.h"
51 #include "source.h"
52 #include "root.h"
53 #include "localdir.h"
54 #include "net.h"
55 #include "site.h"
56 #include "choose.h"
57 #include "prereq.h"
58 #include "confirm.h"
59 #include "threebar.h"
60 #include "desktop.h"
61 #include "postinstallresults.h"
63 #include "getopt++/GetOption.h"
64 #include "getopt++/BoolOption.h"
65 #include "getopt++/StringOption.h"
66 #include "getopt++/StringChoiceOption.h"
67 #include "mklink2.h"
69 #include "Exception.h"
70 #include <stdexcept>
72 #include "UserSettings.h"
73 #include "SourceSetting.h"
74 #include "ConnectionSetting.h"
75 #include "KeysSetting.h"
77 #include <wincon.h>
78 #include <fstream>
80 #ifdef __MINGW64_VERSION_MAJOR
81 extern char **_argv;
82 #endif
84 bool is_64bit;
85 bool is_new_install = false;
86 std::string SetupArch;
87 std::string SetupIniDir;
89 HINSTANCE hinstance;
91 static StringChoiceOption::StringChoices symlink_types({
92 {"native", SymlinkTypeNative},
93 {"lnk", SymlinkTypeShortcut},
94 {"sys", SymlinkTypeMagic},
95 {"wsl", SymlinkTypeWsl},
96 });
98 static StringOption Arch ("", 'a', "arch", IDS_HELPTEXT_ARCH, false);
99 static BoolOption UnattendedOption (false, 'q', "quiet-mode", IDS_HELPTEXT_QUIET_MODE);
100 static BoolOption PackageManagerOption (false, 'M', "package-manager", IDS_HELPTEXT_PACKAGE_MANAGER);
101 static BoolOption NoAdminOption (false, 'B', "no-admin", IDS_HELPTEXT_NO_ADMIN);
102 static BoolOption WaitOption (false, 'W', "wait", IDS_HELPTEXT_WAIT);
103 static BoolOption HelpOption (false, 'h', "help", IDS_HELPTEXT_HELP);
104 static BoolOption VersionOption (false, 'V', "version", IDS_HELPTEXT_VERSION);
105 static StringOption SetupBaseNameOpt ("setup", 'i', "ini-basename", IDS_HELPTEXT_INI_BASENAME, false);
106 BoolOption UnsupportedOption (false, '\0', "allow-unsupported-windows", IDS_HELPTEXT_ALLOW_UNSUPPORTED_WINDOWS);
107 static BoolOption DeprecatedOption (false, 'w', "no-warn-deprecated-windows", IDS_HELPTEXT_NO_WARN_DEPRECATED_WINDOWS);
108 static StringChoiceOption SymlinkTypeOption(symlink_types, '\0', "symlink-type", IDS_HELPTEXT_SYMLINK_TYPE, false, SymlinkTypeMagic);
109 static StringOption GuiLangOption ("", '\0', "lang", IDS_HELPTEXT_LANG);
111 std::string SetupBaseName;
113 static void inline
114 set_cout ()
116 HANDLE my_stdout = GetStdHandle (STD_OUTPUT_HANDLE);
117 if (my_stdout != INVALID_HANDLE_VALUE && GetFileType (my_stdout) != FILE_TYPE_UNKNOWN)
118 return;
120 if (AttachConsole ((DWORD) -1))
122 std::ofstream *conout = new std::ofstream ("conout$");
123 std::cout.rdbuf (conout->rdbuf ());
124 std::cout.flush ();
128 // Other threads talk to these pages, so we need to have it externable.
129 ThreeBarProgressPage Progress;
130 PostInstallResultsPage PostInstallResults;
132 static inline void
133 main_display ()
135 /* nondisplay classes */
136 LocalDirSetting localDir;
137 SourceSetting SourceSettings;
138 ConnectionSetting ConnectionSettings;
139 SiteSetting ChosenSites;
140 ExtraKeysSetting ExtraKeys;
142 SplashPage Splash;
143 AntiVirusPage AntiVirus;
144 SourcePage Source;
145 RootPage Root;
146 LocalDirPage LocalDir;
147 NetPage Net;
148 SitePage Site;
149 ChooserPage Chooser;
150 PrereqPage Prereq;
151 ConfirmPage Confirm;
152 DesktopSetupPage Desktop;
153 PropSheet MainWindow;
155 Log (LOG_TIMESTAMP) << "Current Directory: " << local_dir << endLog;
157 // Initialize common controls
158 INITCOMMONCONTROLSEX icce = { sizeof (INITCOMMONCONTROLSEX),
159 ICC_WIN95_CLASSES | ICC_LISTVIEW_CLASSES };
160 InitCommonControlsEx (&icce);
162 // Initialize COM and ShellLink instance here. For some reason
163 // Windows 7 fails to create the ShellLink instance if this is
164 // done later, in the thread which actually creates the shortcuts.
165 extern IShellLink *sl;
166 CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
167 HRESULT res = CoCreateInstance (CLSID_ShellLink, NULL,
168 CLSCTX_INPROC_SERVER, IID_IShellLink,
169 (LPVOID *) & sl);
170 if (res)
172 mbox (NULL, IDS_SHELLLINK_FAILED, MB_OK, res);
175 // Init window class lib
176 Window::SetAppInstance (hinstance);
178 // Create pages
179 Splash.Create ();
180 AntiVirus.Create ();
181 Source.Create ();
182 Root.Create ();
183 LocalDir.Create ();
184 Net.Create ();
185 Site.Create ();
186 Chooser.Create ();
187 Prereq.Create ();
188 Confirm.Create ();
189 Progress.Create ();
190 PostInstallResults.Create ();
191 Desktop.Create ();
193 // Add pages to sheet
194 MainWindow.AddPage (&Splash);
195 MainWindow.AddPage (&AntiVirus);
196 MainWindow.AddPage (&Source);
197 MainWindow.AddPage (&Root);
198 MainWindow.AddPage (&LocalDir);
199 MainWindow.AddPage (&Net);
200 MainWindow.AddPage (&Site);
201 MainWindow.AddPage (&Chooser);
202 MainWindow.AddPage (&Prereq);
203 MainWindow.AddPage (&Confirm);
204 MainWindow.AddPage (&Progress);
205 MainWindow.AddPage (&PostInstallResults);
206 MainWindow.AddPage (&Desktop);
208 // Create the PropSheet main window
209 MainWindow.Create ();
211 // Uninitalize COM
212 if (sl)
213 sl->Release ();
214 CoUninitialize ();
217 int WINAPI
218 WinMain (HINSTANCE h,
219 HINSTANCE hPrevInstance, LPSTR command_line, int cmd_show)
222 hinstance = h;
224 // Make sure the C runtime functions use the same codepage as the GUI
225 char locale[12];
226 snprintf(locale, sizeof locale, ".%u", GetACP());
227 setlocale(LC_ALL, locale);
229 char **_argv;
230 int argc;
231 for (argc = 0, _argv = __argv; *_argv; _argv++)
232 ++argc;
233 _argv = __argv;
235 try {
236 bool help_option = false;
237 bool invalid_option = false;
238 char cwd[MAX_PATH];
239 GetCurrentDirectory (MAX_PATH, cwd);
240 local_dir = std::string (cwd);
242 if (!GetOption::GetInstance ().Process (argc,_argv, NULL))
243 help_option = invalid_option = true;
244 else if (HelpOption)
245 help_option = true;
247 if (!((std::string) Arch).size ())
249 #ifdef __x86_64__
250 is_64bit = true;
251 #else
252 is_64bit = false;
253 #endif
255 else if (((std::string) Arch).find ("64") != std::string::npos)
256 is_64bit = true;
257 else if (((std::string) Arch).find ("32") != std::string::npos
258 || ((std::string) Arch).find ("x86") != std::string::npos)
259 is_64bit = false;
260 else
262 char buff[80 + ((std::string) Arch).size ()];
263 sprintf (buff, "Invalid option for --arch: \"%s\"",
264 ((std::string) Arch).c_str ());
265 fprintf (stderr, "*** %s\n", buff);
266 exit (1);
269 if (GuiLangOption.isPresent())
271 // If option's value isn't numeric, perhaps we should try to interpret
272 // it as a locale name?
273 int rc = sscanf(((std::string)GuiLangOption).c_str(), "%hx", &langid);
274 if (rc > 0)
275 SetThreadUILanguage(langid);
278 unattended_mode = PackageManagerOption ? chooseronly
279 : (UnattendedOption ? unattended : attended);
281 bool output_only = help_option || VersionOption;
283 SetupBaseName = SetupBaseNameOpt;
284 SetupArch = is_64bit ? "x86_64" : "x86";
285 SetupIniDir = SetupArch+"/";
287 /* Initialize well known SIDs. We need the admin SID to test if we're
288 supposed to elevate. */
289 nt_sec.initialiseWellKnownSIDs ();
290 /* Check if we have to elevate. */
291 bool elevate = !output_only && OSMajorVersion () >= 6
292 && !NoAdminOption && !nt_sec.isRunAsAdmin ();
293 std::string elevate_extra_args;
295 if (unattended_mode || output_only || !elevate)
296 set_cout ();
298 /* Start logging only if we don't elevate. Same for setting default
299 security settings. */
300 LogSingleton::SetInstance (*LogFile::createLogFile ());
301 const char *sep = isdirsep (local_dir[local_dir.size () - 1])
302 ? "" : "\\";
303 /* Don't create log files for help or version output only. */
304 if (!elevate && !output_only)
306 Logger ().setFile (LOG_BABBLE, local_dir + sep + "setup.log.full",
307 false);
308 Logger ().setFile (0, local_dir + sep + "setup.log", true);
309 Log (LOG_PLAIN) << "Starting cygwin install, version "
310 << setup_version << endLog;
313 /* Some confusion of interfaces here: Normally we try to write un-localized
314 strings to the log. However, if output_only is true, then we know that
315 Log() is only outputting to console, not a logfile, so using localized
316 text is ok. */
317 if (help_option)
319 if (invalid_option)
320 Log (LOG_PLAIN) << "\n" << LoadStringUtf8(IDS_HELPTEXT_ERROR) << "\n" << endLog;
321 Log (LOG_PLAIN) << "Cygwin setup " << setup_version << endLog;
322 Log (LOG_PLAIN) << "\n" << LoadStringUtf8(IDS_HELPTEXT_HEADER) << "\n" << endLog;
323 GetOption::GetInstance ().ParameterUsage (Log (LOG_PLAIN), LoadStringUtf8);
324 Log (LOG_PLAIN) << endLog;
325 Log (LOG_PLAIN) << LoadStringUtf8(IDS_HELPTEXT_FOOTER) << endLog;
326 Logger ().exit (invalid_option ? 1 : 0, false);
327 goto finish_up;
330 if (VersionOption)
332 Log (LOG_PLAIN) << "Cygwin setup " << setup_version << endLog;
333 Logger ().exit (0, false);
334 goto finish_up;
337 /* Check if Cygwin works on this Windows architecture/version */
338 if (!UnsupportedOption)
341 #ifdef _X86_
342 (TRUE)
343 #else
344 (!is_64bit)
345 #endif
347 mbox (NULL, IDS_UNSUPPORTED_WINDOWS_ARCH,
348 MB_ICONEXCLAMATION | MB_OK);
349 Logger ().exit (1, false);
351 else if ((OSMajorVersion () < 6) ||
352 ((OSMajorVersion () == 6) && (OSMinorVersion() < 1)))
354 mbox (NULL, IDS_UNSUPPORTED_WINDOWS_VERSION,
355 MB_ICONEXCLAMATION | MB_OK);
356 Logger ().exit (1, false);
360 Warn if Windows version is deprecated for Cygwin: Current plans are to
361 deprecate Windows 7 & 8 sometime during the Cygwin DLL 3.4 lifetime
363 if (!DeprecatedOption && !elevate)
365 if ((OSMajorVersion () == 6) && (OSMinorVersion() < 3))
366 mbox (NULL, IDS_DEPRECATED_WINDOWS_VERSION,
367 MB_ICONEXCLAMATION | MB_OK | MB_DSA_CHECKBOX);
371 /* Set default DACL and Group. */
372 nt_sec.setDefaultSecurity ();
375 If --symlink-type option isn't given, look for winsymlinks in CYGWIN
376 env var for a default
378 Since the current environment doesn't get passed to the process started
379 with with ShellExecuteEx, we need to convert the env var into an option
380 for that elevated instance.
382 if (!SymlinkTypeOption.isPresent()) {
383 std::string cygwin;
384 DWORD len = GetEnvironmentVariable ("CYGWIN", &cygwin[0], 0);
385 cygwin.resize(len);
386 GetEnvironmentVariable ("CYGWIN", &cygwin[0], len);
388 if (cygwin.find("winsymlinks:native") != std::string::npos)
390 symlinkType = SymlinkTypeNative;
391 elevate_extra_args.append("--symlink-type native");
393 else if (cygwin.find("winsymlinks:wsl") != std::string::npos)
395 symlinkType = SymlinkTypeWsl;
396 elevate_extra_args.append("--symlink-type wsl");
398 else if (cygwin.find("winsymlinks:sys") != std::string::npos)
400 symlinkType = SymlinkTypeMagic;
401 elevate_extra_args.append("--symlink-type sys");
403 else if (cygwin.find("winsymlinks:lnk") != std::string::npos)
405 // Ignore CYGWIN=winsymlinks:lnk, as '--symlink-type lnk' is not implemented
406 // symlinkType = SymlinkTypeShortcut;
407 // elevate_extra_args.append("--symlink-type lnk");
410 else
412 symlinkType = (SymlinkTypeEnum)(int)SymlinkTypeOption;
415 if (symlinkType == SymlinkTypeWsl)
417 VersionInfo v = GetVer();
418 if ((v.major() < 10) ||
419 ((v.major() == 10) && (v.buildNumber() < 14393)))
421 fprintf (stderr, "*** --symlink-type wsl requires Windows 10 1607 or later\n");
422 exit(1);
425 else if (symlinkType == SymlinkTypeNative)
427 VersionInfo v = GetVer();
428 if (v.major() < 6)
430 fprintf (stderr, "*** --symlink-type native requires Windows 6.0 or later\n");
431 exit(1);
434 if (!(elevate || is_developer_mode() || nt_sec.hasSymlinkCreationRights()))
436 fprintf (stderr, "*** --symlink-type native requires SeCreateSymbolicLink privilege or 'Developer Mode'\n");
437 exit(1);
440 else if (symlinkType == SymlinkTypeShortcut)
442 fprintf (stderr, "*** --symlink-type lnk is not implemented\n");
443 exit(1);
446 if (elevate)
448 char exe_path[MAX_PATH];
449 if (!GetModuleFileName(NULL, exe_path, ARRAYSIZE(exe_path)))
450 goto finish_up;
452 SHELLEXECUTEINFO sei = { sizeof(sei) };
453 sei.lpVerb = "runas";
454 sei.lpFile = exe_path;
455 sei.nShow = SW_NORMAL;
456 if (WaitOption)
457 sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
459 // Avoid another isRunAsAdmin check in the child.
460 std::string command_line_cs (command_line);
461 command_line_cs += " -";
462 command_line_cs += NoAdminOption.shortOption();
463 command_line_cs += " ";
464 command_line_cs += elevate_extra_args;
465 sei.lpParameters = command_line_cs.c_str ();
467 if (ShellExecuteEx(&sei))
469 DWORD exitcode = 0;
470 /* Wait until child process is finished. */
471 if (WaitOption && sei.hProcess != NULL)
472 if (!WaitForSingleObject (sei.hProcess, INFINITE))
473 GetExitCodeProcess (sei.hProcess, &exitcode);
474 Logger ().setExitMsg (IDS_ELEVATED);
475 Logger ().exit (exitcode, false);
477 Log (LOG_PLAIN) << "Starting elevated child process failed" << endLog;
478 Logger ().exit (1, false);
480 else
482 UserSettings Settings;
483 UserSettings::instance().load (local_dir);
484 main_display ();
485 Settings.save (); // Clean exit.. save user options.
486 if (rebootneeded)
487 Logger ().setExitMsg (IDS_REBOOT_REQUIRED);
488 Logger ().exit (rebootneeded ? IDS_REBOOT_REQUIRED : 0);
490 finish_up:
493 TOPLEVEL_CATCH(NULL, "main");
495 // Never reached
496 return 0;