├── .gitignore ├── CMake └── VersionParser.cmake ├── CMakeLists.txt ├── LICENSE ├── README.md ├── Screenshot-001.png ├── Screenshot-002.png ├── launcher.c ├── launcher.h ├── launcher.rc.in ├── launcher_config.h.in ├── launcher_linux.c ├── launcher_linux_main.c ├── launcher_win.c ├── launcher_win_main.c └── whereami ├── LICENSE.MIT ├── whereami.c └── whereami.h /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | cmake-build-*/ 3 | build 4 | *.ico -------------------------------------------------------------------------------- /CMake/VersionParser.cmake: -------------------------------------------------------------------------------- 1 | function(parse_version_number VERSION_NAME) 2 | if (${VERSION_NAME} MATCHES "^[vV]") 3 | string(SUBSTRING ${${VERSION_NAME}} 1 -1 _VERSION_VALUE) 4 | else () 5 | set(_VERSION_VALUE ${${VERSION_NAME}}) 6 | endif () 7 | 8 | string(REPLACE "." ";" _VERSION_LIST ${_VERSION_VALUE}) 9 | list(LENGTH _VERSION_LIST _VERSION_LIST_LENGTH) 10 | 11 | if (${_VERSION_LIST_LENGTH} GREATER_EQUAL 1) 12 | list(GET _VERSION_LIST 0 _TEMP) 13 | else () 14 | set(_TEMP 0) 15 | endif () 16 | set("${VERSION_NAME}_MAJOR" ${_TEMP} PARENT_SCOPE) 17 | 18 | if (${_VERSION_LIST_LENGTH} GREATER_EQUAL 2) 19 | list(GET _VERSION_LIST 1 _TEMP) 20 | else () 21 | set(_TEMP 0) 22 | endif () 23 | set("${VERSION_NAME}_MINOR" ${_TEMP} PARENT_SCOPE) 24 | 25 | if (${_VERSION_LIST_LENGTH} GREATER_EQUAL 3) 26 | list(GET _VERSION_LIST 2 _TEMP) 27 | else () 28 | set(_TEMP 0) 29 | endif () 30 | set("${VERSION_NAME}_PATCH" ${_TEMP} PARENT_SCOPE) 31 | 32 | if (${_VERSION_LIST_LENGTH} GREATER_EQUAL 4) 33 | list(GET _VERSION_LIST 3 _TEMP) 34 | else () 35 | set(_TEMP 0) 36 | endif () 37 | set("${VERSION_NAME}_TWEAK" ${_TEMP} PARENT_SCOPE) 38 | endfunction() -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | if (NOT DEFINED APP_NAME) 4 | message(FATAL_ERROR "Application name has not been defined") 5 | endif () 6 | 7 | if (NOT DEFINED APP_EXE_NAME) 8 | set(APP_EXE_NAME ${APP_NAME}) 9 | endif () 10 | 11 | if (NOT DEFINED APP_VERSION) 12 | message(FATAL_ERROR "Application version number has not been defined") 13 | endif () 14 | 15 | if (NOT DEFINED APP_ORG) 16 | message(FATAL_ERROR "Application organization has not been defined") 17 | endif () 18 | 19 | if (NOT DEFINED APP_JVM_TYPE) 20 | set(APP_JVM_TYPE "server") 21 | endif () 22 | 23 | if (DEFINED APP_MAIN_CLASS) 24 | string(REPLACE "." "/" APP_MAIN_CLASS_NAME ${APP_MAIN_CLASS}) 25 | elseif () 26 | message(FATAL_ERROR "Application main class has not been defined") 27 | endif () 28 | 29 | if (DEFINED APP_PREDEF_OPTIONS) 30 | string(REPLACE "\\" "\\\\" APP_MAIN_CLASS_NAME ${APP_MAIN_CLASS}) 31 | string(REPLACE "\"" "\\\"" APP_MAIN_CLASS_NAME ${APP_MAIN_CLASS}) 32 | string(REPLACE ";;;" "\\0" APP_MAIN_CLASS_NAME ${APP_MAIN_CLASS}) 33 | endif () 34 | 35 | if (DEFINED APP_ICON) 36 | get_filename_component(_APP_ICON_PATH ${APP_ICON} ABSOLUTE) 37 | string(REPLACE "\\" "\\\\" APP_ICON_PATH ${_APP_ICON_PATH}) 38 | endif () 39 | 40 | if (NOT DEFINED APP_JRE_PATH) 41 | set(APP_JRE_PATH "..") 42 | else () 43 | if (APP_JRE_PATH MATCHES "(.*)\\$") 44 | set(APP_JRE_PATH ${CMAKE_MATCH_1}) 45 | endif () 46 | string(REPLACE "\\" "\\\\" APP_JRE_PATH ${APP_JRE_PATH}) 47 | string(REPLACE "\"" "\\\"" APP_JRE_PATH ${APP_JRE_PATH}) 48 | endif () 49 | 50 | if (NOT DEFINED APP_COPYRIGHT AND DEFINED APP_COPYRIGHT_OWNER) 51 | set(APP_COPYRIGHT "Copyright 2020 ${APP_COPYRIGHT_OWNER}. All rights reserved.") 52 | endif () 53 | 54 | if (DEFINED APP_SHOW_CONSOLE) 55 | endif () 56 | 57 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake) 58 | 59 | project(launcher 60 | VERSION 2.2 61 | LANGUAGES C) 62 | 63 | include(VersionParser) 64 | parse_version_number(APP_VERSION) 65 | 66 | set(CMAKE_C_STANDARD 99) 67 | 68 | find_package(JNI REQUIRED) 69 | include_directories(${JNI_INCLUDE_DIRS}) 70 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 71 | 72 | configure_file( 73 | launcher_config.h.in 74 | ${CMAKE_CURRENT_BINARY_DIR}/launcher_config.h 75 | @ONLY 76 | ) 77 | 78 | if (WIN32 AND MSVC) 79 | configure_file( 80 | launcher.rc.in 81 | ${CMAKE_CURRENT_BINARY_DIR}/launcher.rc 82 | @ONLY 83 | ) 84 | add_executable(${APP_EXE_NAME} launcher_win_main.c ${CMAKE_CURRENT_BINARY_DIR}/launcher.rc ${CMAKE_CURRENT_BINARY_DIR}/launcher_config.h launcher.h launcher_win.c launcher.c) 85 | target_link_libraries(${APP_EXE_NAME} pathcch) 86 | if (NOT DEFINED APP_SHOW_CONSOLE) 87 | set_property(TARGET ${APP_EXE_NAME} PROPERTY WIN32_EXECUTABLE true) 88 | else () 89 | add_compile_definitions("APPLICATION_SHOW_CONSOLE") 90 | endif () 91 | elseif (UNIX) 92 | add_executable(${APP_EXE_NAME} 93 | ${CMAKE_CURRENT_BINARY_DIR}/launcher_config.h 94 | launcher.h launcher.c launcher_linux.c launcher_linux_main.c 95 | whereami/whereami.h whereami/whereami.c 96 | ) 97 | target_link_libraries(${APP_EXE_NAME} dl) 98 | else () 99 | message(FATAL_ERROR "Unsupported platform") 100 | endif () 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Glavo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java GUI launcher 2 | 3 | A lightweight Java GUI application launcher, you can easily customize the launcher icon, name, and file details properties. 4 | 5 | The purpose of this project is to work with jre generated by [jlink](https://docs.oracle.com/en/java/javase/11/tools/jlink.html). 6 | This launcher starts the JVM using the JNI invocation api, does not depend on java.exe / javaw.exe, you can exclude them when using jlink by adding the `--strip-native-commands` option. 7 | 8 | Launching applications with this launcher can avoid opening a console, and the task manager shows the application name instead of the OpenJDK process name (i.e. *OpenJDK Platform binary*). 9 | 10 | ![a launcher file details properties](Screenshot-001.png) 11 | 12 | ![Task Manager](Screenshot-002.png) 13 | 14 | ## Build your launcher 15 | 16 | Before building the launcher, please make sure that CMake is installed on your computer, 17 | and installed the necessary toolchains to build. 18 | 19 | Build your launcher in the console (replace `` with where you want to build the launcher, 20 | replace `` with the source directory of the launcher, replace `` with your [build options](#Build Options): 21 | 22 | ```batch 23 | cd 24 | cmake 25 | cmake --build . 26 | ``` 27 | 28 | Example: 29 | 30 | ``` 31 | cd java-gui-launcher 32 | cmake -B build -DCMAKE_BUILD_TYPE=Release -DAPP_NAME=ClassViewer -DAPP_VERSION=v3 -DAPP_ICON=.\ClassViewer.ico -DAPP_ORG=org.glavo -DAPP_MAIN_CLASS=org.glavo.viewer.Main -DAPP_JRE_PATH=.\jre -DAPP_JVM_TYPE=server .. 33 | cmake --build build 34 | ``` 35 | 36 | ## Build Options 37 | 38 | You can customize the generated launcher by passing build options. 39 | 40 | | Option | Description | 41 | | -------- | ------------ | 42 | | -DAPP_NAME=\ | application name | 43 | | -DAPP_VERSION=[v]\[.\[.\[.\]]] | (Windows Only) application version number| 44 | | -DAPP_ORG=\ | (Windows Only) company/organization name | 45 | | -DAPP_ICON=\ | *(Optional, Windows Only)* application icon | 46 | | -DAPP_JVM_TYPE=\ | JVM type. Default is `server` | 47 | | -DAPP_MAIN_CLASS=\
| full name of the main class, without the module name | 48 | | -DAPP_JRE_PATH=\ | JRE path. The path can be relative to the launcher, or it can be an absolute path. Default is `..` (put launcher into the `bin` directory of JRE) | 49 | | -DAPP_COPYRIGHT=\ | (Windows Only) application copyright notice | 50 | | -DAPP_PREDEF_OPTIONS=\ | predefined VM options, separate multiple options with `;;;`. See [JNI_CreateJavaVM](https://docs.oracle.com/en/java/javase/11/docs/specs/jni/invocation.html#jni_createjavavm), *Standard Options* table | 51 | 52 | ## Runtime Options 53 | The launcher accepts arguments starting with `-J` and passes the rest of the content to JVM 54 | 55 | Example: 56 | ``` 57 | .\ClassViewer.exe -J-Xms128m -J-Xmx128m 58 | ``` 59 | -------------------------------------------------------------------------------- /Screenshot-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassViewer/java-gui-launcher/dcd9af6b1a2d5b2d2ca9eb40de9693cf618f4f2c/Screenshot-001.png -------------------------------------------------------------------------------- /Screenshot-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClassViewer/java-gui-launcher/dcd9af6b1a2d5b2d2ca9eb40de9693cf618f4f2c/Screenshot-002.png -------------------------------------------------------------------------------- /launcher.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Glavo on 2020.1.8. 3 | // 4 | 5 | #include "launcher.h" 6 | 7 | JavaVM *vm = NULL; 8 | JNIEnv *env = NULL; 9 | 10 | CMD cmd; 11 | 12 | LIBRARY_HANDLE libjvm = NULL; 13 | -------------------------------------------------------------------------------- /launcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Glavo on 2019.12.24. 3 | // 4 | 5 | #ifndef LAUNCHER_LAUNCHER_H 6 | #define LAUNCHER_LAUNCHER_H 7 | 8 | #include 9 | #include 10 | 11 | 12 | #ifdef WIN32 13 | 14 | #include 15 | 16 | typedef HMODULE LIBRARY_HANDLE; 17 | #else // WIN32 18 | typedef void *LIBRARY_HANDLE; 19 | #endif 20 | 21 | typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **penv, void *args); 22 | 23 | extern JavaVM *vm; 24 | extern JNIEnv *env; 25 | 26 | typedef struct _CMD { 27 | int nOptions; 28 | JavaVMOption *options; 29 | int nArguments; 30 | #ifdef WIN32 31 | LPWSTR *arguments; 32 | #else 33 | const char **arguments; 34 | #endif 35 | } CMD; 36 | 37 | extern CMD cmd; 38 | 39 | #ifdef WIN32 40 | extern void parseCMD(void); 41 | #else // WIN32 42 | extern void parseCMD(int argc, char *argv[]); 43 | #endif 44 | 45 | extern void destructCMD(void); 46 | 47 | static inline jclass findClass(const char *name) { 48 | return (*env)->FindClass(env, name); 49 | } 50 | 51 | extern LIBRARY_HANDLE libjvm; 52 | 53 | extern void loadJVM(void); 54 | 55 | extern jint createJVM(void); 56 | 57 | extern jobjectArray javaArguments(void); 58 | 59 | #endif //LAUNCHER_LAUNCHER_H 60 | -------------------------------------------------------------------------------- /launcher.rc.in: -------------------------------------------------------------------------------- 1 | #cmakedefine APP_ICON_PATH "@APP_ICON_PATH@" 2 | #cmakedefine APP_COPYRIGHT "@APP_COPYRIGHT@" 3 | 4 | #ifdef APP_ICON_PATH 5 | IDI_ICON1 ICON APP_ICON_PATH 6 | #endif 7 | 8 | 1 VERSIONINFO 9 | FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,0,0 10 | PRODUCTVERSION @APP_VERSION_MAJOR@,@APP_VERSION_MINOR@,@APP_VERSION_PATCH@,@APP_VERSION_TWEAK@ 11 | FILEOS 0x4 12 | FILETYPE 0x1 13 | { 14 | BLOCK "StringFileInfo" 15 | { 16 | BLOCK "040904E4" 17 | { 18 | VALUE "CompanyName", "@APP_ORG@" 19 | VALUE "FileDescription", "@APP_NAME@" 20 | VALUE "FileVersion", "@PROJECT_VERSION@" 21 | VALUE "InternalName", "Launcher" 22 | #ifdef APP_COPYRIGHT 23 | VALUE "LegalCopyright", APP_COPYRIGHT 24 | #endif 25 | VALUE "OriginalFilename", "@APP_EXE_NAME@.exe" 26 | VALUE "ProductName", "@APP_NAME@" 27 | VALUE "ProductVersion", "@APP_VERSION@" 28 | } 29 | } 30 | BLOCK "VarFileInfo" 31 | { 32 | VALUE "Translation", 0x0409 0x04E4 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /launcher_config.h.in: -------------------------------------------------------------------------------- 1 | #define APPLICATION_NAME "@APP_NAME@" 2 | #define APPLICATION_VERSION "@APP_VERSION@" 3 | #define APPLICATION_ORGANIZATION "@APP_ORG@" 4 | #define APPLICATION_MAIN_CLASS "@APP_MAIN_CLASS_NAME@" 5 | #define APPLICATION_PREDEF_OPTIONS "@APPLICATION_PREDEF_OPTIONS@" 6 | #define APPLICATION_JVM_TYPE "@APP_JVM_TYPE@" 7 | #define APPLICATION_JRE_PATH "@APP_JRE_PATH@" -------------------------------------------------------------------------------- /launcher_linux.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Glavo on 2020.01.10. 3 | // 4 | 5 | #define _POSIX_C_SOURCE 1 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "whereami/whereami.h" 13 | #include "launcher.h" 14 | 15 | const char APPLICATION_PREDEF_VM_OPTIONS[] = APPLICATION_PREDEF_OPTIONS; 16 | 17 | void parseCMD(int argc, char *argv[]) { 18 | int preOptionsSize = 0; 19 | for (int i = 0; i < sizeof(APPLICATION_PREDEF_VM_OPTIONS) / sizeof(char); ++i) { 20 | if (APPLICATION_PREDEF_VM_OPTIONS[i] == '\0') { 21 | ++preOptionsSize; 22 | } 23 | } 24 | 25 | int nOptions = 0; 26 | JavaVMOption *options = malloc((argc - 1 + preOptionsSize) * sizeof(JavaVMOption)); 27 | int nArguments = 0; 28 | const char **arguments = malloc((argc - 1) * sizeof(char *)); 29 | 30 | const char *p = APPLICATION_PREDEF_VM_OPTIONS; 31 | while (p < APPLICATION_PREDEF_VM_OPTIONS + sizeof(APPLICATION_PREDEF_VM_OPTIONS)) { 32 | size_t len = strlen(p); 33 | if (len == 0) { 34 | ++p; 35 | continue; 36 | } 37 | options[nOptions].optionString = (char *) p; 38 | options[nOptions].extraInfo = NULL; 39 | ++nOptions; 40 | p += len + 1; 41 | } 42 | 43 | for (int i = 1; i < argc; ++i) { 44 | char *str = argv[i]; 45 | int strLen = (int) strlen(str); 46 | if (strLen > 2 && str[0] == L'-' && str[1] == L'J') { 47 | str += 2; 48 | options[nOptions].optionString = str; 49 | options[nOptions].extraInfo = NULL; 50 | ++nOptions; 51 | } else { 52 | arguments[nArguments++] = str; 53 | } 54 | } 55 | 56 | cmd.nOptions = nOptions; 57 | if (nOptions == 0) { 58 | free(options); 59 | cmd.options = NULL; 60 | } else { 61 | cmd.options = options; 62 | } 63 | 64 | cmd.nArguments = nArguments; 65 | if (nArguments == 0) { 66 | free(arguments); 67 | cmd.arguments = NULL; 68 | } else { 69 | cmd.arguments = arguments; 70 | } 71 | } 72 | 73 | void destructCMD(void) { 74 | 75 | } 76 | 77 | void loadJVM() { 78 | int pathLen; 79 | char jrePath[PATH_MAX + 1] = {0}; 80 | if (APPLICATION_JRE_PATH[0] == '/') { 81 | strcpy(jrePath, APPLICATION_JRE_PATH); 82 | pathLen = strlen(APPLICATION_JRE_PATH); 83 | } else if (wai_getExecutablePath(jrePath, PATH_MAX + 1, &pathLen) != -1) { 84 | jrePath[pathLen] = '/'; 85 | strcpy(jrePath + pathLen + 1, APPLICATION_JRE_PATH); 86 | pathLen = (int) strlen(jrePath); 87 | } else { 88 | fprintf(stderr, "Failed to get file path\n"); 89 | return; 90 | } 91 | 92 | if (jrePath[pathLen - 1] == '/') { 93 | --pathLen; 94 | } 95 | strcpy(jrePath + pathLen, "/lib/" APPLICATION_JVM_TYPE "/libjvm.so"); 96 | void *jvm = dlopen(jrePath, RTLD_NOW); 97 | const char *err = dlerror(); 98 | if (err) { 99 | fprintf(stderr, "Open libjvm.so failed: %s\n", err); 100 | return; 101 | } 102 | libjvm = jvm; 103 | } 104 | 105 | jint createJVM() { 106 | CreateJavaVM_t createJavaVM = (CreateJavaVM_t) dlsym(libjvm, "JNI_CreateJavaVM"); 107 | 108 | if (createJavaVM == NULL) { 109 | return JNI_ERR; 110 | } 111 | 112 | // options 113 | JavaVMInitArgs initArgs; 114 | initArgs.version = JNI_VERSION_1_6; 115 | initArgs.nOptions = cmd.nOptions; 116 | initArgs.options = cmd.options; 117 | 118 | return createJavaVM(&vm, (void **) &env, &initArgs); 119 | } 120 | 121 | jobjectArray javaArguments() { 122 | jclass stringCls = findClass("java/lang/String"); 123 | if (stringCls == NULL) { 124 | return NULL; 125 | } 126 | 127 | jmethodID vf = (*env)->GetMethodID(env, stringCls, "", "([B)V"); 128 | if (vf == NULL) { 129 | fprintf(stderr, "\n"); 130 | return NULL; 131 | } 132 | 133 | int nArguments = cmd.nArguments; 134 | const char **arguments = cmd.arguments; 135 | 136 | jobjectArray arr = (*env)->NewObjectArray(env, nArguments, stringCls, NULL); 137 | for (int i = 0; i < nArguments; ++i) { 138 | const char *arg = arguments[i]; 139 | size_t len = strlen(arg); 140 | 141 | jbyteArray argChars = (*env)->NewByteArray(env, len); 142 | (*env)->SetByteArrayRegion(env, argChars, 0, len, (const jbyte *) arg); 143 | 144 | jstring str = (*env)->NewObject(env, stringCls, vf, argChars); 145 | (*env)->SetObjectArrayElement(env, arr, i - 1, str); 146 | } 147 | return arr; 148 | } -------------------------------------------------------------------------------- /launcher_linux_main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Glavo on 2020.01.10. 3 | // 4 | #include 5 | #include "launcher.h" 6 | 7 | int main(int argc, char *argv[]) { 8 | loadJVM(); 9 | if (libjvm == NULL) { 10 | return EXIT_FAILURE; 11 | } 12 | 13 | // jvm methods 14 | jint ans = createJVM(); 15 | 16 | if (ans != JNI_OK) { 17 | fprintf(stderr, "Create JVM failed"); 18 | return ans; 19 | } 20 | 21 | jthrowable ex; 22 | 23 | jclass mainClass = findClass(APPLICATION_MAIN_CLASS); 24 | ex = (*env)->ExceptionOccurred(env); 25 | if (mainClass == NULL) { 26 | fprintf(stderr, "Main class '" APPLICATION_MAIN_CLASS"' not found"); 27 | return EXIT_FAILURE; 28 | } 29 | if (ex != NULL) { 30 | (*env)->ExceptionDescribe(env); 31 | return EXIT_FAILURE; 32 | } 33 | 34 | 35 | jmethodID mainMethodID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V"); 36 | ex = (*env)->ExceptionOccurred(env); 37 | if (mainMethodID == NULL) { 38 | fprintf(stderr, "Main method not found"); 39 | return EXIT_FAILURE; 40 | } 41 | if (ex != NULL) { 42 | (*env)->ExceptionDescribe(env); 43 | return EXIT_FAILURE; 44 | } 45 | 46 | parseCMD(argc, argv); 47 | jobjectArray args = javaArguments(); 48 | ex = (*env)->ExceptionOccurred(env); 49 | if (args == NULL) { 50 | fprintf(stderr, "Failed to initialization arguments"); 51 | return EXIT_FAILURE; 52 | } 53 | if (ex != NULL) { 54 | (*env)->ExceptionDescribe(env); 55 | return EXIT_FAILURE; 56 | } 57 | 58 | destructCMD(); 59 | 60 | ex = (*env)->ExceptionOccurred(env); 61 | (*env)->CallStaticVoidMethod(env, mainClass, mainMethodID, args); 62 | if (ex != NULL) { 63 | (*env)->ExceptionDescribe(env); 64 | return EXIT_FAILURE; 65 | } 66 | 67 | (*vm)->DestroyJavaVM(vm); 68 | return 0; 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /launcher_win.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Glavo on 2019.12.27. 3 | // 4 | 5 | #include "launcher.h" 6 | #include 7 | #include 8 | 9 | #define MAX_LONG_PATH_SIZE 32768 10 | 11 | const char APPLICATION_PREDEF_VM_OPTIONS[] = APPLICATION_PREDEF_OPTIONS; 12 | 13 | void parseCMD() { 14 | int argc; 15 | LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc); 16 | 17 | 18 | int preOptionsSize = 0; 19 | for (int i = 0; i < sizeof(APPLICATION_PREDEF_VM_OPTIONS) / sizeof(char); ++i) { 20 | if (APPLICATION_PREDEF_VM_OPTIONS[i] == '\0') { 21 | ++preOptionsSize; 22 | } 23 | } 24 | 25 | int nOptions = 0; 26 | JavaVMOption *options = malloc((argc - 1 + preOptionsSize) * sizeof(JavaVMOption)); 27 | int nArguments = 0; 28 | LPWSTR *arguments = malloc((argc - 1) * sizeof(LPSTR)); 29 | 30 | const char *p = APPLICATION_PREDEF_VM_OPTIONS; 31 | while (p < APPLICATION_PREDEF_VM_OPTIONS + sizeof(APPLICATION_PREDEF_VM_OPTIONS)) { 32 | size_t len = strlen(p); 33 | if (len == 0) { 34 | ++p; 35 | continue; 36 | } 37 | LPSTR newStr = malloc(len + 1); 38 | memcpy(newStr, p, len + 1); 39 | options[nOptions].optionString = newStr; 40 | options[nOptions].extraInfo = NULL; 41 | ++nOptions; 42 | p += len + 1; 43 | } 44 | 45 | for (int i = 1; i < argc; ++i) { 46 | LPWSTR str = argv[i]; 47 | int strLen = lstrlenW(str); 48 | if (strLen > 2 && str[0] == L'-' && str[1] == L'J') { 49 | str += 2; 50 | int len = WideCharToMultiByte( 51 | CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); 52 | LPSTR s = calloc(len, sizeof(char)); 53 | WideCharToMultiByte( 54 | CP_UTF8, 0, str, -1, s, len, NULL, NULL); 55 | options[nOptions].optionString = s; 56 | options[nOptions].extraInfo = NULL; 57 | ++nOptions; 58 | } else { 59 | size_t size = (strLen + 1) * sizeof(WCHAR); 60 | LPWSTR newStr = malloc(size); 61 | memcpy(newStr, str, size); 62 | arguments[nArguments++] = newStr; 63 | } 64 | } 65 | 66 | LocalFree(argv); 67 | 68 | cmd.nOptions = nOptions; 69 | if (nOptions == 0) { 70 | free(options); 71 | cmd.options = NULL; 72 | } else { 73 | cmd.options = options; 74 | } 75 | 76 | cmd.nArguments = nArguments; 77 | if (nArguments == 0) { 78 | free(arguments); 79 | cmd.arguments = NULL; 80 | } else { 81 | cmd.arguments = arguments; 82 | } 83 | } 84 | 85 | void destructCMD() { 86 | int nOptions = cmd.nOptions; 87 | JavaVMOption *options = cmd.options; 88 | int nArguments = cmd.nArguments; 89 | LPWSTR *arguments = cmd.arguments; 90 | 91 | cmd.nOptions = 0; 92 | cmd.options = NULL; 93 | cmd.nArguments = 0; 94 | cmd.arguments = NULL; 95 | 96 | for (int i = 0; i < nOptions; ++i) { 97 | free(options[i].optionString); 98 | } 99 | free(options); 100 | 101 | for (int i = 0; i < nArguments; ++i) { 102 | free(arguments[i]); 103 | } 104 | free(arguments); 105 | } 106 | 107 | void loadJVM() { 108 | WCHAR *buffer = calloc(MAX_LONG_PATH_SIZE, sizeof(WCHAR)); 109 | if (GetModuleFileNameW(NULL, buffer, MAX_LONG_PATH_SIZE)) { 110 | PathCchRemoveFileSpec(buffer, MAX_LONG_PATH_SIZE); 111 | PathCchAppendEx(buffer, MAX_LONG_PATH_SIZE, APPLICATION_JRE_PATH "\\bin\\" APPLICATION_JVM_TYPE L"\\jvm.dll", 112 | PATHCCH_ALLOW_LONG_PATHS); 113 | libjvm = LoadLibraryW(buffer); 114 | } 115 | free(buffer); 116 | } 117 | 118 | jint createJVM() { 119 | CreateJavaVM_t createJavaVM = (CreateJavaVM_t) GetProcAddress(libjvm, "JNI_CreateJavaVM"); 120 | 121 | if (createJavaVM == NULL) { 122 | return JNI_ERR; 123 | } 124 | 125 | // options 126 | JavaVMInitArgs initArgs; 127 | initArgs.version = JNI_VERSION_1_6; 128 | initArgs.nOptions = cmd.nOptions; 129 | initArgs.options = cmd.options; 130 | 131 | return createJavaVM(&vm, (void **) &env, &initArgs); 132 | } 133 | 134 | jobjectArray javaArguments() { 135 | jclass stringCls = findClass("java/lang/String"); 136 | if (stringCls == NULL) { 137 | return NULL; 138 | } 139 | 140 | jmethodID vf = (*env)->GetStaticMethodID(env, stringCls, "valueOf", "([C)Ljava/lang/String;"); 141 | if (vf == NULL) { 142 | return NULL; 143 | } 144 | 145 | int nArguments = cmd.nArguments; 146 | LPWSTR *arguments = cmd.arguments; 147 | 148 | jobjectArray arr = (*env)->NewObjectArray(env, nArguments, stringCls, NULL); 149 | for (int i = 0; i < nArguments; ++i) { 150 | LPWSTR arg = arguments[i]; 151 | size_t len = wcslen(arg); 152 | 153 | jcharArray argChars = (*env)->NewCharArray(env, (jsize) len); 154 | (*env)->SetCharArrayRegion(env, argChars, 0, len, (const jchar *) arg); 155 | 156 | jstring str = (*env)->CallStaticObjectMethod(env, stringCls, vf, argChars); 157 | (*env)->SetObjectArrayElement(env, arr, i - 1, str); 158 | } 159 | return arr; 160 | } -------------------------------------------------------------------------------- /launcher_win_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "launcher.h" 3 | 4 | #ifdef APPLICATION_SHOW_CONSOLE 5 | int main() { 6 | #else 7 | 8 | int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { 9 | #endif 10 | loadJVM(); 11 | if (libjvm == NULL) { 12 | MessageBoxA(NULL, "Not found " APPLICATION_JRE_PATH "\\bin\\" APPLICATION_JVM_TYPE "\\jvm.dll", NULL, MB_OK); 13 | return EXIT_FAILURE; 14 | } 15 | 16 | // jvm methods 17 | jint ans = createJVM(); 18 | 19 | if (ans != JNI_OK) { 20 | MessageBoxA(NULL, "Create JVM Failed", NULL, MB_OK); 21 | return ans; 22 | } 23 | 24 | jthrowable ex; 25 | 26 | jclass mainClass = findClass(APPLICATION_MAIN_CLASS); 27 | ex = (*env)->ExceptionOccurred(env); 28 | if (mainClass == NULL) { 29 | MessageBoxA(NULL, "Main class '" APPLICATION_MAIN_CLASS"' not found", NULL, MB_OK); 30 | return EXIT_FAILURE; 31 | } 32 | if (ex != NULL) { 33 | (*env)->ExceptionDescribe(env); 34 | return EXIT_FAILURE; 35 | } 36 | 37 | 38 | jmethodID mainMethodID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V"); 39 | ex = (*env)->ExceptionOccurred(env); 40 | if (mainMethodID == NULL) { 41 | MessageBoxA(NULL, "Main method not found", NULL, MB_OK); 42 | return EXIT_FAILURE; 43 | } 44 | if (ex != NULL) { 45 | (*env)->ExceptionDescribe(env); 46 | return EXIT_FAILURE; 47 | } 48 | 49 | parseCMD(); 50 | jobjectArray args = javaArguments(); 51 | ex = (*env)->ExceptionOccurred(env); 52 | if (args == NULL) { 53 | MessageBoxA(NULL, "Failed to initialization arguments", NULL, MB_OK); 54 | return EXIT_FAILURE; 55 | } 56 | if (ex != NULL) { 57 | (*env)->ExceptionDescribe(env); 58 | return EXIT_FAILURE; 59 | } 60 | 61 | destructCMD(); 62 | 63 | ex = (*env)->ExceptionOccurred(env); 64 | (*env)->CallStaticVoidMethod(env, mainClass, mainMethodID, args); 65 | if (ex != NULL) { 66 | (*env)->ExceptionDescribe(env); 67 | return EXIT_FAILURE; 68 | } 69 | 70 | (*vm)->DestroyJavaVM(vm); 71 | return 0; 72 | } 73 | 74 | 75 | -------------------------------------------------------------------------------- /whereami/LICENSE.MIT: -------------------------------------------------------------------------------- 1 | Copyright Gregory Pakosz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /whereami/whereami.c: -------------------------------------------------------------------------------- 1 | // (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses 2 | // without any warranty. 3 | // by Gregory Pakosz (@gpakosz) 4 | // https://github.com/gpakosz/whereami 5 | 6 | // in case you want to #include "whereami.c" in a larger compilation unit 7 | #if !defined(WHEREAMI_H) 8 | 9 | #include "whereami.h" 10 | 11 | #endif 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) 18 | 19 | #include 20 | 21 | #endif 22 | 23 | #if !defined(WAI_MALLOC) 24 | #define WAI_MALLOC(size) malloc(size) 25 | #endif 26 | 27 | #if !defined(WAI_FREE) 28 | #define WAI_FREE(p) free(p) 29 | #endif 30 | 31 | #if !defined(WAI_REALLOC) 32 | #define WAI_REALLOC(p, size) realloc(p, size) 33 | #endif 34 | 35 | #ifndef WAI_NOINLINE 36 | #if defined(_MSC_VER) 37 | #define WAI_NOINLINE __declspec(noinline) 38 | #elif defined(__GNUC__) 39 | #define WAI_NOINLINE __attribute__((noinline)) 40 | #else 41 | #error unsupported compiler 42 | #endif 43 | #endif 44 | 45 | #if defined(_MSC_VER) 46 | #define WAI_RETURN_ADDRESS() _ReturnAddress() 47 | #elif defined(__GNUC__) 48 | #define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) 49 | #else 50 | #error unsupported compiler 51 | #endif 52 | 53 | #if defined(_WIN32) 54 | 55 | #define WIN32_LEAN_AND_MEAN 56 | #if defined(_MSC_VER) 57 | #pragma warning(push, 3) 58 | #endif 59 | 60 | #include 61 | #include 62 | 63 | #if defined(_MSC_VER) 64 | #pragma warning(pop) 65 | #endif 66 | 67 | static int WAI_PREFIX(getModulePath_)(HMODULE module, char *out, int capacity, int *dirname_length) { 68 | wchar_t buffer1[MAX_PATH]; 69 | wchar_t buffer2[MAX_PATH]; 70 | wchar_t *path = NULL; 71 | int length = -1; 72 | 73 | for (;;) { 74 | DWORD size; 75 | int length_, length__; 76 | 77 | size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0])); 78 | 79 | if (size == 0) 80 | break; 81 | else if (size == (DWORD) (sizeof(buffer1) / sizeof(buffer1[0]))) { 82 | DWORD size_ = size; 83 | do { 84 | wchar_t *path_; 85 | 86 | path_ = (wchar_t *) WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2); 87 | if (!path_) 88 | break; 89 | size_ *= 2; 90 | path = path_; 91 | size = GetModuleFileNameW(module, path, size_); 92 | } while (size == size_); 93 | 94 | if (size == size_) 95 | break; 96 | } else 97 | path = buffer1; 98 | 99 | if (!_wfullpath(buffer2, path, MAX_PATH)) 100 | break; 101 | length_ = (int) wcslen(buffer2); 102 | length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, out, capacity, NULL, NULL); 103 | 104 | if (length__ == 0) 105 | length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL); 106 | if (length__ == 0) 107 | break; 108 | 109 | if (length__ <= capacity && dirname_length) { 110 | int i; 111 | 112 | for (i = length__ - 1; i >= 0; --i) { 113 | if (out[i] == '\\') { 114 | *dirname_length = i; 115 | break; 116 | } 117 | } 118 | } 119 | 120 | length = length__; 121 | 122 | break; 123 | } 124 | 125 | if (path != buffer1) 126 | WAI_FREE(path); 127 | 128 | return length; 129 | } 130 | 131 | WAI_NOINLINE WAI_FUNCSPEC 132 | int WAI_PREFIX(getExecutablePath)(char *out, int capacity, int *dirname_length) { 133 | return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length); 134 | } 135 | 136 | WAI_NOINLINE WAI_FUNCSPEC 137 | int WAI_PREFIX(getModulePath)(char *out, int capacity, int *dirname_length) { 138 | HMODULE module; 139 | int length = -1; 140 | 141 | #if defined(_MSC_VER) 142 | #pragma warning(push) 143 | #pragma warning(disable: 4054) 144 | #endif 145 | if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 146 | (LPCTSTR) WAI_RETURN_ADDRESS(), &module)) 147 | #if defined(_MSC_VER) 148 | #pragma warning(pop) 149 | #endif 150 | { 151 | length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length); 152 | } 153 | 154 | return length; 155 | } 156 | 157 | #elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE) 158 | 159 | #include 160 | #include 161 | #include 162 | #if defined(__linux__) 163 | #include 164 | #else 165 | #include 166 | #endif 167 | #ifndef __STDC_FORMAT_MACROS 168 | #define __STDC_FORMAT_MACROS 169 | #endif 170 | #include 171 | 172 | #if !defined(WAI_PROC_SELF_EXE) 173 | #if defined(__sun) 174 | #define WAI_PROC_SELF_EXE "/proc/self/path/a.out" 175 | #else 176 | #define WAI_PROC_SELF_EXE "/proc/self/exe" 177 | #endif 178 | #endif 179 | 180 | WAI_FUNCSPEC 181 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 182 | { 183 | char buffer[PATH_MAX]; 184 | char* resolved = NULL; 185 | int length = -1; 186 | 187 | for (;;) 188 | { 189 | resolved = realpath(WAI_PROC_SELF_EXE, buffer); 190 | if (!resolved) 191 | break; 192 | 193 | length = (int)strlen(resolved); 194 | if (length <= capacity) 195 | { 196 | memcpy(out, resolved, length); 197 | 198 | if (dirname_length) 199 | { 200 | int i; 201 | 202 | for (i = length - 1; i >= 0; --i) 203 | { 204 | if (out[i] == '/') 205 | { 206 | *dirname_length = i; 207 | break; 208 | } 209 | } 210 | } 211 | } 212 | 213 | break; 214 | } 215 | 216 | return length; 217 | } 218 | 219 | #if !defined(WAI_PROC_SELF_MAPS_RETRY) 220 | #define WAI_PROC_SELF_MAPS_RETRY 5 221 | #endif 222 | 223 | #if !defined(WAI_PROC_SELF_MAPS) 224 | #if defined(__sun) 225 | #define WAI_PROC_SELF_MAPS "/proc/self/map" 226 | #else 227 | #define WAI_PROC_SELF_MAPS "/proc/self/maps" 228 | #endif 229 | #endif 230 | 231 | #if defined(__ANDROID__) || defined(ANDROID) 232 | #include 233 | #include 234 | #include 235 | #endif 236 | 237 | WAI_NOINLINE WAI_FUNCSPEC 238 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 239 | { 240 | int length = -1; 241 | FILE* maps = NULL; 242 | 243 | for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r) 244 | { 245 | maps = fopen(WAI_PROC_SELF_MAPS, "r"); 246 | if (!maps) 247 | break; 248 | 249 | for (;;) 250 | { 251 | char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX]; 252 | uint64_t low, high; 253 | char perms[5]; 254 | uint64_t offset; 255 | uint32_t major, minor; 256 | char path[PATH_MAX]; 257 | uint32_t inode; 258 | 259 | if (!fgets(buffer, sizeof(buffer), maps)) 260 | break; 261 | 262 | if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8) 263 | { 264 | uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS(); 265 | if (low <= addr && addr <= high) 266 | { 267 | char* resolved; 268 | 269 | resolved = realpath(path, buffer); 270 | if (!resolved) 271 | break; 272 | 273 | length = (int)strlen(resolved); 274 | #if defined(__ANDROID__) || defined(ANDROID) 275 | if (length > 4 276 | &&buffer[length - 1] == 'k' 277 | &&buffer[length - 2] == 'p' 278 | &&buffer[length - 3] == 'a' 279 | &&buffer[length - 4] == '.') 280 | { 281 | int fd = open(path, O_RDONLY); 282 | char* begin; 283 | char* p; 284 | 285 | begin = (char*)mmap(0, offset + sizeof(p), PROT_READ, MAP_SHARED, fd, 0); 286 | p = begin + offset; 287 | 288 | while (p >= begin) // scan backwards 289 | { 290 | if (*((uint32_t*)p) == 0x04034b50UL) // local file header found 291 | { 292 | uint16_t length_ = *((uint16_t*)(p + 26)); 293 | 294 | if (length + 2 + length_ < (int)sizeof(buffer)) 295 | { 296 | memcpy(&buffer[length], "!/", 2); 297 | memcpy(&buffer[length + 2], p + 30, length_); 298 | length += 2 + length_; 299 | } 300 | 301 | break; 302 | } 303 | 304 | --p; 305 | } 306 | 307 | munmap(begin, offset); 308 | close(fd); 309 | } 310 | #endif 311 | if (length <= capacity) 312 | { 313 | memcpy(out, resolved, length); 314 | 315 | if (dirname_length) 316 | { 317 | int i; 318 | 319 | for (i = length - 1; i >= 0; --i) 320 | { 321 | if (out[i] == '/') 322 | { 323 | *dirname_length = i; 324 | break; 325 | } 326 | } 327 | } 328 | } 329 | 330 | break; 331 | } 332 | } 333 | } 334 | 335 | fclose(maps); 336 | maps = NULL; 337 | 338 | if (length != -1) 339 | break; 340 | } 341 | 342 | if (maps) 343 | fclose(maps); 344 | 345 | return length; 346 | } 347 | 348 | #elif defined(__APPLE__) 349 | 350 | #define _DARWIN_BETTER_REALPATH 351 | #include 352 | #include 353 | #include 354 | #include 355 | #include 356 | 357 | WAI_FUNCSPEC 358 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 359 | { 360 | char buffer1[PATH_MAX]; 361 | char buffer2[PATH_MAX]; 362 | char* path = buffer1; 363 | char* resolved = NULL; 364 | int length = -1; 365 | 366 | for (;;) 367 | { 368 | uint32_t size = (uint32_t)sizeof(buffer1); 369 | if (_NSGetExecutablePath(path, &size) == -1) 370 | { 371 | path = (char*)WAI_MALLOC(size); 372 | if (!_NSGetExecutablePath(path, &size)) 373 | break; 374 | } 375 | 376 | resolved = realpath(path, buffer2); 377 | if (!resolved) 378 | break; 379 | 380 | length = (int)strlen(resolved); 381 | if (length <= capacity) 382 | { 383 | memcpy(out, resolved, length); 384 | 385 | if (dirname_length) 386 | { 387 | int i; 388 | 389 | for (i = length - 1; i >= 0; --i) 390 | { 391 | if (out[i] == '/') 392 | { 393 | *dirname_length = i; 394 | break; 395 | } 396 | } 397 | } 398 | } 399 | 400 | break; 401 | } 402 | 403 | if (path != buffer1) 404 | WAI_FREE(path); 405 | 406 | return length; 407 | } 408 | 409 | WAI_NOINLINE WAI_FUNCSPEC 410 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 411 | { 412 | char buffer[PATH_MAX]; 413 | char* resolved = NULL; 414 | int length = -1; 415 | 416 | for(;;) 417 | { 418 | Dl_info info; 419 | 420 | if (dladdr(WAI_RETURN_ADDRESS(), &info)) 421 | { 422 | resolved = realpath(info.dli_fname, buffer); 423 | if (!resolved) 424 | break; 425 | 426 | length = (int)strlen(resolved); 427 | if (length <= capacity) 428 | { 429 | memcpy(out, resolved, length); 430 | 431 | if (dirname_length) 432 | { 433 | int i; 434 | 435 | for (i = length - 1; i >= 0; --i) 436 | { 437 | if (out[i] == '/') 438 | { 439 | *dirname_length = i; 440 | break; 441 | } 442 | } 443 | } 444 | } 445 | } 446 | 447 | break; 448 | } 449 | 450 | return length; 451 | } 452 | 453 | #elif defined(__QNXNTO__) 454 | 455 | #include 456 | #include 457 | #include 458 | #include 459 | #include 460 | 461 | #if !defined(WAI_PROC_SELF_EXE) 462 | #define WAI_PROC_SELF_EXE "/proc/self/exefile" 463 | #endif 464 | 465 | WAI_FUNCSPEC 466 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 467 | { 468 | char buffer1[PATH_MAX]; 469 | char buffer2[PATH_MAX]; 470 | char* resolved = NULL; 471 | FILE* self_exe = NULL; 472 | int length = -1; 473 | 474 | for (;;) 475 | { 476 | self_exe = fopen(WAI_PROC_SELF_EXE, "r"); 477 | if (!self_exe) 478 | break; 479 | 480 | if (!fgets(buffer1, sizeof(buffer1), self_exe)) 481 | break; 482 | 483 | resolved = realpath(buffer1, buffer2); 484 | if (!resolved) 485 | break; 486 | 487 | length = (int)strlen(resolved); 488 | if (length <= capacity) 489 | { 490 | memcpy(out, resolved, length); 491 | 492 | if (dirname_length) 493 | { 494 | int i; 495 | 496 | for (i = length - 1; i >= 0; --i) 497 | { 498 | if (out[i] == '/') 499 | { 500 | *dirname_length = i; 501 | break; 502 | } 503 | } 504 | } 505 | } 506 | 507 | break; 508 | } 509 | 510 | fclose(self_exe); 511 | 512 | return length; 513 | } 514 | 515 | WAI_FUNCSPEC 516 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 517 | { 518 | char buffer[PATH_MAX]; 519 | char* resolved = NULL; 520 | int length = -1; 521 | 522 | for(;;) 523 | { 524 | Dl_info info; 525 | 526 | if (dladdr(WAI_RETURN_ADDRESS(), &info)) 527 | { 528 | resolved = realpath(info.dli_fname, buffer); 529 | if (!resolved) 530 | break; 531 | 532 | length = (int)strlen(resolved); 533 | if (length <= capacity) 534 | { 535 | memcpy(out, resolved, length); 536 | 537 | if (dirname_length) 538 | { 539 | int i; 540 | 541 | for (i = length - 1; i >= 0; --i) 542 | { 543 | if (out[i] == '/') 544 | { 545 | *dirname_length = i; 546 | break; 547 | } 548 | } 549 | } 550 | } 551 | } 552 | 553 | break; 554 | } 555 | 556 | return length; 557 | } 558 | 559 | #elif defined(__DragonFly__) || defined(__FreeBSD__) || \ 560 | defined(__FreeBSD_kernel__) || defined(__NetBSD__) 561 | 562 | #include 563 | #include 564 | #include 565 | #include 566 | #include 567 | #include 568 | 569 | WAI_FUNCSPEC 570 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) 571 | { 572 | char buffer1[PATH_MAX]; 573 | char buffer2[PATH_MAX]; 574 | char* path = buffer1; 575 | char* resolved = NULL; 576 | int length = -1; 577 | 578 | for (;;) 579 | { 580 | #if defined(__NetBSD__) 581 | int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; 582 | #else 583 | int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; 584 | #endif 585 | size_t size = sizeof(buffer1); 586 | 587 | if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0) 588 | break; 589 | 590 | resolved = realpath(path, buffer2); 591 | if (!resolved) 592 | break; 593 | 594 | length = (int)strlen(resolved); 595 | if (length <= capacity) 596 | { 597 | memcpy(out, resolved, length); 598 | 599 | if (dirname_length) 600 | { 601 | int i; 602 | 603 | for (i = length - 1; i >= 0; --i) 604 | { 605 | if (out[i] == '/') 606 | { 607 | *dirname_length = i; 608 | break; 609 | } 610 | } 611 | } 612 | } 613 | 614 | break; 615 | } 616 | 617 | if (path != buffer1) 618 | WAI_FREE(path); 619 | 620 | return length; 621 | } 622 | 623 | WAI_NOINLINE WAI_FUNCSPEC 624 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) 625 | { 626 | char buffer[PATH_MAX]; 627 | char* resolved = NULL; 628 | int length = -1; 629 | 630 | for(;;) 631 | { 632 | Dl_info info; 633 | 634 | if (dladdr(WAI_RETURN_ADDRESS(), &info)) 635 | { 636 | resolved = realpath(info.dli_fname, buffer); 637 | if (!resolved) 638 | break; 639 | 640 | length = (int)strlen(resolved); 641 | if (length <= capacity) 642 | { 643 | memcpy(out, resolved, length); 644 | 645 | if (dirname_length) 646 | { 647 | int i; 648 | 649 | for (i = length - 1; i >= 0; --i) 650 | { 651 | if (out[i] == '/') 652 | { 653 | *dirname_length = i; 654 | break; 655 | } 656 | } 657 | } 658 | } 659 | } 660 | 661 | break; 662 | } 663 | 664 | return length; 665 | } 666 | 667 | #else 668 | 669 | #error unsupported platform 670 | 671 | #endif 672 | 673 | #ifdef __cplusplus 674 | } 675 | #endif -------------------------------------------------------------------------------- /whereami/whereami.h: -------------------------------------------------------------------------------- 1 | // (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses 2 | // without any warranty. 3 | // by Gregory Pakosz (@gpakosz) 4 | // https://github.com/gpakosz/whereami 5 | 6 | #ifndef WHEREAMI_H 7 | #define WHEREAMI_H 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #ifndef WAI_FUNCSPEC 14 | #define WAI_FUNCSPEC 15 | #endif 16 | #ifndef WAI_PREFIX 17 | #define WAI_PREFIX(function) wai_##function 18 | #endif 19 | 20 | /** 21 | * Returns the path to the current executable. 22 | * 23 | * Usage: 24 | * - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to 25 | * retrieve the length of the path 26 | * - allocate the destination buffer with `path = (char*)malloc(length + 1);` 27 | * - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the 28 | * path 29 | * - add a terminal NUL character with `path[length] = '\0';` 30 | * 31 | * @param out destination buffer, optional 32 | * @param capacity destination buffer capacity 33 | * @param dirname_length optional recipient for the length of the dirname part 34 | * of the path. 35 | * 36 | * @return the length of the executable path on success (without a terminal NUL 37 | * character), otherwise `-1` 38 | */ 39 | WAI_FUNCSPEC 40 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length); 41 | 42 | /** 43 | * Returns the path to the current module 44 | * 45 | * Usage: 46 | * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve 47 | * the length of the path 48 | * - allocate the destination buffer with `path = (char*)malloc(length + 1);` 49 | * - call `wai_getModulePath(path, length, NULL)` again to retrieve the path 50 | * - add a terminal NUL character with `path[length] = '\0';` 51 | * 52 | * @param out destination buffer, optional 53 | * @param capacity destination buffer capacity 54 | * @param dirname_length optional recipient for the length of the dirname part 55 | * of the path. 56 | * 57 | * @return the length of the module path on success (without a terminal NUL 58 | * character), otherwise `-1` 59 | */ 60 | WAI_FUNCSPEC 61 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #endif // #ifndef WHEREAMI_H --------------------------------------------------------------------------------