ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
resources.c
Go to the documentation of this file.
1
11#include <ascii-chat/options/manpage/resources.h>
12#include <ascii-chat/embedded_resources.h>
13#include <ascii-chat/log/logging.h>
14#include <stdlib.h>
15#include <string.h>
16
28static asciichat_error_t load_single_resource(const char **out_content, size_t *out_len,
29 int (*get_resource_func)(FILE **out_file, const char **out_content,
30 size_t *out_len),
31 const char *resource_name) {
32 if (!out_content || !out_len) {
33 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid output pointers for %s", resource_name);
34 }
35
36 FILE *file = NULL;
37 const char *content = NULL;
38 size_t len = 0;
39
40 // Call the resource function to get template or content
41 int result = get_resource_func(&file, &content, &len);
42 if (result != 0) {
43 return SET_ERRNO(ERROR_CONFIG, "Failed to load %s resource", resource_name);
44 }
45
46 // Determine if we got embedded (content) or filesystem (file) resource
47 if (content) {
48 // Production: embedded resource
49 // No allocation needed, content is static
50 *out_content = content;
51 *out_len = len;
52 log_debug("Loaded %s from embedded resources (%zu bytes)", resource_name, len);
53 return ASCIICHAT_OK;
54 } else if (file) {
55 // Development: filesystem resource
56 // Need to read entire file into memory for easy access
57 char *buffer = NULL;
58
59 // Seek to end to determine file size
60 if (fseek(file, 0, SEEK_END) != 0) {
62 return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to seek %s file", resource_name);
63 }
64
65 long file_size = ftell(file);
66 if (file_size < 0) {
68 return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to get %s file size", resource_name);
69 }
70
71 if (fseek(file, 0, SEEK_SET) != 0) {
73 return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to seek %s file", resource_name);
74 }
75
76 // Allocate buffer (file_size + 1 for null terminator)
77 size_t buffer_size = (size_t)file_size + 1;
78 buffer = SAFE_MALLOC(buffer_size, char *);
79 if (!buffer) {
81 return SET_ERRNO(ERROR_CONFIG, "Failed to allocate memory for %s (%zu bytes)", resource_name, buffer_size);
82 }
83
84 // Read file content
85 size_t bytes_read = fread(buffer, 1, (size_t)file_size, file);
87
88 if (bytes_read != (size_t)file_size) {
89 SAFE_FREE(buffer);
90 return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to read complete %s file (%zu/%lu bytes read)", resource_name,
91 bytes_read, (unsigned long)file_size);
92 }
93
94 // Ensure null termination
95 buffer[(size_t)file_size] = '\0';
96
97 *out_content = buffer;
98 *out_len = (size_t)file_size;
99 log_debug("Loaded %s from filesystem (%zu bytes)", resource_name, *out_len);
100 return ASCIICHAT_OK;
101 } else {
102 // Should not happen - either file or content should be set
103 return SET_ERRNO(ERROR_CONFIG, "Invalid %s resource state (neither file nor content available)", resource_name);
104 }
105}
106
107asciichat_error_t manpage_resources_load(manpage_resources_t *resources) {
108 if (!resources) {
109 return SET_ERRNO(ERROR_INVALID_PARAM, "resources pointer cannot be NULL");
110 }
111
112 // Initialize to safe state
113 resources->template_content = NULL;
114 resources->template_len = 0;
115 resources->content_sections = NULL;
116 resources->content_len = 0;
117 resources->is_embedded = false;
118 resources->allocated = false;
119
120 // Load template resource
121 asciichat_error_t err = load_single_resource(&resources->template_content, &resources->template_len,
122 get_manpage_template, "man page template");
123 if (err != ASCIICHAT_OK) {
124 return err;
125 }
126
127 // Content is now merged into template - no separate content file needed
128 // Set to NULL so cleanup doesn't try to free a string literal
129 resources->content_sections = NULL;
130 resources->content_len = 0;
131
132 // Determine if resources came from embedded binary
133 // In production (NDEBUG), both would be embedded; in development, both would be files
134#ifdef NDEBUG
135 resources->is_embedded = true;
136#else
137 resources->is_embedded = false;
138#endif
139
140 // Mark that we may have allocated memory (only in debug builds for filesystem reads)
141#ifdef NDEBUG
142 resources->allocated = false;
143#else
144 resources->allocated = true;
145#endif
146
147 log_debug("Resources loaded successfully (embedded=%d, allocated=%d)", resources->is_embedded, resources->allocated);
148 return ASCIICHAT_OK;
149}
150
151void manpage_resources_destroy(manpage_resources_t *resources) {
152 if (!resources) {
153 return;
154 }
155
156 // Only free content if it was allocated (i.e., loaded from filesystem in debug builds)
157 if (resources->allocated) {
158#ifndef NDEBUG
159 // In debug builds, template and content were allocated by load_single_resource
160 if (resources->template_content) {
161 char *template_ptr = (char *)resources->template_content;
162 SAFE_FREE(template_ptr);
163 }
164 if (resources->content_sections) {
165 char *content_ptr = (char *)resources->content_sections;
166 SAFE_FREE(content_ptr);
167 }
168#endif
169 }
170
171 // Clear the structure
172 resources->template_content = NULL;
173 resources->template_len = 0;
174 resources->content_sections = NULL;
175 resources->content_len = 0;
176 resources->is_embedded = false;
177 resources->allocated = false;
178}
179
180bool manpage_resources_is_valid(const manpage_resources_t *resources) {
181 if (!resources) {
182 return false;
183 }
184
185 // Resources are valid if template is loaded (content is now optional/merged into template)
186 return resources->template_content != NULL && resources->template_len > 0;
187}
int get_manpage_template(FILE **out_file, const char **out_content, size_t *out_len)
void release_manpage_resources(FILE *file)
int buffer_size
Size of circular buffer.
Definition grep.c:84
void manpage_resources_destroy(manpage_resources_t *resources)
Definition resources.c:151
asciichat_error_t manpage_resources_load(manpage_resources_t *resources)
Definition resources.c:107
bool manpage_resources_is_valid(const manpage_resources_t *resources)
Definition resources.c:180