text: // LICENSE NOTICE ==================================================================================
  
  /**
   * netzhaut - Web Browser Engine
   * Copyright (C) 2020 The netzhaut Authors
   * Published under MIT
   */
  
  // INCLUDE =========================================================================================
  
  #include "nhcore.h"
  
  #include "../nhcore/Loader/Loader.h"
  #include "../nhcore/System/Thread.h"
  #include "../nhcore/Common/Initialize.h"
  
  #include <unistd.h>
  #include <dlfcn.h>
  #include <stddef.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  // DATA ============================================================================================
  
  nh_Loader *NH_LOADER_P = NULL;
  static void *core_p  = NULL;
  
  // LOADER ==========================================================================================
  
  static void *nh_openCoreLibrary(
      NH_BYTE *path_p)
  {
  #ifdef __unix__
  
      NH_BYTE *error_p;
      dlerror();
  
      void *dl_p = dlopen(path_p, RTLD_NOW | RTLD_GLOBAL);
  
      if ((error_p = dlerror()) != NULL) { 
          fprintf(stderr, "%s\n", error_p);
          return NULL;
      }
  
      return dl_p;
  
  #endif
  }
  
  static void *nh_closeCoreLibrary()
  {
  #ifdef __unix__
  
      dlclose(core_p);
  
  #endif
  }
  
  static NH_RESULT nh_getExeDir(
      NH_BYTE *buffer_p, size_t size)
  {
  #ifdef __unix__
  
      if (readlink("/proc/self/exe", buffer_p, 1024) == -1 
      &&  readlink("/proc/curproc/file", buffer_p, 1024) == -1
      &&  readlink("/proc/self/path/a.out", buffer_p, 1024) == -1) {return NH_ERROR_BAD_STATE;}
  
      int i;
      for (i = strlen(buffer_p); i > -1 && buffer_p[i] != '/'; --i) {}
      if (i == -1) {return NH_ERROR_BAD_STATE;}
  
      buffer_p[i] = '\0'; // remove exe name
      return NH_SUCCESS;
  
  #endif
  }
  
  static void *nh_loadCore(
      NH_LOADER_SCOPE loaderScope)
  {
  #ifdef __unix__
  
      switch (loaderScope)
      {
          case NH_LOADER_SCOPE_LOCAL :
          {
              NH_BYTE exeDir_p[2048] = {'\0'};
              if (nh_getExeDir(exeDir_p, 2048) != NH_SUCCESS) {return NULL;}
              NH_BYTE loaderPath_p[2048] = {'\0'};
              sprintf(loaderPath_p, "%s/netzhaut-master/lib/libnhcore.so.0", exeDir_p);
              return nh_openCoreLibrary(loaderPath_p);
          }
          case NH_LOADER_SCOPE_LOCAL_SYSTEM :
              return nh_openCoreLibrary("/usr/local/lib/libnhcore.so.0");
          case NH_LOADER_SCOPE_SYSTEM :
              return nh_openCoreLibrary("libnhcore.so.0");
      }
  
  #endif
  
      return NULL;
  }
  
  static void *nh_loadCoreFunction(
      const NH_BYTE *functionName_p)
  {
  #ifdef __unix__
  
      NH_BYTE *error_p;
      dlerror(); // clear any existing error
  
      void *function_p = dlsym(core_p, functionName_p); 
      if ((error_p = dlerror()) != NULL) {
          fprintf(stderr, "%s\n", error_p);
          return NULL;
      }
  
      return function_p;
  
  #endif
  }
  
  // INITIALIZE ======================================================================================
  
  NH_RESULT nh_initialize(
       NH_LOADER_SCOPE loaderScope, NH_BYTE *loaderPath_p, NH_BOOL install)
  {
      if (core_p != NULL || NH_LOADER_P != NULL) {return NH_ERROR_BAD_STATE;}
  
      core_p = nh_loadCore(loaderScope);
  
      NH_BOOL fallback = !core_p && loaderPath_p != NULL;
      if (fallback) {core_p = nh_openCoreLibrary(loaderPath_p);}
  
      if (!core_p) {return NH_ERROR_BAD_STATE;}
  
      nh_initialize_f initialize_f = nh_loadCoreFunction("nh_initialize");
      NH_LOADER_P = !initialize_f ? NULL : initialize_f(loaderScope, fallback, install);
  
      return NH_LOADER_P ? NH_SUCCESS : NH_ERROR_BAD_STATE;
  }
   
  NH_RESULT nh_initializeUsingDefaultArguments()
  {
      return nh_initialize(NH_LOADER_SCOPE_SYSTEM, NULL, NH_FALSE);
  }
  
  // RUN =============================================================================================
  
  unsigned int nh_run()
  {
      nh_runWorkloads_f runWorkloads_f = !NH_LOADER_P ? NULL : NH_LOADER_P->loadSymbol_f(NH_MODULE_CORE, 0, "nh_runWorkloads");
      return runWorkloads_f ? runWorkloads_f() : 0;
  }
  
  // IS RUNNING ======================================================================================
  
  NH_BOOL nh_keepRunning()
  {
      nh_keepRunning_f keepRunning_f = !NH_LOADER_P ? NULL : NH_LOADER_P->loadSymbol_f(NH_MODULE_CORE, 0, "nh_keepRunning");
      return keepRunning_f ? keepRunning_f() : NH_FALSE;
  }
  
  // TERMINATE =======================================================================================
  
  NH_RESULT nh_terminate()
  {
      if (!NH_LOADER_P) {return NH_ERROR_BAD_STATE;}
  
      nh_closeCoreLibrary();
      NH_LOADER_P = NULL;
  
      return NH_SUCCESS;
  }
  
  // LOG CALLBACK ====================================================================================
  
  NH_RESULT nh_addLogCallback(
      nh_logCallback_f logCallback_f)
  {
      nh_addLogCallback_f addLogCallback_f = !NH_LOADER_P ? NULL : NH_LOADER_P->loadSymbol_f(NH_MODULE_CORE, 0, "nh_addLogCallback");
      return addLogCallback_f ? addLogCallback_f(logCallback_f) : NH_ERROR_BAD_STATE;
  }
  
  