From 30a92ababaa9148a7354ebf52bf318d37b5451d2 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Sat, 8 Mar 2014 19:00:57 -0500
Subject: [PATCH 2/4] [INI] New entry to read data from mem

The commit adds a new entry point to read configuration
data from a memory buffer instead of file.
---
 ini/ini_configobj.h   |   25 ++++++
 ini/ini_fileobj.c     |  204 +++++++++++++++++++++++++++++++++++++------------
 ini/libini_config.sym |    1 +
 3 files changed, 182 insertions(+), 48 deletions(-)

diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
index 9a4eb58..4ef98dc 100644
--- a/ini/ini_configobj.h
+++ b/ini/ini_configobj.h
@@ -483,6 +483,31 @@ int ini_config_file_open(const char *filename,
                          struct ini_cfgfile **file_ctx);
 
 /**
+ * @brief Create a configuration file object using memory buffer.
+ *
+ * Create a file object for parsing a configuration file.
+ * Configuration will use provided memory instead of the actual file.
+ *
+ * A "configuration file object" is different from
+ * a "configuration object". The former stores metadata
+ * about a file the configuration data is read from,
+ * while the latter holds the configuration itself.
+ *
+ * @param[in]  data_buf         In memory configuration data.
+ *                              Needs to be NULL terminated.
+ * @param[in]  data_len         Length of memory data
+ *                              not including terminating NULL.
+ * @param[out] file_ctx         Configuration file object.
+ *
+ * @return 0 - Success.
+ * @return EINVAL - Invalid parameter.
+ * @return ENOMEM - No memory.
+ */
+int ini_config_file_from_mem(void *data_buf,
+                             uint32_t data_len,
+                             struct ini_cfgfile **file_ctx);
+
+/**
  * @brief Close configuration file after parsing
  *
  * Closes file but keeps the context. File can be reopened
diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
index d87956a..fbb1a81 100644
--- a/ini/ini_fileobj.c
+++ b/ini/ini_fileobj.c
@@ -46,6 +46,12 @@ enum index_utf_t {
     INDEX_UTF8 = 4
 };
 
+/* Trick to work around the fact that
+ * fmemopen failes if buffer is 0 length
+ */
+unsigned char alt_buffer[2] = {0, 0};
+uint32_t alt_buffer_len = 1;
+
 /* Close file but not destroy the object */
 void ini_config_file_close(struct ini_cfgfile *file_ctx)
 {
@@ -138,7 +144,7 @@ static enum index_utf_t check_bom(enum index_utf_t ind,
     return ind;
 }
 
-static int read_chunk(int raw_file, size_t left, size_t increment,
+static int read_chunk(FILE *file, size_t left, size_t increment,
                       char *position, size_t *read_num)
 {
     int error = EOK;
@@ -149,22 +155,28 @@ static int read_chunk(int raw_file, size_t left, size_t increment,
 
     to_read = how_much_to_read(left, increment);
 
-    TRACE_INFO_NUMBER("About to read", to_read);
-    errno = 0;
-    read_cnt = read(raw_file, position, to_read);
-    if (read_cnt == -1) {
-        error = errno;
-        TRACE_ERROR_NUMBER("Failed to read data from file", error);
-        return error;
-    }
+    TRACE_INFO_NUMBER("About to read", (unsigned int)to_read);
 
-    if (read_cnt != to_read) {
-        error = EIO;
-        TRACE_ERROR_NUMBER("Read less than required", error);
-        return error;
+    read_cnt = fread(position, to_read, 1, file);
+
+    TRACE_INFO_NUMBER("Read", (unsigned int)(read_cnt * to_read));
+
+    if (read_cnt == 0) {
+        error = ferror(file);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to read data from file", error);
+            return error;
+        }
+        error = feof(file);
+        if(error) {
+            TRACE_FLOW_EXIT();
+            return EOK;
+        }
+        TRACE_ERROR_NUMBER("Failed to read data from file", EIO);
+        return EIO;
     }
 
-    *read_num = read_cnt;
+    *read_num = to_read;
 
     TRACE_FLOW_EXIT();
     return error;
@@ -229,7 +241,9 @@ static int initialize_conv(unsigned char *read_buf,
 }
 
 /* Internal conversion part */
-static int common_file_convert(int raw_file, struct ini_cfgfile *file_ctx)
+static int common_file_convert(FILE *file,
+                               struct ini_cfgfile *file_ctx,
+                               uint32_t size)
 {
     int error = EOK;
     size_t read_cnt = 0;
@@ -249,8 +263,8 @@ static int common_file_convert(int raw_file, struct ini_cfgfile *file_ctx)
 
     do {
         /* print_buffer(read_buf, ICONV_BUFFER); */
-        error = read_chunk(raw_file,
-                           file_ctx->file_stats.st_size - total_read,
+        error = read_chunk(file,
+                           size - total_read,
                            ICONV_BUFFER - in_buffer,
                            read_buf + in_buffer,
                            &read_cnt);
@@ -305,9 +319,8 @@ static int common_file_convert(int raw_file, struct ini_cfgfile *file_ctx)
                     /* We need to just read more if we can */
                     TRACE_INFO_NUMBER("Incomplete sequence len",
                                       src - read_buf);
-                    TRACE_INFO_NUMBER("File size.",
-                                        file_ctx->file_stats.st_size);
-                    if (total_read == file_ctx->file_stats.st_size) {
+                    TRACE_INFO_NUMBER("File size.", size);
+                    if (total_read == size) {
                         /* Or return error if we can't */
                         TRACE_ERROR_NUMBER("Incomplete sequence", error);
                         iconv_close(conv);
@@ -356,7 +369,7 @@ static int common_file_convert(int raw_file, struct ini_cfgfile *file_ctx)
         }
         while (1);
     }
-    while (total_read < file_ctx->file_stats.st_size);
+    while (total_read < size);
 
     iconv_close(conv);
 
@@ -365,8 +378,7 @@ static int common_file_convert(int raw_file, struct ini_cfgfile *file_ctx)
                       (char *)simplebuffer_get_vbuf(file_ctx->file_data));
     TRACE_INFO_NUMBER("File len",
                       simplebuffer_get_len(file_ctx->file_data));
-    TRACE_INFO_NUMBER("Size",
-                      file_ctx->file_data->size);
+    TRACE_INFO_NUMBER("Size", size);
     errno = 0;
     file_ctx->file = fmemopen(simplebuffer_get_vbuf(file_ctx->file_data),
                               simplebuffer_get_len(file_ctx->file_data),
@@ -383,59 +395,98 @@ static int common_file_convert(int raw_file, struct ini_cfgfile *file_ctx)
 
 
 /* Internal common initialization part */
-static int common_file_init(struct ini_cfgfile *file_ctx)
+static int common_file_init(struct ini_cfgfile *file_ctx,
+                            void *data_buf,
+                            uint32_t data_len)
 {
     int error = EOK;
-    int raw_file = 0;
+    FILE *file = NULL;
     int stat_ret = 0;
+    uint32_t size = 0;
+    void *internal_data = NULL;
+    uint32_t internal_len = 0;
 
     TRACE_FLOW_ENTRY();
 
-    TRACE_INFO_STRING("File", file_ctx->filename);
+    if (data_buf) {
 
-    /* Open file in binary mode first */
-    errno = 0;
-    raw_file = open(file_ctx->filename, O_RDONLY);
-    if (raw_file == -1) {
-        error = errno;
-        TRACE_ERROR_NUMBER("Failed to open file in binary mode", error);
-        return error;
+        if(data_len) {
+            internal_data = data_buf;
+            internal_len = data_len;
+        }
+        else {
+            /* If buffer is empty fmemopen will return an error.
+             * This will prevent creation of adefault config object.
+             * Instead we will use buffer that has at least one character. */
+            internal_data = (void *)alt_buffer;
+            internal_len = alt_buffer_len;
+        }
+
+        TRACE_INFO_NUMBER("Inside file_init len", internal_len);
+        TRACE_INFO_STRING("Inside file_init data:", (char *)internal_data);
+
+        file = fmemopen(internal_data, internal_len, "r");
+        if (!file) {
+            error = errno;
+            TRACE_ERROR_NUMBER("Failed to memmap file", error);
+            return error;
+        }
+        size = internal_len;
     }
+    else {
 
-    /* Get the size of the file */
-    errno = 0;
-    stat_ret = fstat(raw_file, &(file_ctx->file_stats));
-    if (stat_ret == -1) {
-        error = errno;
-        close(raw_file);
-        TRACE_ERROR_NUMBER("Failed to get file stats", error);
-        return error;
+        TRACE_INFO_STRING("File", file_ctx->filename);
+
+        /* Open file to get its size */
+        errno = 0;
+        file = fopen(file_ctx->filename, "r");
+        if (!file) {
+            error = errno;
+            TRACE_ERROR_NUMBER("Failed to open file", error);
+            return error;
+        }
+
+        /* Get the size of the file */
+        errno = 0;
+        stat_ret = fstat(fileno(file), &(file_ctx->file_stats));
+        if (stat_ret == -1) {
+            error = errno;
+            fclose(file);
+            TRACE_ERROR_NUMBER("Failed to get file stats", error);
+            return error;
+        }
+        size = file_ctx->file_stats.st_size;
     }
 
     /* Trick to overcome the fact that
      * fopen and fmemopen behave differently when file
      * is 0 length
      */
-    if (file_ctx->file_stats.st_size) {
-        error = common_file_convert(raw_file, file_ctx);
-        close(raw_file);
+    if (size) {
+        error = common_file_convert(file, file_ctx, size);
         if (error) {
             TRACE_ERROR_NUMBER("Failed to convert file",
                                 error);
+            fclose(file);
             return error;
         }
     }
     else {
+
+        TRACE_INFO_STRING("File is 0 length","");
         errno = 0;
-        file_ctx->file = fdopen(raw_file, "r");
+
+        file_ctx->file = fdopen(fileno(file), "r");
         if (!(file_ctx->file)) {
             error = errno;
-            close(raw_file);
+            fclose(file);
             TRACE_ERROR_NUMBER("Failed to fdopen file", error);
             return error;
         }
     }
 
+    fclose(file);
+
     /* Collect stats */
     if (file_ctx->metadata_flags & INI_META_STATS) {
         file_ctx->stats_read = 1;
@@ -505,7 +556,7 @@ int ini_config_file_open(const char *filename,
     }
 
     /* Do common init */
-    error = common_file_init(new_ctx);
+    error = common_file_init(new_ctx, NULL, 0);
     if(error) {
         TRACE_ERROR_NUMBER("Failed to do common init", error);
         ini_config_file_destroy(new_ctx);
@@ -517,6 +568,63 @@ int ini_config_file_open(const char *filename,
     return error;
 }
 
+/* Create a file object from a memory buffer */
+int ini_config_file_from_mem(void *data_buf,
+                             uint32_t data_len,
+                             struct ini_cfgfile **file_ctx)
+{
+    int error = EOK;
+    struct ini_cfgfile *new_ctx = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    if ((!data_buf) || (!file_ctx)) {
+        TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
+        return EINVAL;
+    }
+
+    /* Allocate structure */
+    new_ctx = malloc(sizeof(struct ini_cfgfile));
+    if (!new_ctx) {
+        TRACE_ERROR_NUMBER("Failed to allocate file ctx.", ENOMEM);
+        return ENOMEM;
+    }
+
+    new_ctx->filename = NULL;
+    new_ctx->file = NULL;
+    new_ctx->file_data = NULL;
+    new_ctx->metadata_flags = 0;
+
+    error = simplebuffer_alloc(&(new_ctx->file_data));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to allocate buffer ctx.", error);
+        ini_config_file_destroy(new_ctx);
+        return error;
+    }
+
+    /* Put an empty string into the file name */
+    new_ctx->filename = strdup("");
+    if (!(new_ctx->filename)) {
+        ini_config_file_destroy(new_ctx);
+        TRACE_ERROR_NUMBER("Failed to put empty string into filename.", ENOMEM);
+        return ENOMEM;
+    }
+
+    /* Do common init */
+    error = common_file_init(new_ctx, data_buf, data_len);
+    if(error) {
+        TRACE_ERROR_NUMBER("Failed to do common init", error);
+        ini_config_file_destroy(new_ctx);
+        return error;
+    }
+
+    *file_ctx = new_ctx;
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+
+
 /* Create a file object from existing one */
 int ini_config_file_reopen(struct ini_cfgfile *file_ctx_in,
                            struct ini_cfgfile **file_ctx_out)
@@ -564,7 +672,7 @@ int ini_config_file_reopen(struct ini_cfgfile *file_ctx_in,
     }
 
     /* Do common init */
-    error = common_file_init(new_ctx);
+    error = common_file_init(new_ctx, NULL, 0);
     if(error) {
         TRACE_ERROR_NUMBER("Failed to do common init", error);
         ini_config_file_destroy(new_ctx);
diff --git a/ini/libini_config.sym b/ini/libini_config.sym
index d38b17b..d0035f2 100644
--- a/ini/libini_config.sym
+++ b/ini/libini_config.sym
@@ -46,6 +46,7 @@ global:
     ini_config_destroy;
     ini_config_clean_state;
     ini_config_file_open;
+    ini_config_file_from_mem;
     ini_config_file_close;
     ini_config_file_reopen;
     ini_config_file_destroy;
-- 
1.7.1

