Open Ulrond opened 1 month ago
functionality to support reading from either a local file or a remote one, using the curl library ->
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
// Function to get the size of a remote file
long get_remote_file_size(const char *url) {
CURL *curl;
CURLcode res;
double filesize = 0.0;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
curl_easy_setopt(curl, CURLOPT_HEADER, 1L);
curl_easy_setopt(curl, CURLOPT_FILETIME, 1L);
res = curl_easy_perform(curl);
if(res == CURLE_OK) {
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize);
if((res == CURLE_OK) && (filesize > 0.0)) {
curl_easy_cleanup(curl);
return (long)filesize;
}
}
curl_easy_cleanup(curl);
}
return -1;
}
// Function to get the size of a local file
long get_local_file_size(const char *filename) {
FILE *file = fopen(filename, "rb");
if(file == NULL) {
perror("Error opening file");
return -1;
}
fseek(file, 0, SEEK_END);
long filesize = ftell(file);
fclose(file);
return filesize;
}
// Callback function to write data into a buffer
size_t write_data(void *ptr, size_t size, size_t nmemb, void *data) {
size_t total_size = size * nmemb;
char **buffer = (char **)data;
*buffer = realloc(*buffer, strlen(*buffer) + total_size + 1);
strncat(*buffer, ptr, total_size);
return total_size;
}
// Function to download a file using libcurl and store its content in a buffer
int download_file(const char *url, char **buffer) {
CURL *curl;
CURLcode res;
*buffer = malloc(1);
**buffer = '\0';
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
free(*buffer);
return -1;
}
return 0;
}
free(*buffer);
return -1;
}
// Function to read content from a local file into a buffer
int read_local_file(const char *filename, char **buffer) {
long filesize = get_local_file_size(filename);
if (filesize == -1) {
return -1;
}
FILE *file = fopen(filename, "rb");
if (file == NULL) {
perror("Error opening file");
return -1;
}
*buffer = malloc(filesize + 1);
if (*buffer == NULL) {
perror("Error allocating memory");
fclose(file);
return -1;
}
fread(*buffer, 1, filesize, file);
(*buffer)[filesize] = '\0'; // Null-terminate the buffer
fclose(file);
return 0;
}
// Unified function to read from either a URL or a local file
int ut_read(const char *path, char **buffer) {
// Check if the path is a URL (simple check for http:// or https://)
if (strstr(path, "http://") || strstr(path, "https://")) {
long filesize = get_remote_file_size(path);
if (filesize == -1) {
fprintf(stderr, "Failed to get remote file size.\n");
return -1;
}
return download_file(path, buffer);
} else {
return read_local_file(path, buffer);
}
}
int main(void) {
const char *input = "https://example.com/file.txt"; // or "local_file.txt"
char *buffer = NULL;
if (ut_read(input, &buffer) == 0) {
printf("File content:\n%s\n", buffer);
free(buffer); // Don't forget to free the allocated memory
} else {
printf("Failed to read file.\n");
}
return 0;
}
Recursive version
#include <libfyaml.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#define MAX_INCLUDE_DEPTH 10
struct MemoryStruct
{
char *memory;
size_t size;
};
/* Function prototypes */
fy_node* process_include(fy_node *node, fy_parser *parser, int depth);
void merge_nodes(fy_node *dest, fy_node *src);
/* Processing functions */
size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if (!ptr)
{
fprintf(stderr, "Error: Not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
fy_node* process_node(fy_node *node, fy_parser *parser, int depth)
{
if (!node) return NULL;
if (fy_node_is_scalar(node))
{
fy_tag tag = fy_node_get_tag(node);
if (strcmp(fy_tag_get_name(tag), "!include") == 0)
{
fy_node *included = process_include(node, parser, depth);
merge_nodes(node, included);
fy_node_free(included);
// Remove the !include tag
if (fy_node_is_mapping(node->parent))
{
fy_node *parent = node->parent;
for (size_t i = 0; i < fy_node_get_mapping_pairs_length(parent); i++)
{
fy_node_pair pair = fy_node_get_mapping_pair_by_index(parent, i);
if (pair.value == node)
{
fy_node_remove_mapping_pair_by_index(parent, i);
break; // Only remove the first match
}
}
}
}
} else if (fy_node_is_sequence(node))
{
for (size_t i = 0; i < fy_node_get_sequence_items_length(node); i++)
{
fy_node_set_sequence_item_by_index(node, i,
process_node(fy_node_get_sequence_item_by_index(node, i), parser, depth));
}
} else if (fy_node_is_mapping(node))
{
for (size_t i = 0; i < fy_node_get_mapping_pairs_length(node); i++)
{
fy_node_pair pair = fy_node_get_mapping_pair_by_index(node, i);
pair.key = process_node(pair.key, parser, depth);
pair.value = process_node(pair.value, parser, depth);
fy_node_set_mapping_pair_by_index(node, i, pair);
}
}
return node;
}
void merge_nodes(fy_node *dest, fy_node *src)
{
if (!dest || !src) return;
if (fy_node_is_scalar(dest))
{
fy_node_move(dest, src); // Move ownership, not copy
} else if (fy_node_is_sequence(dest) && fy_node_is_sequence(src))
{
for (size_t i = 0; i < fy_node_get_sequence_items_length(src); i++)
{
fy_node_append_sequence_item(dest, fy_node_get_sequence_item_by_index(src, i));
fy_node_set_sequence_item_by_index(src, i, NULL); // Prevent double-free
}
} else if (fy_node_is_mapping(dest) && fy_node_is_mapping(src))
{
for (size_t i = 0; i < fy_node_get_mapping_pairs_length(src); i++)
{
fy_node_pair pair = fy_node_get_mapping_pair_by_index(src, i);
fy_node_add_mapping_pair(dest, pair.key, pair.value);
pair.key = NULL;
pair.value = NULL; // Prevent double-free
}
} else
{
fprintf(stderr, "Warning: Cannot merge nodes of incompatible types\n");
}
}
fy_node* process_include(fy_node *node, fy_parser *parser, int depth)
{
if (depth >= MAX_INCLUDE_DEPTH)
{
fprintf(stderr, "Error: Maximum include depth exceeded.\n");
exit(EXIT_FAILURE);
}
const char *filename = fy_node_get_scalar_str(node, NULL);
if (strncmp(filename, "file:", 5) == 0)
{
// Local file include
filename += 5; // Skip the "file:" prefix
FILE *file = fopen(filename, "r");
if (!file)
{
fprintf(stderr, "Error: Cannot open include file '%s'.\n", filename);
exit(EXIT_FAILURE);
}
fy_parser_set_input_file(parser, file);
fy_document doc;
if (!fy_parse_document(parser, &doc))
{
fprintf(stderr, "Error: Cannot parse include file '%s'.\n", filename);
exit(EXIT_FAILURE);
}
fy_node *root = fy_document_get_root_node(&doc);
root = process_node(root, parser, depth + 1);
fy_document_destroy(&doc);
fclose(file);
return root;
} else if (strncmp(filename, "http:", 5) == 0 || strncmp(filename, "https:", 6) == 0)
{
// URL include
struct MemoryStruct chunk = { .memory = malloc(1), .size = 0 };
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (!curl)
{
fprintf(stderr, "Error: Could not initialize curl\n");
exit(EXIT_FAILURE);
}
curl_easy_setopt(curl, CURLOPT_URL, filename);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
fprintf(stderr, "Error: curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
exit(EXIT_FAILURE);
}
long response_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200)
{
fprintf(stderr, "Error: HTTP request failed with code %ld\n", response_code);
exit(EXIT_FAILURE);
}
fy_parser_set_input_string(parser, chunk.memory, chunk.size);
fy_document doc;
if (!fy_parse_document(parser, &doc))
{
fprintf(stderr, "Error: Cannot parse included content\n");
exit(EXIT_FAILURE);
}
fy_node *root = fy_document_get_root_node(&doc);
root = process_node(root, parser, depth + 1);
fy_document_destroy(&doc);
Yaml file testing requirements
## Single include URL check
`1-single-include-url.yaml`
1:
value: true
!include <https://github.com/rdkcentral/ut-control/tree/main/tests/src/assets/include/2s.yaml>
!include <https://github.com/rdkcentral/ut-control/tree/main/tests/src/assets/include/3s.yaml>
!include <https://github.com/rdkcentral/ut-control/tree/main/tests/src/assets/include/4s.yaml>
## Multi-include check file check
`1-single-include-file.yaml`
1:
value: true
!include <file:2s.yaml>
!include <file:3s.yaml>
!include <file:4s.yaml>
`2s.yml`
2:
value: true
3:
value: true
4:
value: true
5:
value: true
## Include Depth Check
`depth_check.yml`
1:
value: true
!include <file:2.yaml>
`2d.yaml`
2:
value: true
!include <file:3.yaml>
`3d.yaml`
3:
value: true
!include <file:4.yaml>
`4d.yaml`
4:
value: true
!include <file:5.yaml>
`5s.yaml`
5:
value: true
OpenKVP( "./profiles/1.yml" );
UT_ASSERT_KVP_PROFILE_CHECK_BOOL( "4:value" , true);
UT_ASSERT_KVP_PROFILE_CHECK_BOOL( "3:value" , true);
UT_ASSERT_KVP_PROFILE_CHECK_BOOL( "2:value" , true);
UT_ASSERT_KVP_PROFILE_CHECK_BOOL( "1:value" , true);
## Master file would look like this in both cases
1:
value: true
2:
value: true
3:
value: true
4:
value: true
## Check for MAX INCLUDES - Set Max Includes 4
While libfyaml itself doesn't directly support including other YAML files. We need to create a mechanism for including files.
Here's an approach that may work.
1. Manual Inclusion and Parsing:
!include
).2. Preprocessing:
3. Custom Tag Support:
!include
) for including other files.Example (Manual Inclusion):