diff --git a/CMakeLists.txt b/CMakeLists.txt index c6a17877d..24dc42f72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,9 +43,10 @@ set(LLVM_LINK_COMPONENTS llvm_map_components_to_libnames(llvm_libs support core irreader ${LLVM_LINK_COMPONENTS}) +file(COPY ${CMAKE_SOURCE_DIR}/resources/lib DESTINATION ${CMAKE_BINARY_DIR}) + include_directories( - "${CMAKE_SOURCE_DIR}/src/" - "${CMAKE_SOURCE_DIR}/build/") + "${CMAKE_SOURCE_DIR}/src/") add_executable(c3c src/main.c @@ -86,8 +87,25 @@ add_executable(c3c src/compiler/llvm_codegen_function.c src/build/builder.c src/utils/toml.c src/build/project.c - src/compiler/sema_name_resolution.c src/target_info/target_info.c src/compiler/parse_expr.c src/compiler/parser_internal.h src/compiler/parse_stmt.c src/compiler/sema_passes.c src/compiler/sema_internal.h src/compiler/sema_decls.c src/compiler/sema_types.c src/compiler/sema_stmts.c src/compiler/number.c src/utils/vmem.c src/utils/vmem.h) + src/compiler/sema_name_resolution.c + src/target_info/target_info.c + src/compiler/parse_expr.c + src/compiler/parser_internal.h + src/compiler/parse_stmt.c + src/compiler/sema_passes.c + src/compiler/sema_internal.h + src/compiler/sema_decls.c + src/compiler/sema_types.c + src/compiler/sema_stmts.c + src/compiler/number.c + src/utils/vmem.c + src/utils/vmem.h + src/utils/whereami.c + ) target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wno-unknown-pragmas -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter) target_link_libraries(c3c m ${llvm_libs}) + +install(TARGETS c3c DESTINATION bin) + diff --git a/README.md b/README.md index 631ac97d8..a80605b9c 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,30 @@ C3 tries to be an alternative in the the C/C++ niche: fast and close to the meta ### Design Principles - Procedural "get things done"-type of language. - Try to stay close to C - only change where truly needed. -- C ABI compatibility and C C integration. +- C ABI compatibility and excellent C integration. - Learning C3 should be easy for a C programmer. -- Dare violating the "close to metal" principle if the value is great. - Data is inert. -- Avoid "big ideas". -- Avoid the kitchen sink language trap. +- Avoid "big ideas" & the "more is better" fallacy. +- Dare introducing some conveniences not "close to metal" if the value is great. + + +### In what ways do C3 differ from C? + +- No mandatory header files +- New semantic macro system +- Generic modules +- Module based +- Subarrays (slices) and vararrays built in +- Compile time reflection +- Enhanced compile time execution +- "Result" based zero overhead error handling +- Defer +- Value methods +- Associated enum data +- Built in strings +- No preprocessor +- Undefined behaviour trapped on debug by default +- Optional pre and post conditions ### Current status @@ -23,44 +41,34 @@ developer of Judge0. Design work is still being done in the design draft here: https://c3lang.github.io/c3docs/. If you have suggestions, send a mail to [christoffer@aegik.com](mailto:christoffer@aegik.com), [file an issue](https://github.com/c3lang/c3c/issues) or discuss C3 on the r/ProgrammingLanguages Discord server: https://discord.gg/cfu4wdk -There are some small work being done on the parser here, but most of the structure is still missing: -#### What's missing in the parser +#### What's currently missing - `asm` sections. - bitstructs - array range initializers e.g. `{ [1..2] = 2 }` -- assert/$assert as keywords -- Docs not linked to statements/functions/declarations. +- `assert` - with compiler hint +- `$switch` `$for` - compile time iteration / switch +- Pre/post conditions +- `generic` - explicit overloading +- `malloc` / `free` +- `string` not fully implemented +- vararrays, e.g. `int[*]` not working +- `unreachable` for compiler hinting +- Generic modules +- Stdlib not linked. -#### What's missing in the semantic analyser - -- Incomplete handling of imports. -- Function signatures incomplete. -- Function typedef not done. -- `asm` not done. -- `generic` not analysed. -- `$switch` and `$for` not handled. -- Enums not correctly handled. -- Type resolution not complete for all types. -- Macro call not handled completely. - -#### What's missing overall - -- Improved integration with C. -- Generic macros. -- Update of error system -- Imports aren't quite stable -- Strings, vararrays aren't finalized -- Stdlib not started -- More tests and support for multi file tests. +Also see: https://github.com/c3lang/c3c/issues #### What's working? -- Lexing and parsing works with some minor exceptions -- Normal code works +- Lexing/parsing/semantic analysis/codegen. +- "Regular code" should mostly work. - You can use any C function by declaring it as a normal C3 function with external -(For more details see missing.txt) +#### What can you help with? -If you wish to contribute with ideas, please file issues on the c3docs: https://github.com/c3lang/c3docs instead of the compiler. +- If you wish to contribute with ideas, please file issues on the c3docs: https://github.com/c3lang/c3docs instead of the compiler. +- Discuss the language on discord to help iron out syntax. +- Stdlib work will soon start, do you want to help out building the C3 std lib? +- Do you want do do real compiler work? Everyone is welcome to contribute. \ No newline at end of file diff --git a/missing.txt b/missing.txt deleted file mode 100644 index 3d0ab5c7d..000000000 --- a/missing.txt +++ /dev/null @@ -1,74 +0,0 @@ -Things missing: - -* Attributes -- All types: @noreflect, @deprecated -- Struct: @packed, @opaque -- Enums: @distinct, @noreflect -- Unions: @packed, @opaque -- Functions: @reflect, @noreturn, @unused, @used, @interrupt, @naked, @convention() -- Calls: @noinline, @inline -- Variables, parameters: @unused -- Constants, globals: @unused, @used, @section -- Labels: @unused - -* Designated initializer -- Array range initializer { [1..2] = 2 } - -* Initializers -- Vararray initializers -- Incremental array initializers -- Slice initializers -- Constant initializers for globals - -* Asserts -- assert, $assert -- @unreachable - -* Types -- Vararrays -- Strings -- Array -- Slice -- Values: alignment, name, qualifiedName -- Functions: offsetof -- Distinct types -- Simd types? -- Complex types? -- Subtype casts -- Bitstruct -- Enumset -- Typeid -- Ranges - -* Arrays -- Allow fixed arrays with no larger size than 32 bit unsigned. - -* Struct / union -- Cast to union? -- Structural typed anonymous structs and casts to them. - -* Expressions -- Disallow x >= 0 and x < 0 on unsigned types unless in a macro. -- Range check arrays on debug -- Allow negating int if assigned to a larger type. E.g short x = 1; int y = -x; -- Expression block does not handle defer correctly. - -* Switch -- String switch -- Range case - -* Functions -- C ABI -- Safe varargs -- Malloc/free -- Check that structs are transferred the right way. - -* Pre-post conditions -- Breakdown here - -* Enum -- Values: min, max, array -- Functions: fomOrdinal, ordinal, fromName, name, fromFullName, fullName, fromQualifiedName, qualifiedName, (), fromValue() - -* Error reporting -- When a variable fails to parse correctly, store it to prevent follow up errors. diff --git a/resources/lib/system/builtin.c3 b/resources/lib/std/builtin.c3 similarity index 83% rename from resources/lib/system/builtin.c3 rename to resources/lib/std/builtin.c3 index 1d2c90974..218a125c7 100644 --- a/resources/lib/system/builtin.c3 +++ b/resources/lib/std/builtin.c3 @@ -1,4 +1,4 @@ -module system::builtin; +module std::builtin; enum TypeKind { @@ -30,56 +30,56 @@ struct TypeData struct TypeAlias { - TypeData; + TypeData data; typeid aliasType; } struct TypeError { - TypeData; + TypeData data; TypeErrorValue[] errors; } struct TypeArray { - TypeData; + TypeData data; typeid elementType; ulong elements; } struct TypeVarArray { - TypeData; + TypeData data; typeid elementType; } struct TypeSubarray { - TypeData; + TypeData data; typeid elementType; } struct TypePointer { - TypeData; + TypeData data; typeid baseType; } struct TypeStruct { - TypeData; + TypeData data; TypeData*[] fields; } struct TypeUnion { - TypeData; + TypeData data; TypeData*[] variants; } struct TypeEnum { - TypeData; + TypeData data; typeid valueType; TypeData*[] associated_value_types; } diff --git a/resources/lib/std/cinterop.c3 b/resources/lib/std/cinterop.c3 new file mode 100644 index 000000000..a50113e77 --- /dev/null +++ b/resources/lib/std/cinterop.c3 @@ -0,0 +1,2 @@ +module std:cinterop; + diff --git a/resources/lib/system/io.c3 b/resources/lib/std/io.c3 similarity index 91% rename from resources/lib/system/io.c3 rename to resources/lib/std/io.c3 index cdbcd0b66..c541dfc81 100644 --- a/resources/lib/system/io.c3 +++ b/resources/lib/std/io.c3 @@ -1,23 +1,32 @@ -module system::io; - -typedef File as void; +module std::io; +/* extern File *stdin @cname(__stdinp); extern File *stdout @cname(__stdoutp); extern File *stderr @cname(__stderrp); +*/ +/* +extern func int fputc(int, aliased void*); +extern func void clearerr(aliased void*); +extern func int fclose(aliased void*); +extern func int feof(aliased void*); +extern func int ferror(aliased void*); +extern func int fflush(aliased FILE *); +extern func int fgetc(aliased FILE *); +extern func int fgetpos(FILE *, fpos_t *); +extern func int fseek(void*, long, int); +extern func void* fopen(char *, char *); +*/ -extern int fputc(int, aliased void*); -extern void clearerr(aliased void*); -extern int fclose(aliased void*); -extern int feof(aliased void*); -extern int ferror(aliased void*); -extern int fflush(aliased FILE *); -extern int fgetc(aliased FILE *); -extern int fgetpos(FILE *, fpos_t *); -extern int fseek(void*, long, int); -extern void* fopen(char *, char *); +extern func int _puts(char* message) @cname("puts"); +extern func int printf(char* message, ...); +public func int println(char *message) +{ + return _puts(message); +} +/* struct File { void *file; @@ -30,38 +39,40 @@ enum Seek END = 2 } -errorset FileError +error FileError { + ulong errno; } -local func FileError errorFromErrno() +func FileError errorFromErrno() { + return FileError { }; } -public func void File.open(File *file, char *filename, char *mode) throws FileError +public func void! File.open(File *file, char *filename, char *mode) { file.file = fopen(filename, mode); - if (!file.file) throw errorFromErrno(); + if (!file.file) return errorFromErrno()!; } -public func void File.seek(File *file, long offset, Seek seekMode = Seek.SET) throws FileError +public func void! File.seek(File *file, long offset, Seek seekMode = Seek.SET) { - if (fseek(file->file, offset, cast(seekMode as int))) throw errorFromErrno(); + if (fseek(file->file, offset, cast(seekMode as int))) return errorFromErrno()!; } -public func void File.putChar(File *file as char c) throws FileError +public func void! File.putChar(File *file as char c) { - if (fputc(c, file->file)) throw errorFromErrno(); + if (fputc(c, file->file)) return errorFromErrno()!; } -pubic func void File.clearerr(File *file) @inline throws FileError +pubic func void! File.clearerr(File *file) @inline { clearerr(file->file); } func void File.close(File *file) @inline { - if (fclose(file->file)) throw errorFromErrno(); + if (fclose(file->file)) return errorFromErrno()!; } func void File.eof(File *file) @inline @@ -73,7 +84,8 @@ func void File.error(File *file) @inline { int err = ferror } - +*/ +/+ #define __SLBF 0x0001 /* line buffered */ #define __SNBF 0x0002 /* unbuffered */ @@ -388,3 +400,4 @@ __END_DECLS #endif #endif /* _STDIO_H_ */ ++/ \ No newline at end of file diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 43bf5d243..f5460cebe 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -2,8 +2,14 @@ module bar; import baz; import gen; - typedef int as Bob; + +func void testVararg(int foo, ... args) +{ + @args.start(); + int i = @args.next(i, int); + @args.end(); +} /* macro namespaceFor(ns; void(int i) $body) { @@ -1229,7 +1235,6 @@ func void test22() func int main(int x) { - int[6] feok2 = { 1, 8, 100, 293, 23982, 34}; int[] feok = &feok2; int[] flok = feok2[3..6]; diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index d82ef48ff..7e2e572e2 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -17,6 +17,8 @@ Vmem type_info_arena; void compiler_init(void) { + compiler.lib_dir = find_lib_dir(); + DEBUG_LOG("Found std library: %s", compiler.lib_dir); stable_init(&compiler.modules, 64); stable_init(&compiler.global_symbols, 0x1000); vmem_init(&ast_arena, 4 * 1024); @@ -72,6 +74,8 @@ void compiler_compile(BuildTarget *target) { Context **contexts = NULL; diag_reset(); + vec_add(target->sources, strformat("%s/std/builtin.c3", compiler.lib_dir)); + //vec_add(target->sources, strformat("%s/std/io.c3", compiler.lib_dir)); VECEACH(target->sources, i) { bool loaded = false; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 975fcfc87..0864cb96c 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1107,6 +1107,7 @@ typedef struct STable global_symbols; STable qualified_symbols; Type **type; + const char *lib_dir; } Compiler; typedef enum diff --git a/src/compiler/context.c b/src/compiler/context.c index 14511fdf6..34c39f821 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -192,7 +192,7 @@ bool context_add_import(Context *context, Path *path, Token token, Token alias) } vec_add(context->imports, import); - DEBUG_LOG("Added import %s\n", path->module); + DEBUG_LOG("Added import %s", path->module); return true; } diff --git a/src/compiler/parser.c b/src/compiler/parser.c index a96154942..af7c77d98 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -1708,7 +1708,7 @@ static inline Decl *parse_top_level(Context *context) { return parse_global_declaration(context, visibility); } - SEMA_TOKEN_ERROR(context->tok, "Unexpected symbol found"); + SEMA_TOKEN_ERROR(context->tok, "Unexpected symbol found."); return poisoned_decl; } } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 07ba5c329..9ca608fca 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -205,6 +205,8 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi } buffer[buffer_write_offset++] = ')'; + if (!all_ok) return NULL; + Type *return_type = signature->rtype->type->canonical; signature->return_param = false; if (return_type->type_kind != TYPE_VOID) @@ -216,7 +218,6 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi } } - if (!all_ok) return NULL; TokenType type = TOKEN_INVALID_TOKEN; signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type); Type *func_type = stable_get(&context->local_symbols, signature->mangled_signature); diff --git a/src/utils/file_utils.c b/src/utils/file_utils.c index 59fdd849b..aa9074506 100644 --- a/src/utils/file_utils.c +++ b/src/utils/file_utils.c @@ -11,6 +11,7 @@ #include #include #include +#include "whereami.h" const char* expand_path(const char* path) { @@ -55,6 +56,7 @@ int filename_to_module(const char *path, char buffer[MAX_IDENTIFIER_LENGTH + 1]) return namelen; } + char *read_file(const char *path, size_t *return_size) { FILE *file = fopen(path, "rb"); @@ -69,24 +71,56 @@ char *read_file(const char *path, size_t *return_size) *return_size = file_size; rewind(file); - char *buffer = (char *)malloc((size_t)file_size + 1); + char *buffer = (char *)malloc(file_size + 1); if (buffer == NULL) { error_exit("Not enough memory to read \"%s\".\n", path); } - size_t bytesRead = fread(buffer, sizeof(char), (size_t)file_size, file); - if (bytesRead < file_size) + size_t bytes_read = fread(buffer, sizeof(char), file_size, file); + if (bytes_read < file_size) { error_exit("Failed to read file \"%s\".\n", path); } - buffer[bytesRead] = '\0'; + buffer[bytes_read] = '\0'; fclose(file); return buffer; } +const char* find_lib_dir(void) +{ + const char *path = find_executable_path(); + + DEBUG_LOG("Detected executable path at %s", path); + + struct stat info; + char *lib_path = NULL; + asprintf(&lib_path, "%s../lib/std/", path); + DEBUG_LOG("Checking %s", lib_path); + int err = stat(lib_path, &info); + + // Found it at ../lib/std + if (!err && S_ISDIR(info.st_mode)) + { + asprintf(&lib_path, "%s../lib/", path); + return lib_path; + } + + asprintf(&lib_path, "%slib/std/", path); + err = stat(lib_path, &info); + + // Found it at ./lib/std + if (!err && S_ISDIR(info.st_mode)) + { + asprintf(&lib_path, "%slib/", path); + return lib_path; + } + + error_exit("Could not find the standard library /lib/std/"); +} + void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path) { char path[256]; @@ -107,8 +141,6 @@ void path_get_dir_and_filename_from_full(const char *full_path, char **filename, } - - void file_find_top_dir() { while (1) diff --git a/src/utils/lib.h b/src/utils/lib.h index 950669b3f..749b1ce00 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -7,6 +7,7 @@ #include "common.h" const char* expand_path(const char* path); +const char* find_lib_dir(void); char *read_file(const char *path, size_t *return_size); int filename_to_module(const char *path, char buffer[MAX_IDENTIFIER_LENGTH + 1]); void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path); diff --git a/src/utils/whereami.c b/src/utils/whereami.c new file mode 100644 index 000000000..f3ca6fec3 --- /dev/null +++ b/src/utils/whereami.c @@ -0,0 +1,431 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +// Code based off Gregory Pakosz's whereami. + +#include "whereami.h" + +#include + +#ifndef NOINLINE +#if defined(_MSC_VER) +#define NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +#define NOINLINE __attribute__((noinline)) +#else +#error unsupported compiler +#endif +#endif + +#if defined(_MSC_VER) +#define RETURN_ADDRESS() _ReturnAddress() +#elif defined(__GNUC__) +#define RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) +#else +#error unsupported compiler +#endif + +#if defined(_WIN32) + +#if defined(_MSC_VER) +#pragma warning(push, 3) +#endif + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +static int get_module_path_(HMODULE module, char *out, int capacity, int *dirname_length) +{ + wchar_t buffer1[MAX_PATH]; + wchar_t buffer2[MAX_PATH]; + wchar_t *path = NULL; + int length = -1; + + for (;;) + { + DWORD size; + int length_, length__; + + size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0])); + + if (size == 0) + { + break; + } + else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0]))) + { + DWORD size_ = size; + do + { + wchar_t *path_; + + path_ = (wchar_t *) realloc(path, sizeof(wchar_t) * size_ * 2); + if (!path_) + { + break; + } + size_ *= 2; + path = path_; + size = GetModuleFileNameW(module, path, size_); + } while (size == size_); + + if (size == size_) + { + break; + } + } + else + { + path = buffer1; + } + + if (!_wfullpath(buffer2, path, MAX_PATH)) + { + break; + } + length_ = (int) wcslen(buffer2); + length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, out, capacity, NULL, NULL); + + if (length__ == 0) + { + length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL); + } + if (length__ == 0) + { + break; + } + + if (length__ <= capacity && dirname_length) + { + int i; + + for (i = length__ - 1; i >= 0; --i) + { + if (out[i] == '\\') + { + *dirname_length = i; + break; + } + } + } + + length = length__; + + break; + } + + if (path != buffer1) + { + free(path); + } + + return length; +} + +NOINLINE +int get_executable_path_raw(char *out, int capacity, int *dirname_length) +{ + return get_module_path_(NULL, out, capacity, dirname_length); +} + + +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(USE_PROC_SELF_EXE) + +#include +#include +#include + +#if defined(__linux__) + +#include + +#else +#include +#endif +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include + +#if !defined(PROC_SELF_EXE) +#if defined(__sun) +#define PROC_SELF_EXE "/proc/self/path/a.out" +#else +#define PROC_SELF_EXE "/proc/self/exe" +#endif +#endif + +static int get_executable_path_raw(char *out, int capacity, int *dirname_length) +{ + char buffer[PATH_MAX]; + char *resolved = NULL; + int length = -1; + + for (;;) + { + resolved = realpath(PROC_SELF_EXE, buffer); + if (!resolved) + { + break; + } + + length = (int) strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + + return length; +} + +#elif defined(__APPLE__) + +#define _DARWIN_BETTER_REALPATH + +#include +#include +#include +#include +#include + + +static int get_executable_path_raw(char *out, int capacity, int *dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char *path = buffer1; + char *resolved = NULL; + int length = -1; + + for (;;) + { + uint32_t size = (uint32_t) sizeof(buffer1); + if (_NSGetExecutablePath(path, &size) == -1) + { + path = (char *) malloc(size); + if (!_NSGetExecutablePath(path, &size)) + { + break; + } + } + + resolved = realpath(path, buffer2); + if (!resolved) + { + break; + } + + length = (int) strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + + if (path != buffer1) + { + free(path); + } + return length; +} + +#elif defined(__QNXNTO__) + +#include +#include +#include +#include +#include + +#if !defined(PROC_SELF_EXE) +#define PROC_SELF_EXE "/proc/self/exefile" +#endif + +static int get_executable_path_raw(char *out, int capacity, int *dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char *resolved = NULL; + FILE *self_exe = NULL; + int length = -1; + + for (;;) + { + self_exe = fopen(PROC_SELF_EXE, "r"); + if (!self_exe) + { + break; + } + + if (!fgets(buffer1, sizeof(buffer1), self_exe)) + { + break; + } + + resolved = realpath(buffer1, buffer2); + if (!resolved) + { + break; + } + + length = (int) strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + + fclose(self_exe); + + return length; +} + +#elif defined(__DragonFly__) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__NetBSD__) + +#include +#include +#include +#include +#include +#include + +static int get_executable_path_raw(char *out, int capacity, int *dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char *path = buffer1; + char *resolved = NULL; + int length = -1; + + for (;;) + { +#if defined(__NetBSD__) + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; +#else + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; +#endif + size_t size = sizeof(buffer1); + + if (sysctl(mib, (u_int) (sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0) + { + break; + } + + resolved = realpath(path, buffer2); + if (!resolved) + { + break; + } + + length = (int) strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + + if (path != buffer1) + { + free(path); + } + + return length; +} + +#else + +#error unsupported platform + +#endif + +const char *find_executable_path(void) +{ + int len = get_executable_path_raw(NULL, 0, NULL); + char *path = malloc(len + 1); + get_executable_path_raw(path, len, NULL); + path[len] = '\0'; + for (int i = len - 1; i >= 0; i--) + { + switch (path[i]) + { + case '/': + case '\\': + path[i + 1] = '\0'; + return path; + } + } + path[len] = '\0'; + return path; +} \ No newline at end of file diff --git a/src/utils/whereami.h b/src/utils/whereami.h new file mode 100644 index 000000000..1e56038e6 --- /dev/null +++ b/src/utils/whereami.h @@ -0,0 +1,7 @@ +#pragma once + +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +const char *find_executable_path(void); \ No newline at end of file