linux make it build and update filetrack system

filetrack now tracks directories like windows, which handles
moves/deletes of files better.
master
insofaras 2016-08-31 00:33:10 +01:00
parent c645b9a57b
commit 6f836eab45
4 changed files with 125 additions and 117 deletions

View File

@ -3,7 +3,7 @@ C_FILES := $(wildcard *.c) $(wildcard **/*.c)
H_FILES := $(wildcard *.h) $(wildcard **/*.h) H_FILES := $(wildcard *.h) $(wildcard **/*.h)
WARNINGS := -Wno-write-strings WARNINGS := -Wno-write-strings
PLAT_LINKS := -L/usr/local/lib -lX11 -lpthread -lm -lrt -lGL -ldl -lXfixes -lfreetype -lfontconfig 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 # main stuff

View File

@ -12,61 +12,22 @@
#include "4tech_file_track.h" #include "4tech_file_track.h"
#include "4tech_file_track_general.c" #include "4tech_file_track_general.c"
#include <libgen.h> // dirname
// 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
//
typedef struct { typedef struct {
void *tables; 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; int inotify;
pthread_mutex_t lock; pthread_mutex_t lock;
char *string_mem_begin; char *string_mem_begin;
char *string_mem_end; char *string_mem_end;
} Linux_File_Track_Vars; } Linux_File_Track_Vars;
//static_assert(sizeof(Linux_File_Track_Vars) <= sizeof(File_Track_System));
typedef struct { typedef struct {
File_Index hash; File_Index hash;
// NOTE(allen): char* dir;
// This struct must fit inside the opaque File_Track_Entry struct. int ref_count;
// 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;
} Linux_File_Track_Entry; } 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_vars(s) ((Linux_File_Track_Vars*)(s))
#define to_tables(v) ((File_Track_Tables*)(v->tables)) #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 *table_memory, int32_t table_memory_size,
void *listener_memory, int32_t listener_memory_size){ void *listener_memory, int32_t listener_memory_size){
File_Track_Result result = FileTrack_MemoryTooSmall; File_Track_Result result = FileTrack_MemoryTooSmall;
Assert(sizeof(Linux_File_Track_Vars) <= sizeof(File_Track_System));
Linux_File_Track_Vars *vars = to_vars(system); Linux_File_Track_Vars *vars = to_vars(system);
Assert(sizeof(Linux_File_Track_Entry) <= sizeof(File_Track_Entry)); Assert(sizeof(Linux_File_Track_Entry) <= sizeof(File_Track_Entry));
if (enough_memory_to_init_table(table_memory_size) && if (enough_memory_to_init_table(table_memory_size)){
/*if listener memory is important check it's size here*/ 1){
// NOTE(allen): Initialize main data tables // NOTE(allen): Initialize main data tables
vars->tables = (File_Track_Tables*) table_memory; vars->tables = (File_Track_Tables*) table_memory;
@ -110,21 +73,13 @@ add_listener(File_Track_System *system, char *filename){
pthread_mutex_lock(&vars->lock); 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)){ if(tracking_system_has_space(tables, 1)){
size_t filename_len = strlen(filename) + 1; char *dir = dirname(strdupa(filename));
if(vars->string_mem_end - vars->string_mem_begin >= filename_len){ 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){ if(wd != -1){
File_Index key = { wd, 1 }; File_Index key = { wd, 1 };
File_Track_Entry *entry = tracking_system_lookup_entry(tables, key); 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); LINUX_FN_DEBUG("map %s to wd %d", filename, wd);
Assert(entry_is_available(entry)); if(entry_is_available(entry)){
linux_entry->hash = key;
linux_entry->hash = key; linux_entry->dir = vars->string_mem_begin;
linux_entry->filename = vars->string_mem_begin; linux_entry->ref_count = 1;
memcpy(vars->string_mem_begin, dir, dir_len);
memcpy(vars->string_mem_begin, filename, filename_len); vars->string_mem_begin += dir_len;
vars->string_mem_begin += filename_len; ++tables->tracked_count;
} else {
++tables->tracked_count; LINUX_FN_DEBUG("dir already tracked, adding ref");
++linux_entry->ref_count;
}
} else { } else {
result = FileTrack_FileSystemError; result = FileTrack_FileSystemError;
} }
@ -167,21 +124,22 @@ remove_listener(File_Track_System *system, char *filename){
pthread_mutex_lock(&vars->lock); pthread_mutex_lock(&vars->lock);
// NOTE(allen): char *dir = dirname(strdupa(filename));
// Here do something to stop listening to changes to the file named filename. // NOTE(inso): this assumes the filename was previously added
// 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.
for(uint32_t i = 0; i < tables->max; ++i){ for(uint32_t i = 0; i < tables->max; ++i){
Linux_File_Track_Entry *e = (Linux_File_Track_Entry*)(entries + i); Linux_File_Track_Entry *e = (Linux_File_Track_Entry*)(entries + i);
if(e->hash.id[1] != 1) continue; 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]); LINUX_FN_DEBUG("%s found as wd %d", filename, e->hash.id[0]);
if(inotify_rm_watch(vars->inotify, e->hash.id[0]) == -1){ if(--e->ref_count == 0){
perror("inotify_rm_watch"); LINUX_FN_DEBUG("refcount == 0, calling rm_watch");
result = FileTrack_FileSystemError; 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 // NOTE(inso): associated string memory in listeners would be freed here
break; break;
} }
@ -223,11 +181,6 @@ expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size
pthread_mutex_lock(&vars->lock); 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. // NOTE(inso): pointer to old string mem is lost here.
// would need to keep it around if we want to free in the future // 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); pthread_mutex_lock(&vars->lock);
// NOTE(allen): If there are any new file changes report them struct inotify_event *ev;
// by copying the name of the file to the buffer and returning char buff[sizeof(*ev) + NAME_MAX + 1] __attribute__((aligned(__alignof__(*ev))));
// 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; // 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){ if(n == -1 && errno != EAGAIN){
perror("inotify read"); perror("inotify read");
} else if(n > 0){ } 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); File_Track_Entry *entry = tracking_system_lookup_entry(tables, key);
Linux_File_Track_Entry *linux_entry = (Linux_File_Track_Entry*) entry; Linux_File_Track_Entry *linux_entry = (Linux_File_Track_Entry*) entry;
LINUX_FN_DEBUG("mask: %#x", ev->mask);
if(!entry_is_available(entry)){ 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); char* full_name = (char*) alloca(strlen(linux_entry->dir) + ev->len + 1);
if(max < filename_size){ 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; result = FileTrack_MemoryTooSmall;
// NOTE(inso): this event will be dropped, needs to be stashed. // NOTE(inso): this event will be dropped, needs to be stashed.
LINUX_FN_DEBUG("max too small, event dropped"); LINUX_FN_DEBUG("max too small, event dropped");
} else { } else {
memcpy(buffer, linux_entry->filename, filename_size); memcpy(buffer, full_name, full_name_size);
*size = filename_size; *size = full_name_size;
result = FileTrack_Good; result = FileTrack_Good;
} }
} else { } else {
LINUX_FN_DEBUG("dead event from wd %d", ev.wd); LINUX_FN_DEBUG("dead event from wd %d", ev->wd);
} }
} }

View File

@ -10,18 +10,54 @@
// TOP // TOP
#include "4coder_default_bindings.cpp" # include "4ed_defines.h"
#undef exec_command #if FRED_SUPER
#undef exec_command_keep_stack
#undef clear_parameters
#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 <assert.h>
#include "4coder_string.h"
# 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" #include "4ed_math.h"
@ -464,15 +500,20 @@ Sys_Get_Canonical_Sig(system_get_canonical){
int32_t result = 0; int32_t result = 0;
char* path = realpath(strndupa(filename, len), NULL); char* path = realpath(strndupa(filename, len), NULL);
if(!path){ if(path){
perror("realpath");
} else {
size_t path_len = strlen(path); size_t path_len = strlen(path);
if(max >= path_len){ if(max >= path_len){
memcpy(buffer, path, path_len); memcpy(buffer, path, path_len);
result = 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); free(path);
} else {
LINUX_FN_DEBUG("[%.*s] -> [null]", len, filename);
} }
return result; return result;
@ -670,7 +711,7 @@ FILE_EXISTS_SIG(system_file_exists){
internal internal
DIRECTORY_CD_SIG(system_directory_cd){ DIRECTORY_CD_SIG(system_directory_cd){
String directory = make_string(dir, *len, capacity); String directory = make_string_cap(dir, *len, capacity);
b32 result = 0; b32 result = 0;
i32 old_size; i32 old_size;
@ -685,8 +726,8 @@ DIRECTORY_CD_SIG(system_directory_cd){
else{ else{
if (directory.size + rel_len + 1 > directory.memory_size){ if (directory.size + rel_len + 1 > directory.memory_size){
old_size = directory.size; old_size = directory.size;
append_partial(&directory, rel_path); append_partial_sc(&directory, rel_path);
append_partial(&directory, "/"); append_s_char(&directory, '/');
terminate_with_null(&directory); terminate_with_null(&directory);
struct stat st; struct stat st;
@ -709,7 +750,7 @@ DIRECTORY_CD_SIG(system_directory_cd){
internal internal
GET_4ED_PATH_SIG(system_get_4ed_path){ 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)); return(system_get_binary_path(&str));
} }
@ -2827,6 +2868,7 @@ main(int argc, char **argv)
if (linuxvars.custom_api.get_alpha_4coder_version == 0 || if (linuxvars.custom_api.get_alpha_4coder_version == 0 ||
linuxvars.custom_api.get_alpha_4coder_version(MAJOR, MINOR, PATCH) == 0){ linuxvars.custom_api.get_alpha_4coder_version(MAJOR, MINOR, PATCH) == 0){
fprintf(stderr, "*** Failed to use 4coder_custom.so: version mismatch ***\n"); fprintf(stderr, "*** Failed to use 4coder_custom.so: version mismatch ***\n");
exit(1);
} }
else{ else{
linuxvars.custom_api.get_bindings = (Get_Binding_Data_Function*) 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){ if (linuxvars.custom_api.get_bindings == 0){
fprintf(stderr, "*** Failed to use 4coder_custom.so: get_bindings not exported ***\n"); fprintf(stderr, "*** Failed to use 4coder_custom.so: get_bindings not exported ***\n");
exit(1);
} }
else{ else{
fprintf(stderr, "Successfully loaded 4coder_custom.so\n"); fprintf(stderr, "Successfully loaded 4coder_custom.so\n");
@ -2844,13 +2887,13 @@ main(int argc, char **argv)
} else { } else {
const char* error = dlerror(); const char* error = dlerror();
fprintf(stderr, "*** Failed to load 4coder_custom.so: %s\n", error ? error : "dlopen failed."); 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 #endif
if (linuxvars.custom_api.get_bindings == 0){ linuxvars.custom_api.view_routine = 0;
linuxvars.custom_api.get_bindings = get_bindings;
}
#if 0 #if 0
if (linuxvars.custom_api.view_routine == 0){ if (linuxvars.custom_api.view_routine == 0){

View File

@ -63,7 +63,7 @@ linux_font_load(Partition *part, Render_Font *rf, char *name, i32 pt_size, i32 t
#else #else
char* filename = push_array(part, char, 256); char* filename = push_array(part, char, 256);
if (filename != 0){ if (filename != 0){
String str = make_string(filename, 0, 256); String str = make_string_cap(filename, 0, 256);
sysshared_to_binary_path(&str, name); sysshared_to_binary_path(&str, name);
} }
#endif #endif