ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
reader.c
Go to the documentation of this file.
1
11#include <stdlib.h>
12#include <stdio.h>
13#include <string.h>
15
16typedef struct terminal_fd_reader_s {
17 FILE *fp; // File pointer created from FD
19 int frame_width; // Detected from first frame (0 = not yet detected)
20 char *line_buffer; // Temporary buffer for one line
23 bool first_frame_processed; // Track if we've processed first frame yet
25
26#define LINE_BUFFER_SIZE 16384 // Max bytes per line
27
29 if (fd < 0 || frame_height <= 0) {
30 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid fd or frame height: fd=%d, height=%d", fd, frame_height);
31 }
32
33 FILE *fp = platform_fdopen("terminal_fd_reader", fd, "r");
34 if (!fp) {
35 return SET_ERRNO_SYS(ERROR_INVALID_PARAM, "fdopen failed for FD %d", fd);
36 }
37
38 terminal_fd_reader_t *reader = SAFE_CALLOC(1, sizeof(*reader), terminal_fd_reader_t *);
39 reader->fp = fp;
40 reader->frame_height = frame_height;
41 reader->frame_width = 0; // Will be detected from first frame
42 reader->line_buffer = SAFE_MALLOC(LINE_BUFFER_SIZE, char *);
44 reader->eof_reached = false;
45 reader->first_frame_processed = false;
46
47 log_debug("terminal_fd_reader: created with fd=%d, frame height %d (width auto-detect)", fd, frame_height);
48 *out = reader;
49 return ASCIICHAT_OK;
50}
51
53 if (!reader || !out_frame) {
54 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters to terminal_fd_reader_next");
55 }
56
57 if (reader->eof_reached) {
58 *out_frame = NULL;
59 return ASCIICHAT_OK; // EOF, but no error
60 }
61
62 // Allocate buffer for complete frame (frame_height lines)
63 // Estimate: 16KB per line should be enough for ANSI + ASCII
64 size_t frame_capacity = reader->frame_height * LINE_BUFFER_SIZE;
65 char *frame_data = SAFE_MALLOC(frame_capacity, char *);
66 size_t frame_pos = 0;
67
68 // Read exactly frame_height lines from FD
69 for (int line_num = 0; line_num < reader->frame_height; line_num++) {
70 // Read one line from FD
71 char *line = fgets(reader->line_buffer, (int)reader->line_buffer_size, reader->fp);
72
73 if (!line) {
74 // EOF reached
75 reader->eof_reached = true;
76
77 // If we read some lines but not a complete frame, that's ok - return partial
78 if (frame_pos > 0) {
79 log_debug("terminal_fd_reader: EOF reached after %d/%d lines, returning partial frame", line_num,
80 reader->frame_height);
81 // Remove trailing newline from last line if present
82 if (frame_pos > 0 && frame_data[frame_pos - 1] == '\n') {
83 frame_pos--;
84 }
85 *out_frame = frame_data;
86 return ASCIICHAT_OK;
87 }
88
89 SAFE_FREE(frame_data);
90 *out_frame = NULL;
91 return ASCIICHAT_OK; // EOF
92 }
93
94 // Append line to frame
95 size_t line_len = strlen(line);
96 if (frame_pos + line_len >= frame_capacity) {
97 log_warn("terminal_fd_reader: frame buffer overflow, line %d too large", line_num);
98 // Realloc and continue
99 frame_capacity *= 2;
100 char *new_frame = SAFE_MALLOC(frame_capacity, char *);
101 memcpy(new_frame, frame_data, frame_pos);
102 SAFE_FREE(frame_data);
103 frame_data = new_frame;
104 }
105
106 memcpy(frame_data + frame_pos, line, line_len);
107 frame_pos += line_len;
108 }
109
110 // Remove trailing newline from last line (we'll add it back in display)
111 if (frame_pos > 0 && frame_data[frame_pos - 1] == '\n') {
112 frame_data[frame_pos - 1] = '\0';
113 frame_pos--;
114 }
115
116 // Detect frame width from first frame (count visible chars in first line)
117 if (!reader->first_frame_processed && frame_pos > 0) {
118 reader->first_frame_processed = true;
119
120 // Find first line by looking for newline
121 char *first_newline = strchr(frame_data, '\n');
122 if (first_newline) {
123 // Null-terminate first line temporarily for width calculation
124 char saved_char = *first_newline;
125 *first_newline = '\0';
126 reader->frame_width = display_width(frame_data);
127 *first_newline = saved_char; // Restore
128 } else {
129 // Only one line in frame, use it for width
130 reader->frame_width = display_width(frame_data);
131 }
132
133 log_info("terminal_fd_reader: detected frame width %d from first frame", reader->frame_width);
134 }
135
136 *out_frame = frame_data;
137 log_debug_every(NS_PER_SEC_INT, "terminal_fd_reader: read frame (%zu bytes, %dx%d)", frame_pos, reader->frame_width,
138 reader->frame_height);
139 return ASCIICHAT_OK;
140}
141
143 if (!reader)
144 return;
145 if (reader->fp) {
146 fclose(reader->fp);
147 reader->fp = NULL;
148 }
149 SAFE_FREE(reader->line_buffer);
150 SAFE_FREE(reader);
151}
Platform abstraction layer umbrella header providing unified cross-platform API.
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_CALLOC(count, size, cast)
Definition common.h:274
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context, returning the error code.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define NS_PER_SEC_INT
Definition time.h:157
FILE * platform_fdopen(const char *name, int fd, const char *mode)
Convert file descriptor to stream (fdopen replacement)
int display_width(const char *text)
Calculate the visible display width of text with ANSI escape codes.
🖥️ Display Width and Terminal Centering Utilities
📝 Logging API with multiple log levels and terminal output control
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:702
Cross-platform memory allocation utilities.
struct terminal_fd_reader_s terminal_fd_reader_t
void terminal_fd_reader_destroy(terminal_fd_reader_t *reader)
Destroy terminal FD reader.
Definition reader.c:142
asciichat_error_t terminal_fd_reader_next(terminal_fd_reader_t *reader, char **out_frame)
Read next frame from file descriptor.
Definition reader.c:52
asciichat_error_t terminal_fd_reader_create(int fd, int frame_height, terminal_fd_reader_t **out)
Create a terminal FD reader from a file descriptor.
Definition reader.c:28
#define LINE_BUFFER_SIZE
Definition reader.c:26
Read ANSI ASCII frames from file descriptor, chunked by height.
size_t line_buffer_size
Definition reader.c:21
bool first_frame_processed
Definition reader.c:23