src/server/daemon/config.c

branch
config
changeset 255
b5d15a4a19f5
parent 254
4784c14aa639
child 256
19259b6c5cf7
--- a/src/server/daemon/config.c	Sun Aug 23 23:04:17 2020 +0200
+++ b/src/server/daemon/config.c	Mon Aug 24 12:50:16 2020 +0200
@@ -55,10 +55,8 @@
 #include "../util/atomic.h"
 #include "ucx/buffer.h"
 
-pool_handle_t *cfg_pool;
+pool_handle_t *init_pool;
 
-// TODO: Funktion für ConfigDirective -> directive
-// TODO: Funktion für UcxList parameter list -> pblock
 
 int load_init_conf(char *file) {
     log_ereport(LOG_VERBOSE, "load_init_conf");
@@ -70,14 +68,14 @@
     }
     UcxAllocator *mp = cfg->parser.mp;
 
-    cfg_pool = pool_create(); // one pool for one Configuration
+    init_pool = pool_create(); // one pool for one Configuration
     UcxList *dirs = cfg->directives;
     while(dirs != NULL) {
         ConfigDirective *dir = dirs->data;
 
         /* create NSAPI directive */
         directive *d = malloc(sizeof(directive));
-        d->param = pblock_create_pool(cfg_pool, 8);
+        d->param = pblock_create_pool(init_pool, 8);
         UcxList *param = cfg_param_list(dir->value, mp);
         while(param != NULL) {
             ConfigParam *p = param->data;
@@ -127,19 +125,27 @@
     return 0;
 }
 
-ServerConfiguration* load_server_conf(ServerConfiguration *old, char *file) {
+ServerConfiguration* load_server_conf(char *file) {
     log_ereport(LOG_VERBOSE, "load_server_conf");
 
-    ServerConfig2 *serverconf = load_server_config(file);
-    if(serverconf == NULL) {
+    ServerConfig2 *serverconf2 = load_server_config(file);
+    if(serverconf2 == NULL) {
         log_ereport(LOG_FAILURE, "Cannot load server.conf");
     }
-    ServerConfiguration *serverconfig = calloc(1, sizeof(ServerConfiguration));
+    pool_handle_t *pool = pool_create();
+    
+    
+    ServerConfiguration *serverconfig = pool_calloc(pool, 1, sizeof(ServerConfiguration));
     serverconfig->ref = 1;
-    serverconfig->pool = pool_create();
+    serverconfig->pool = pool;
     serverconfig->listeners = NULL;
     serverconfig->host_vs = ucx_map_new(16);
     serverconfig->authdbs = ucx_map_new(16);
+    
+    UcxAllocator allocator = util_pool_allocator(serverconfig->pool);
+    serverconfig->a = pool_malloc(pool, sizeof(UcxAllocator));
+    *serverconfig->a = allocator;
+    
     // TODO: init serverconfig stuff
     
     
@@ -161,7 +167,7 @@
      */
     
     // init logfile first
-    UcxList *lfl = ucx_map_sstr_get(serverconf->objects, sstrn("LogFile", 7));
+    UcxList *lfl = ucx_map_sstr_get(serverconf2->objects, sstrn("LogFile", 7));
     if(lfl != NULL) {
         ServerConfigObject *logobj = lfl->data;
         if(logobj == NULL) {
@@ -179,7 +185,7 @@
         return NULL;
     }
      
-    UcxList *list = ucx_map_sstr_get(serverconf->objects, sstrn("Runtime", 7));
+    UcxList *list = ucx_map_sstr_get(serverconf2->objects, sstrn("Runtime", 7));
     UCX_FOREACH(elm, list) {
         ServerConfigObject *scfgobj = elm->data;
         if(cfg_handle_runtime(serverconfig, scfgobj)) {
@@ -188,7 +194,7 @@
         }
     }
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("Threadpool", 10));
+    list = ucx_map_sstr_get(serverconf2->objects, sstrn("Threadpool", 10));
     UCX_FOREACH(elm, list) {
         if(cfg_handle_threadpool(serverconfig, elm->data)) {
             return NULL;
@@ -200,7 +206,7 @@
         return NULL;
     }
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("EventHandler", 12));
+    list = ucx_map_sstr_get(serverconf2->objects, sstrn("EventHandler", 12));
     UCX_FOREACH(elm, list) {
         if(cfg_handle_eventhandler(
                 serverconfig, (ServerConfigObject*)elm->data)) {
@@ -214,7 +220,7 @@
         return NULL;
     }
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("AccessLog", 9));
+    list = ucx_map_sstr_get(serverconf2->objects, sstrn("AccessLog", 9));
     UCX_FOREACH(elm, list) {
         ServerConfigObject *scfgobj = elm->data;
         if(cfg_handle_accesslog(serverconfig, scfgobj)) {
@@ -222,7 +228,7 @@
         }
     }
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("AuthDB", 6));
+    list = ucx_map_sstr_get(serverconf2->objects, sstrn("AuthDB", 6));
     UCX_FOREACH(elm, list) {
         ServerConfigObject *scfgobj = elm->data;
         if(cfg_handle_authdb(serverconfig, scfgobj)) {
@@ -230,7 +236,7 @@
         }
     }
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("Listener", 8));
+    list = ucx_map_sstr_get(serverconf2->objects, sstrn("Listener", 8));
     UCX_FOREACH(elm, list) {
         ServerConfigObject *scfgobj = elm->data;
         if(cfg_handle_listener(serverconfig, scfgobj)) {
@@ -238,7 +244,7 @@
         }
     }
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("VirtualServer", 13));
+    list = ucx_map_sstr_get(serverconf2->objects, sstrn("VirtualServer", 13));
     UCX_FOREACH(elm, list) {
         ServerConfigObject *scfgobj = elm->data;
         if(cfg_handle_vs(serverconfig, scfgobj)) {
@@ -268,7 +274,7 @@
         ls = ls->next;
     }
     
-    free_server_config(serverconf);
+    free_server_config(serverconf2);
     return serverconfig;
 }
 
@@ -279,8 +285,7 @@
 void cfg_unref(ServerConfiguration *cfg) {
     uint32_t ref = ws_atomic_dec32(&cfg->ref);
     if(ref == 0) {
-        // TODO: free configuration
-        printf("free ServerConfiguration %"PRIxPTR"\n", (intptr_t)cfg);
+        pool_destroy(cfg->pool);
     }
 }
 
@@ -307,28 +312,10 @@
     sstr_t base = sstr("config/"); 
     sstr_t file = sstrcat(2, base, mf);
     
-    ConfigFile *f = cfgmgr_get_file(file);
-    if(f == NULL) {
-        f = malloc(sizeof(ConfigFile));
-        f->data = NULL;
-        f->file = sstrdup(file);
-        f->reload = mime_conf_reload;
-        f->last_modified = 0;
-        
-        // load the file content
-        //f->reload(f, cfg);
-        if(cfgmgr_reload_file(f, cfg, NULL)) {
-            free(f->file.ptr);
-            free(f);
-            
-            free(file.ptr);
-            return -1;
-        }
-        cfgmgr_attach_file(f);
+    if(mime_conf_load(cfg, file)) {
+        return -1;
     }
     
-    cfg->mimetypes = f->data;
-    
     free(file.ptr);
     return 0;
 }
@@ -472,6 +459,8 @@
     sstr_t name = cfg_directivelist_get_str(obj->directives, sstr("Name"));
     sstr_t type = cfg_directivelist_get_str(obj->directives, sstr("Type"));
     
+    AuthDB *authdb = NULL;
+    
     if(!sstrcmp(type, sstr("ldap"))) {
         LDAPConfig conf;
         
@@ -505,8 +494,7 @@
         
         name = sstrdup(name);
         
-        AuthDB *authdb = create_ldap_authdb(name.ptr, &conf);
-        ucx_map_sstr_put(cfg->authdbs, name, authdb);
+        authdb = create_ldap_authdb(name.ptr, &conf);
         
         // TODO: create_ldap_authdb should copy the strings
         /*
@@ -531,28 +519,15 @@
         }
         
         // load keyfile
-        ConfigFile *f = cfgmgr_get_file(file);
-        if(f == NULL) {
-            f = malloc(sizeof(ConfigFile));
-            f->data = NULL;
-            f->file = sstrdup(file);
-            f->reload = keyfile_reload;
-            f->last_modified = 0;
-            //f->reload(f, cfg);
-            if(cfgmgr_reload_file(f, cfg, NULL)) {
-                free(f->file.ptr);
-                free(f);
-                return -1;
-            }
-            cfgmgr_attach_file(f);
-        }
-        
-        // add keyfile authdb
-        Keyfile *keyfile = f->data;
-        keyfile->authdb.name = sstrdup(name).ptr;
-        ucx_map_sstr_put(cfg->authdbs, name, keyfile);
+        authdb = keyfile_load(cfg, file);
     }
 
+    if(authdb) {
+        if(ucx_map_sstr_put(cfg->authdbs, name, authdb)) {
+            return -1;
+        }
+    }
+    
     return 0;
 }
 
@@ -656,56 +631,31 @@
     // load the object config file
     sstr_t base = sstr("config/");
     sstr_t file = sstrcat(2, base, objfile);
-    file = sstrcat(2, base, objfile);
+    // sstrcat with allocator because we want to keep the string
+    file = sstrcat_a(cfg->a, 2, base, objfile);
 
-    // the file is managed by the configuration manager
-    ConfigFile *f = cfgmgr_get_file(file);
-    if(f == NULL) {
-        f = malloc(sizeof(ConfigFile));
-        f->data = NULL;
-        f->file = sstrdup(file);
-        f->reload = object_conf_reload;
-        f->last_modified = 0;
-        //f->reload(f, cfg);
-        if(cfgmgr_reload_file(f, cfg, NULL)) {
-            free(f->file.ptr);
-            free(f);
-            
-            free(file.ptr);
-            return -1;
-        }
-        cfgmgr_attach_file(f);
+    HTTPObjectConfig *httpobj = objconf_load(cfg, file);
+    if(!httpobj) {
+        return -1;
     }
-    vs->objectfile = sstrdup(file);
-    vs->objects = (HTTPObjectConfig*)f->data;
-    free(file.ptr);
+    vs->objectfile = file;
+    vs->objects = httpobj;
     
     
     // load acl config file
     file = sstrcat(2, base, aclfile);
     
-    ConfigFile *aclf = cfgmgr_get_file(file);
-    if(aclf == NULL) {
-        aclf = malloc(sizeof(ConfigFile));
-        aclf->data = NULL;
-        aclf->file = sstrdup(file);
-        aclf->reload = acl_conf_reload;
-        aclf->last_modified = 0;
-        //aclf->reload(aclf, cfg);
-        if(cfgmgr_reload_file(aclf, cfg, NULL)) {
-            free(aclf->file.ptr);
-            free(aclf);
-            
-            free(file.ptr);
-            return -1;
-        }
-        cfgmgr_attach_file(aclf);
+    ACLData *acldata = acl_conf_load(cfg, file);
+    if(!acldata) {
+        return -1;
     }
-    vs->acls = aclf->data;
+    vs->acls = acldata;
+    
     free(file.ptr);
     
+    
     // set the access log for the virtual server
-    // TODO: don't use always the default
+    // TODO: don't always use the default
     vs->log = cfg->default_log;
 
     ucx_map_sstr_put(cfg->host_vs, vs->host, vs);
@@ -714,53 +664,8 @@
 }
 
 
-int object_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
-    HTTPObjectConfig *old_conf = file->data;
-    file->data = load_obj_conf(file->file.ptr);
-    if(old_conf) {
-        object_conf_unref(old_conf);
-    }
-    if(file->data) {
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-void object_conf_ref(HTTPObjectConfig *conf) {
-    if(conf) {
-        ws_atomic_inc32(&conf->ref);
-    }
-}
-
-void object_conf_unref(HTTPObjectConfig *conf) {
-    uint32_t ref = ws_atomic_dec32(&conf->ref);
-    if(ref == 0) {
-        printf("free HTTPObjectConfig %"PRIxPTR"\n", (intptr_t)conf);
-        pool_destroy(conf->pool);
-    }
-}
-
-HTTPObjectConfig* load_obj_conf(char *file) {
-    log_ereport(LOG_VERBOSE, "load_obj_conf");
-
-    // new conf function test
-    ObjectConfig *cfg = load_object_config(file);
-    UcxAllocator *mp = cfg->parser.mp;
-    if(cfg == NULL) {
-        return NULL;
-    }
-
-    // create object config
-    pool_handle_t *pool = pool_create();
-    HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1);
-    conf->pool = pool;
-
-    // convert ObjectConfig to HTTPObjectConfig
-
-    // add objects
-    conf->nobj = ucx_list_size(cfg->objects);
-    conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*));
+static int convert_objconf(ServerConfiguration *scfg, ObjectConfig *cfg, HTTPObjectConfig *conf, sstr_t file) {
+    pool_handle_t *pool = conf->pool;
     
     UcxList *objlist = cfg->objects;
     int i = 0;
@@ -772,13 +677,16 @@
         char *ppath = NULL;
         if(cob->name.length > 0) {
             name = sstrdup_pool(pool, cob->name).ptr;
+            if(!name) return -1;
         }
         if(cob->ppath.length > 0) {
             ppath = sstrdup_pool(pool, cob->ppath).ptr;
+            if(!ppath) return -1;
         }
 
         // create and add object
         httpd_object *obj = object_new(pool, name);
+        if(!obj) return -1;
         obj->path = NULL;
 
         conf->objects[i] = obj;
@@ -788,8 +696,9 @@
             UcxList *dirs = cob->directives[j];
             while(dirs != NULL) {
                 ConfigDirective *cfgdir = dirs->data;
-                
+
                 directive *d = pool_malloc(pool, sizeof(directive));
+                if(!d) return -1;
                 if(cfgdir->condition) {
                     sstr_t expr = cfgdir->condition->param_str;
                     d->cond = condition_from_str(pool, expr.ptr, expr.length);
@@ -799,7 +708,7 @@
                 d->param = pblock_create_pool(pool, 8);
 
                 // add params
-                UcxList *param = cfg_param_list(cfgdir->value, mp);
+                UcxList *param = cfg_param_list(cfgdir->value, scfg->a);
                 while(param != NULL) {
                     ConfigParam *p = param->data;
                     pblock_nvlinsert(
@@ -814,13 +723,13 @@
                 // get function
                 char *func_name = pblock_findval("fn", d->param);
                 if(!func_name) {
-                    log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file);
-                    return NULL;
+                    log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file.ptr);
+                    return -1;
                 }
                 d->func = get_function(func_name);
                 if(!d->func) {
                     log_ereport(LOG_MISCONFIG, "func %s not found", func_name);
-                    return NULL;
+                    return -1;
                 }
 
                 dirs = dirs->next;
@@ -834,66 +743,94 @@
         i++;
         objlist = objlist->next;
     }
+    
+    return 0;
+}
+
+HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, sstr_t file) {
+    log_ereport(LOG_VERBOSE, "load_obj_conf");
+    
+    int ret = 0;
+
+    // create object config
+    pool_handle_t *pool = scfg->pool;
+    HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1);
+    if(!conf) {
+        return NULL;
+    }
+    conf->pool = pool;
+    
+    // load obj config file
+    ObjectConfig *cfg = load_object_config(file.ptr);
+    if(!cfg) {
+        return NULL;
+    }
+
+    // convert ObjectConfig to HTTPObjectConfig
+
+    // add objects
+    conf->nobj = ucx_list_size(cfg->objects);
+    conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*));
+    if(conf->objects) {
+        ret = convert_objconf(scfg, cfg, conf, file);
+    } else {
+        ret = -1;
+    }
 
     free_object_config(cfg);
 
-    return conf;
+    return !ret ? conf : NULL;
 }
 
-int mime_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
-    MimeConfig *mimecfg = load_mime_config(file->file.ptr);
-    MimeMap *old_conf = file->data;
-    
-    MimeMap *mimemap = malloc(sizeof(MimeMap));
-    mimemap->ref = 1;
-    UcxMap *map = ucx_map_new((mimecfg->ntypes * 3) / 2);
-    mimemap->map = map;
-    
-    // add ext type pairs
-    UCX_FOREACH(md, mimecfg->directives) {
-        MimeDirective *d = md->data;
-        // add the type for each extension to the map
-        UCX_FOREACH(xl, d->exts) {
-            sstr_t ext = sstr(xl->data);
-            sstr_t value = sstrdup(d->type);
-            ucx_map_sstr_put(map, ext, value.ptr);
-        }
+int mime_conf_load(ServerConfiguration *cfg, sstr_t file) {
+    MimeConfig *mimecfg = load_mime_config(file.ptr);
+    if(!mimecfg) {
+        return -1;
     }
     
-    file->data = mimemap;
+    int ret = 0;
+    
+    // cleanup in case of errors is done by the allocator
+    MimeMap *mimemap = almalloc(cfg->a, sizeof(MimeMap));
+    UcxMap *map = ucx_map_new_a(cfg->a, (mimecfg->ntypes * 3) / 2);
     
-    if(old_conf) {
-        mime_conf_unref(old_conf);
+    if(mimemap && map) {
+        mimemap->map = map;
+        
+        // add ext type pairs
+        UCX_FOREACH(md, mimecfg->directives) {
+            MimeDirective *d = md->data;
+            // add the type for each extension to the map
+            UCX_FOREACH(xl, d->exts) {
+                sstr_t ext = sstr(xl->data);
+                sstr_t value = sstrdup(d->type);
+                if(ucx_map_sstr_put(map, ext, value.ptr)) {
+                    ret = -1;
+                    break;
+                }
+            }
+            if(ret) {
+                break;
+            }
+        }
+        
+        cfg->mimetypes = mimemap;
+    } else {
+        ret = -1;
     }
     
     free_mime_config(mimecfg);
-    return 0;
-}
-
-void mime_conf_ref(MimeMap *conf) {
-    if(conf) {
-        ws_atomic_inc32(&conf->ref);
-    }
+    return ret;
 }
 
-void mime_conf_unref(MimeMap *conf) {
-    uint32_t ref = ws_atomic_dec32(&conf->ref);
-    if(ref == 0) {
-        printf("free MimeConfig %"PRIxPTR"\n", (intptr_t)conf);
-        UcxMapIterator i = ucx_map_iterator(conf->map);
-        char *str;
-        UCX_MAP_FOREACH(key, str, i) {
-            free(str);
-        }
-        ucx_map_free(conf->map);
-        free(conf);
-    }
-}
+
 
-int acl_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
-    ACLFile *aclfile = load_acl_file(file->file.ptr);
+ACLData* acl_conf_load(ServerConfiguration *cfg, sstr_t file) {
+    ACLFile *aclfile = load_acl_file(file.ptr);
     
-    ACLData *acldata = acl_data_new();
+    // TODO: malloc return checks
+    
+    ACLData *acldata = acl_data_new(cfg->a);
     UCX_FOREACH(elm, aclfile->namedACLs) {
         ACLConfig *ac = elm->data;
         ACLList *acl = acl_config_convert(cfg, ac);
@@ -902,17 +839,13 @@
     }
     free_acl_file(aclfile);
     
-    ACLData *old_data = file->data;
-    file->data = acldata;
-    if(old_data) {
-        acl_data_unref(old_data);
-    }
-    
-    return 0;
+    return acldata;
 }
 
 ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) {
-    WSAcl *acllist = malloc(sizeof(WSAcl));
+    UcxAllocator *a = cfg->a;
+    
+    WSAcl *acllist = almalloc(cfg->a, sizeof(WSAcl));
     acllist->acl.check = (acl_check_f)wsacl_check;
     acllist->acl.authdb = NULL;
     acllist->acl.authprompt = NULL;
@@ -925,8 +858,8 @@
     }
     
     size_t s = ucx_list_size(acl->entries);
-    WSAce **aces = calloc(s, sizeof(WSAce*));
-    WSAce **eces = calloc(s, sizeof(WSAce*));
+    WSAce **tmp_aces = calloc(s, sizeof(WSAce*));
+    WSAce **tmp_eces = calloc(s, sizeof(WSAce*));
     int ai = 0;
     int ei = 0;
     
@@ -935,36 +868,36 @@
         ACEConfig *acecfg = elm->data;
         
         // copy data
-        WSAce *ace = malloc(sizeof(WSAce));
+        WSAce *ace = almalloc(a, sizeof(WSAce));
         ace->access_mask = acecfg->access_mask;
         ace->flags = acecfg->flags;
         ace->type = acecfg->type;
-        ace->who = sstrdup(acecfg->who).ptr;
+        ace->who = sstrdup_a(a, acecfg->who).ptr;
         
         // add the entry to the correct array
         if(ace->type >= ACL_TYPE_AUDIT) {
-            eces[ei] = ace;
+            tmp_eces[ei] = ace;
             ei++;
         } else {
-            aces[ai] = ace;
+            tmp_aces[ai] = ace;
             ai++;
         }
     }
     
     // create new entrie arrays with perfect fitting size
     if(ai > 0) {
-        acllist->ace = calloc(ai, sizeof(WSAce*));
+        acllist->ace = alcalloc(a, ai, sizeof(WSAce*));
     }
     if(ei > 0) {
-        acllist->ece = calloc(ei, sizeof(WSAce*));
+        acllist->ece = alcalloc(a, ei, sizeof(WSAce*));
     }
-    memcpy(acllist->ace, aces, ai*sizeof(WSAce*));
-    memcpy(acllist->ece, eces, ei*sizeof(WSAce*));
+    memcpy(acllist->ace, tmp_aces, ai*sizeof(WSAce*));
+    memcpy(acllist->ece, tmp_eces, ei*sizeof(WSAce*));
     acllist->acenum = ai;
     acllist->ecenum = ei;
     
-    free(aces);
-    free(eces);
+    free(tmp_aces);
+    free(tmp_eces);
     
     // get authentication information
     if(acl->authparam) {
@@ -975,7 +908,7 @@
             AuthDB *authdb = ucx_map_sstr_get(cfg->authdbs, authdb_str);
             acllist->acl.authdb = authdb;
             if(authdb && prompt_str.ptr) {
-                acllist->acl.authprompt = sstrdup(prompt_str).ptr;
+                acllist->acl.authprompt = sstrdup_a(a, prompt_str).ptr;
             }
         }
     }
@@ -983,69 +916,35 @@
     return &acllist->acl;
 }
 
-int keyfile_reload(ConfigFile *file, ServerConfiguration *cfg) {
-    KeyfileConfig *conf = load_keyfile_config(file->file.ptr);
-    if(!conf) {
-        return 1;
+AuthDB* keyfile_load(ServerConfiguration *cfg, sstr_t file) {
+    Keyfile *keyfile = keyfile_new(cfg->a);
+    if(!keyfile) {
+        return NULL;
     }
     
-    Keyfile *keyfile = keyfile_new();
+    KeyfileConfig *conf = load_keyfile_config(file.ptr);
+    if(!conf) {
+        return NULL;
+    }
+    
+    AuthDB *ret = &keyfile->authdb;
     
     UCX_FOREACH(elm, conf->users) {
         KeyfileEntry *user = elm->data;
-        keyfile_add_user(
+        if(keyfile_add_user(
                 keyfile,
                 user->name,
                 user->hashtype,
                 user->hashdata,
                 user->groups,
-                user->numgroups);
+                user->numgroups))
+        {
+            ret = NULL;
+            break;
+        }
     }
     
     free_keyfile_config(conf);
     
-    Keyfile *old_data = file->data;
-    file->data = keyfile;
-    if(old_data) {
-        keyfile_unref(old_data);
-    }
-    
-    return 0;
+    return ret;
 }
-
-
-sstr_t cfg_load_file(sstr_t file) {
-    sstr_t r;
-    r.ptr = NULL;
-    r.length = 0;
-    
-    if(!file.ptr) {
-        return r;
-    }
-    
-    sstr_t f = sstrdup(file);
-    FILE *in = fopen(f.ptr, "r");
-    if(!in) {
-        return r;
-    }
-    
-    UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
-    if(!buf) {
-        fclose(in);
-        return r;
-    }
-    
-    if(ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write) == 0) {
-        fclose(in);
-        ucx_buffer_free(buf);
-        return r;
-    }
-    
-    r.ptr = buf->space;
-    r.length = buf->pos;
-    
-    free(buf);
-    fclose(in);
-    
-    return r;
-}

mercurial