mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-10-31 10:37:00 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			240 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * portable.cpp
 | |
|  * Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>
 | |
|  *
 | |
|  * Distributed under terms of the GPL3 license.
 | |
|  */
 | |
| 
 | |
| #ifndef UNICODE
 | |
| #define UNICODE
 | |
| #endif
 | |
| 
 | |
| #ifndef _UNICODE
 | |
| #define _UNICODE
 | |
| #endif
 | |
| 
 | |
| #include <Windows.h>
 | |
| #include <tchar.h>
 | |
| #include <wchar.h>
 | |
| #include <stdio.h>
 | |
| #include <string>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #define BUFSIZE 4096
 | |
| 
 | |
| 
 | |
| // error handling {{{
 | |
| static void
 | |
| show_error(LPCWSTR msg) {
 | |
|     MessageBeep(MB_ICONERROR);
 | |
|     MessageBoxW(NULL, msg, L"Error", MB_OK|MB_ICONERROR);
 | |
| }
 | |
| 
 | |
| static void
 | |
| show_detailed_error(LPCWSTR preamble, LPCWSTR msg, int code) {
 | |
|     LPTSTR buf;
 | |
|     buf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*
 | |
|             (_tcslen(msg) + _tcslen(preamble) + 80));
 | |
| 
 | |
|     _snwprintf_s(buf,
 | |
|         LocalSize(buf) / sizeof(TCHAR), _TRUNCATE,
 | |
|         L"%s\r\n  %s (Error Code: %d)\r\n",
 | |
|         preamble, msg, code);
 | |
| 
 | |
|     show_error(buf);
 | |
|     LocalFree(buf);
 | |
| }
 | |
| 
 | |
| static void
 | |
| show_last_error_crt(LPCWSTR preamble) {
 | |
|     TCHAR buf[BUFSIZE];
 | |
|     int err = 0;
 | |
| 
 | |
|     _get_errno(&err);
 | |
|     _tcserror_s(buf, BUFSIZE, err);
 | |
|     show_detailed_error(preamble, buf, err);
 | |
| }
 | |
| 
 | |
| static void
 | |
| show_last_error(LPCWSTR preamble) {
 | |
|     TCHAR *msg = NULL;
 | |
|     DWORD dw = GetLastError();
 | |
| 
 | |
|     FormatMessage(
 | |
|         FORMAT_MESSAGE_ALLOCATE_BUFFER |
 | |
|         FORMAT_MESSAGE_FROM_SYSTEM |
 | |
|         FORMAT_MESSAGE_IGNORE_INSERTS,
 | |
|         NULL,
 | |
|         dw,
 | |
|         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | |
|         (LPTSTR)&msg,
 | |
|         0, NULL );
 | |
| 
 | |
|     show_detailed_error(preamble, msg, (int)dw);
 | |
| }
 | |
| 
 | |
| 
 | |
| // }}}
 | |
| 
 | |
| 
 | |
| 
 | |
| static bool
 | |
| get_app_dir(std::wstring& ans, std::wstring& exe_name) {
 | |
|     DWORD sz;
 | |
|     static wchar_t drive[_MAX_DRIVE] = {0};
 | |
|     static wchar_t buf[BUFSIZE] = {0}, dirpath[_MAX_DIR] = {0}, fname[_MAX_FNAME] = {0}, ext[_MAX_EXT] = {0};
 | |
| 
 | |
|     sz = GetModuleFileName(NULL, buf, BUFSIZE);
 | |
| 
 | |
|     if (sz == 0 || sz > BUFSIZE-1) {
 | |
|         show_error(L"Failed to get path to portable launcher");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     errno_t err = _wsplitpath_s(buf, drive, _MAX_DRIVE, dirpath, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
 | |
| 
 | |
|     if (err != 0) {
 | |
|         show_last_error_crt(L"Failed to split path to portable launcher");
 | |
|         return false;
 | |
|     }
 | |
|     ans.append(drive); ans.append(dirpath);
 | |
|     if (ans.length() > 58) {
 | |
|         std::wstring msg;
 | |
|         msg.append(L"Path to Calibre Portable (");
 | |
|         msg.append(ans);
 | |
|         msg.append(L") too long. It must be less than 59 characters.");
 | |
|         show_error(msg.c_str());
 | |
|         return false;
 | |
| 	}
 | |
|     exe_name.append(fname);
 | |
|     exe_name.erase(exe_name.length() - sizeof("portable"), sizeof("portable"));
 | |
|     exe_name.append(ext);
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| quote_argv(const std::wstring& arg, std::wstring& cmd_line) {
 | |
|     if (!arg.empty() && arg.find_first_of(L" \t\n\v\"") == arg.npos) {
 | |
|         cmd_line.append(arg);
 | |
|         return;
 | |
|     }
 | |
|     cmd_line.push_back(L'"');
 | |
| 
 | |
|     for (auto iterator = arg.begin() ; ; ++iterator) {
 | |
|         unsigned num_back_slashes = 0;
 | |
| 
 | |
|         while (iterator != arg.end() && *iterator == L'\\') {
 | |
|             ++iterator;
 | |
|             ++num_back_slashes;
 | |
|         }
 | |
| 
 | |
|         if (iterator == arg.end()) {
 | |
| 
 | |
|             //
 | |
|             // Escape all backslashes, but let the terminating
 | |
|             // double quotation mark we add below be interpreted
 | |
|             // as a metacharacter.
 | |
|             //
 | |
| 
 | |
|             cmd_line.append (num_back_slashes * 2, L'\\');
 | |
|             break;
 | |
|         }
 | |
|         else if (*iterator == L'"') {
 | |
| 
 | |
|             //
 | |
|             // Escape all backslashes and the following
 | |
|             // double quotation mark.
 | |
|             //
 | |
| 
 | |
|             cmd_line.append (num_back_slashes * 2 + 1, L'\\');
 | |
|             cmd_line.push_back (*iterator);
 | |
|         }
 | |
|         else {
 | |
| 
 | |
|             //
 | |
|             // Backslashes aren't special here.
 | |
|             //
 | |
| 
 | |
|             cmd_line.append (num_back_slashes, L'\\');
 | |
|             cmd_line.push_back (*iterator);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     cmd_line.push_back (L'"');
 | |
| }
 | |
| 
 | |
| static int
 | |
| launch_exe(LPCWSTR exe_path, const std::wstring &cmd_line, LPCWSTR config_dir) {
 | |
|     DWORD dwFlags=0;
 | |
|     STARTUPINFO si;
 | |
|     PROCESS_INFORMATION pi;
 | |
|     if (cmd_line.length() > BUFSIZE - 4) {
 | |
|         show_error(L"Path to executable in portable folder too long.");
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (!SetEnvironmentVariableW(L"CALIBRE_CONFIG_DIRECTORY", config_dir)) {
 | |
|         show_last_error(L"Failed to set environment variables");
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (!SetEnvironmentVariableW(L"CALIBRE_PORTABLE_BUILD", exe_path)) {
 | |
|         show_last_error(L"Failed to set environment variables");
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP;
 | |
| 
 | |
|     ZeroMemory( &si, sizeof(si) );
 | |
|     si.cb = sizeof(si);
 | |
|     ZeroMemory( &pi, sizeof(pi) );
 | |
|     static wchar_t mutable_cmdline[BUFSIZE] = {0};
 | |
|     cmd_line.copy(mutable_cmdline, BUFSIZE-1);
 | |
| 
 | |
|     if (!CreateProcess(NULL, mutable_cmdline,
 | |
|         NULL,           // Process handle not inheritable
 | |
|         NULL,           // Thread handle not inheritable
 | |
|         FALSE,          // Set handle inheritance to FALSE
 | |
|         dwFlags,        // Creation flags http://msdn.microsoft.com/en-us/library/ms684863(v=vs.85).aspx
 | |
|         NULL,           // Use parent's environment block
 | |
|         NULL,           // Use parent's starting directory
 | |
|         &si,            // Pointer to STARTUPINFO structure
 | |
|         &pi             // Pointer to PROCESS_INFORMATION structure
 | |
|     )) {
 | |
|         std::wstring message(L"Failed to launch: ");
 | |
|         message.append(mutable_cmdline);
 | |
|         show_last_error(message.c_str());
 | |
|     }
 | |
| 
 | |
|     // Close process and thread handles.
 | |
|     CloseHandle( pi.hProcess );
 | |
|     CloseHandle( pi.hThread );
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int WINAPI
 | |
| wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR orig_cmd_line, int nCmdShow) {
 | |
|     std::wstring exe, config_dir, cmd_line, application_dir, exe_name;
 | |
| 
 | |
|     if (!get_app_dir(application_dir, exe_name)) return 1;
 | |
|     config_dir.append(application_dir); config_dir.append(L"Calibre Settings");
 | |
|     exe.append(application_dir); exe.append(L"Calibre\\"); exe.append(exe_name);
 | |
| 
 | |
|     int argc;
 | |
|     wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc);
 | |
|     if (argv == NULL) {
 | |
|         show_last_error(L"Failed to convert cmdline to argv array");
 | |
|         return 1;
 | |
|     }
 | |
|     quote_argv(exe, cmd_line);
 | |
|     for (int i = 1; i < argc; i++) {
 | |
|         std::wstring arg(argv[i]);
 | |
|         cmd_line.push_back(L' ');
 | |
|         quote_argv(arg, cmd_line);
 | |
|     }
 | |
|     LocalFree(argv);
 | |
|     return launch_exe(exe.c_str(), cmd_line.c_str(), config_dir.c_str());
 | |
| }
 |