diff --git a/Makefile b/Makefile index 482ca7da..cfc83c30 100755 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ C_FILES := $(wildcard *.c) $(wildcard **/*.c) H_FILES := $(wildcard *.h) $(wildcard **/*.h) WARNINGS := -Wno-write-strings PLAT_LINKS := -L/usr/local/lib -lX11 -lpthread -lm -lrt -lGL -ldl -lXfixes -lfreetype -lfontconfig -FLAGS := -fPIC -fno-threadsafe-statics -pthread -I../foreign $(shell pkg-config --cflags freetype2) +FLAGS := -D_GNU_SOURCE -fPIC -fno-threadsafe-statics -pthread -I../foreign $(shell pkg-config --cflags freetype2) # main stuff diff --git a/filetrack/4tech_file_track_linux.c b/filetrack/4tech_file_track_linux.c index e8b7251f..2850bcbe 100644 --- a/filetrack/4tech_file_track_linux.c +++ b/filetrack/4tech_file_track_linux.c @@ -12,61 +12,22 @@ #include "4tech_file_track.h" #include "4tech_file_track_general.c" - -// NOTE(allen): -// There are a number of places where the "table" is built into this system. -// If a table is not actually needed we will leave it in the API, but pull -// out all of the following internal refrences to the table: -// In Linux_File_Track_Vars : -// + void *tables; and all refrences to it -// The entire Linux_File_Track_Entry struct and all refrences to it. -// In init_track_system : -// + enough_memory_to_init_table(table_memory_size) -// + everything under the note "Initialize main data tables" -// Leave move_track_system unimplemented and assert when it is called -// +#include // dirname typedef struct { void *tables; - // NOTE(allen): - // This struct must fit inside the opaque File_Track_System struct. - // If it needs more room we can make File_Track_System bigger. - // - // Here we need: - // 1. A mutex of some kind to make the main operations thread safe. - // If it turns out the operations will be thread safe without a mutex - // then it is not required. - // 2. Any synchronization primatives that are needed for dequeueing - // change events. - // 3. Any data structures needed for handling the listeners. - // This system can expect to be provided with memory from the user. - // Right now the way the API works, it assumes that the user will - // only ever add memory and never free it. If freeing becomes necessary - // on Linux the API can be extended to support that. - int inotify; pthread_mutex_t lock; char *string_mem_begin; char *string_mem_end; - } Linux_File_Track_Vars; -//static_assert(sizeof(Linux_File_Track_Vars) <= sizeof(File_Track_System)); - typedef struct { File_Index hash; - // NOTE(allen): - // This struct must fit inside the opaque File_Track_Entry struct. - // The table is meant to help keep track of what is already being - // tracked. It may turn out that this isn't needed on Linux which - // would be fine. If it is used hash should be the first element - // of this struct and it should be used to uniquely identify each - // entry because it is used as the key for the table. - char* filename; + char* dir; + int ref_count; } Linux_File_Track_Entry; -//static_assert(sizeof(Linux_File_Track_Entry) <= sizeof(File_Track_Entry)); - #define to_vars(s) ((Linux_File_Track_Vars*)(s)) #define to_tables(v) ((File_Track_Tables*)(v->tables)) @@ -75,12 +36,14 @@ init_track_system(File_Track_System *system, void *table_memory, int32_t table_memory_size, void *listener_memory, int32_t listener_memory_size){ File_Track_Result result = FileTrack_MemoryTooSmall; + + Assert(sizeof(Linux_File_Track_Vars) <= sizeof(File_Track_System)); + Linux_File_Track_Vars *vars = to_vars(system); Assert(sizeof(Linux_File_Track_Entry) <= sizeof(File_Track_Entry)); - if (enough_memory_to_init_table(table_memory_size) && - /*if listener memory is important check it's size here*/ 1){ + if (enough_memory_to_init_table(table_memory_size)){ // NOTE(allen): Initialize main data tables vars->tables = (File_Track_Tables*) table_memory; @@ -110,21 +73,13 @@ add_listener(File_Track_System *system, char *filename){ pthread_mutex_lock(&vars->lock); - // NOTE(allen): - // Here do something to begin listening to changes to the file named filename. - // On Windows it listens to the parent directory if no other file in that - // directory is already being listened to. We will assume that the user - // never passes the same filename in twice, although they may pass in two - // different names that both refer to the same file. In this case we should - // treat them as two separate files so that if one of the listeners is removed - // the other on the same file keeps working. - if(tracking_system_has_space(tables, 1)){ - size_t filename_len = strlen(filename) + 1; - if(vars->string_mem_end - vars->string_mem_begin >= filename_len){ + char *dir = dirname(strdupa(filename)); + size_t dir_len = strlen(dir) + 1; + + if(vars->string_mem_end - vars->string_mem_begin >= dir_len){ + int wd = inotify_add_watch(vars->inotify, dir, IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE); - // TODO(inso): which events do we want? - int wd = inotify_add_watch(vars->inotify, filename, IN_ALL_EVENTS); if(wd != -1){ File_Index key = { wd, 1 }; File_Track_Entry *entry = tracking_system_lookup_entry(tables, key); @@ -132,15 +87,17 @@ add_listener(File_Track_System *system, char *filename){ LINUX_FN_DEBUG("map %s to wd %d", filename, wd); - Assert(entry_is_available(entry)); - - linux_entry->hash = key; - linux_entry->filename = vars->string_mem_begin; - - memcpy(vars->string_mem_begin, filename, filename_len); - vars->string_mem_begin += filename_len; - - ++tables->tracked_count; + if(entry_is_available(entry)){ + linux_entry->hash = key; + linux_entry->dir = vars->string_mem_begin; + linux_entry->ref_count = 1; + memcpy(vars->string_mem_begin, dir, dir_len); + vars->string_mem_begin += dir_len; + ++tables->tracked_count; + } else { + LINUX_FN_DEBUG("dir already tracked, adding ref"); + ++linux_entry->ref_count; + } } else { result = FileTrack_FileSystemError; } @@ -167,21 +124,22 @@ remove_listener(File_Track_System *system, char *filename){ pthread_mutex_lock(&vars->lock); - // NOTE(allen): - // Here do something to stop listening to changes to the file named filename. - // We assume this filename has been passed into add_listener already and that - // if it has not it is a bug in the user's code not in this code. + char *dir = dirname(strdupa(filename)); + // NOTE(inso): this assumes the filename was previously added for(uint32_t i = 0; i < tables->max; ++i){ Linux_File_Track_Entry *e = (Linux_File_Track_Entry*)(entries + i); if(e->hash.id[1] != 1) continue; - if(strcmp(e->filename, filename) == 0){ + if(strcmp(e->dir, dir) == 0){ LINUX_FN_DEBUG("%s found as wd %d", filename, e->hash.id[0]); - if(inotify_rm_watch(vars->inotify, e->hash.id[0]) == -1){ - perror("inotify_rm_watch"); - result = FileTrack_FileSystemError; + if(--e->ref_count == 0){ + LINUX_FN_DEBUG("refcount == 0, calling rm_watch"); + if(inotify_rm_watch(vars->inotify, e->hash.id[0]) == -1){ + perror("inotify_rm_watch"); + result = FileTrack_FileSystemError; + } + internal_free_slot(tables, (File_Track_Entry*)e); } - internal_free_slot(tables, (File_Track_Entry*)e); // NOTE(inso): associated string memory in listeners would be freed here break; } @@ -223,11 +181,6 @@ expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size pthread_mutex_lock(&vars->lock); - // NOTE(allen): If there is a data structure for the listeners - // this call adds more memory to that system. If this system - // wants to free old memory the API does not currently support - // that but it can. - // NOTE(inso): pointer to old string mem is lost here. // would need to keep it around if we want to free in the future @@ -255,38 +208,50 @@ get_change_event(File_Track_System *system, char *buffer, int32_t max, int32_t * pthread_mutex_lock(&vars->lock); - // NOTE(allen): If there are any new file changes report them - // by copying the name of the file to the buffer and returning - // FileTrack_Good. It is allowed for this system to report - // changes to files that were not requested to be tracked, it - // is up to the user to check the filename and see if it cares - // about that file. If there are no new changes the return - // FileTrack_NoMoreEvents. + struct inotify_event *ev; + char buff[sizeof(*ev) + NAME_MAX + 1] __attribute__((aligned(__alignof__(*ev)))); - struct inotify_event ev; + // NOTE(inso): make sure we only read one event + size_t read_count = sizeof(*ev); + ssize_t n; + + do { + n = read(vars->inotify, buff, read_count); + read_count++; + } while(n == -1 && errno == EINVAL); - ssize_t n = read(vars->inotify, &ev, sizeof(ev)); if(n == -1 && errno != EAGAIN){ perror("inotify read"); } else if(n > 0){ - File_Index key = { ev.wd, 1 }; + ev = (struct inotify_event*) buff; + + File_Index key = { ev->wd, 1 }; File_Track_Entry *entry = tracking_system_lookup_entry(tables, key); Linux_File_Track_Entry *linux_entry = (Linux_File_Track_Entry*) entry; + LINUX_FN_DEBUG("mask: %#x", ev->mask); + if(!entry_is_available(entry)){ - LINUX_FN_DEBUG("event from wd %d (%s)", ev.wd, linux_entry->filename); - size_t filename_size = strlen(linux_entry->filename); - if(max < filename_size){ + + char* full_name = (char*) alloca(strlen(linux_entry->dir) + ev->len + 1); + strcpy(full_name, linux_entry->dir); + strcat(full_name, "/"); + strcat(full_name, ev->name); + + LINUX_FN_DEBUG("event from wd %d (%s)", ev->wd, full_name); + + size_t full_name_size = strlen(full_name); + if(max < full_name_size){ result = FileTrack_MemoryTooSmall; // NOTE(inso): this event will be dropped, needs to be stashed. LINUX_FN_DEBUG("max too small, event dropped"); } else { - memcpy(buffer, linux_entry->filename, filename_size); - *size = filename_size; + memcpy(buffer, full_name, full_name_size); + *size = full_name_size; result = FileTrack_Good; } } else { - LINUX_FN_DEBUG("dead event from wd %d", ev.wd); + LINUX_FN_DEBUG("dead event from wd %d", ev->wd); } } diff --git a/linux_4ed.cpp b/linux_4ed.cpp index dd72d139..e53c0c3b 100644 --- a/linux_4ed.cpp +++ b/linux_4ed.cpp @@ -10,18 +10,54 @@ // TOP -#include "4coder_default_bindings.cpp" +# include "4ed_defines.h" -#undef exec_command -#undef exec_command_keep_stack -#undef clear_parameters +#if FRED_SUPER -#include "4ed_meta.h" +# define FSTRING_IMPLEMENTATION +# define FSTRING_C +# include "4coder_string.h" -#define FCPP_FORBID_MALLOC +#include "4coder_version.h" +# include "4coder_keycodes.h" +# include "4coder_style.h" +# include "4coder_rect.h" -#define FCPP_STRING_IMPLEMENTATION -#include "4coder_string.h" +# include + +# include "4coder_mem.h" + +// TODO(allen): This is duplicated from 4coder_custom.h +// I need to work out a way to avoid this. +#define VIEW_ROUTINE_SIG(name) void name(struct Application_Links *app, int32_t view_id) +#define GET_BINDING_DATA(name) int32_t name(void *data, int32_t size) +#define _GET_VERSION_SIG(n) int32_t n(int32_t maj, int32_t min, int32_t patch) + +typedef VIEW_ROUTINE_SIG(View_Routine_Function); +typedef GET_BINDING_DATA(Get_Binding_Data_Function); +typedef _GET_VERSION_SIG(_Get_Version_Function); + +struct Custom_API{ + View_Routine_Function *view_routine; + Get_Binding_Data_Function *get_bindings; + _Get_Version_Function *get_alpha_4coder_version; +}; + + +typedef void Custom_Command_Function; +#include "4coder_types.h" +struct Application_Links; +# include "4ed_os_custom_api.h" + +//# include "4coder_custom.h" +#else +# include "4coder_default_bindings.cpp" + +# define FSTRING_IMPLEMENTATION +# define FSTRING_C +# include "4coder_string.h" + +#endif #include "4ed_math.h" @@ -464,15 +500,20 @@ Sys_Get_Canonical_Sig(system_get_canonical){ int32_t result = 0; char* path = realpath(strndupa(filename, len), NULL); - if(!path){ - perror("realpath"); - } else { + if(path){ size_t path_len = strlen(path); if(max >= path_len){ memcpy(buffer, path, path_len); result = path_len; } +#if FRED_INTERNAL + if(strncmp(filename, path, len) != 0){ + LINUX_FN_DEBUG("[%.*s] -> [%s]", len, filename, path); + } +#endif free(path); + } else { + LINUX_FN_DEBUG("[%.*s] -> [null]", len, filename); } return result; @@ -670,7 +711,7 @@ FILE_EXISTS_SIG(system_file_exists){ internal DIRECTORY_CD_SIG(system_directory_cd){ - String directory = make_string(dir, *len, capacity); + String directory = make_string_cap(dir, *len, capacity); b32 result = 0; i32 old_size; @@ -685,8 +726,8 @@ DIRECTORY_CD_SIG(system_directory_cd){ else{ if (directory.size + rel_len + 1 > directory.memory_size){ old_size = directory.size; - append_partial(&directory, rel_path); - append_partial(&directory, "/"); + append_partial_sc(&directory, rel_path); + append_s_char(&directory, '/'); terminate_with_null(&directory); struct stat st; @@ -709,7 +750,7 @@ DIRECTORY_CD_SIG(system_directory_cd){ internal GET_4ED_PATH_SIG(system_get_4ed_path){ - String str = make_string(out, 0, capacity); + String str = make_string_cap(out, 0, capacity); return(system_get_binary_path(&str)); } @@ -2827,6 +2868,7 @@ main(int argc, char **argv) if (linuxvars.custom_api.get_alpha_4coder_version == 0 || linuxvars.custom_api.get_alpha_4coder_version(MAJOR, MINOR, PATCH) == 0){ fprintf(stderr, "*** Failed to use 4coder_custom.so: version mismatch ***\n"); + exit(1); } else{ linuxvars.custom_api.get_bindings = (Get_Binding_Data_Function*) @@ -2836,6 +2878,7 @@ main(int argc, char **argv) if (linuxvars.custom_api.get_bindings == 0){ fprintf(stderr, "*** Failed to use 4coder_custom.so: get_bindings not exported ***\n"); + exit(1); } else{ fprintf(stderr, "Successfully loaded 4coder_custom.so\n"); @@ -2844,13 +2887,13 @@ main(int argc, char **argv) } else { const char* error = dlerror(); fprintf(stderr, "*** Failed to load 4coder_custom.so: %s\n", error ? error : "dlopen failed."); + exit(1); } - +#else + linuxvars.custom_api.get_bindings = get_bindings; #endif - if (linuxvars.custom_api.get_bindings == 0){ - linuxvars.custom_api.get_bindings = get_bindings; - } + linuxvars.custom_api.view_routine = 0; #if 0 if (linuxvars.custom_api.view_routine == 0){ diff --git a/linux_font.cpp b/linux_font.cpp index 38694826..e5f38d09 100644 --- a/linux_font.cpp +++ b/linux_font.cpp @@ -63,7 +63,7 @@ linux_font_load(Partition *part, Render_Font *rf, char *name, i32 pt_size, i32 t #else char* filename = push_array(part, char, 256); if (filename != 0){ - String str = make_string(filename, 0, 256); + String str = make_string_cap(filename, 0, 256); sysshared_to_binary_path(&str, name); } #endif