ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
reader.h File Reference

Read ANSI ASCII frames from file descriptor, chunked by height. More...

Go to the source code of this file.

Functions

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.
asciichat_error_t terminal_fd_reader_next (terminal_fd_reader_t *reader, char **out_frame)
 Read next frame from file descriptor.
void terminal_fd_reader_destroy (terminal_fd_reader_t *reader)
 Destroy terminal FD reader.

Detailed Description

Read ANSI ASCII frames from file descriptor, chunked by height.

Reads ASCII frames from any file descriptor (stdin, file, pipe) and chunks them by height (number of lines) as specified. Supports reading ANSI-colored ASCII art frames in batches.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
February 2026

Definition in file reader.h.

Function Documentation

◆ terminal_fd_reader_create()

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.

Parameters
fdFile descriptor to read from (e.g., STDIN_FILENO, open file)
frame_heightNumber of lines per frame (required - used for frame boundaries)
outPointer to store reader context
Returns
ASCIICHAT_OK on success

Width is auto-detected from the first frame's line length.

Definition at line 33 of file reader.c.

33 {
34 if (fd < 0 || frame_height <= 0 || !out) {
35 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid reader parameters: fd=%d, height=%d, output=%p", fd, frame_height,
36 (void *)out);
37 }
38
39 FILE *fp = platform_fdopen("terminal_fd_reader", fd, "r");
40 if (!fp) {
41 return SET_ERRNO_SYS(ERROR_INVALID_PARAM, "fdopen failed for FD %d", fd);
42 }
43
44 terminal_fd_reader_t *reader = SAFE_CALLOC(1, sizeof(*reader), terminal_fd_reader_t *);
45 reader->fp = fp;
46 reader->frame_height = frame_height;
47 reader->frame_width = 0; // Will be detected from first frame
48 reader->line_buffer = SAFE_MALLOC(LINE_BUFFER_SIZE, char *);
50 reader->eof_reached = false;
51 reader->first_frame_processed = false;
52
53 log_debug("created with fd=%d, frame height %d (width auto-detect)", fd, frame_height);
54 *out = reader;
55 return ASCIICHAT_OK;
56}
#define SET_ERRNO(code,...)
#define SET_ERRNO_SYS(code,...)
#define SAFE_MALLOC(size, cast)
Definition common.h:265
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
static bool size_t gotel_query_t * out
Definition queries.h:8
struct terminal_fd_reader_s terminal_fd_reader_t
#define LINE_BUFFER_SIZE
Definition reader.c:31
size_t line_buffer_size
Definition reader.c:26
bool first_frame_processed
Definition reader.c:28

References ASCIICHAT_OK, terminal_fd_reader_s::eof_reached, ERROR_INVALID_PARAM, terminal_fd_reader_s::first_frame_processed, terminal_fd_reader_s::fp, terminal_fd_reader_s::frame_height, terminal_fd_reader_s::frame_width, terminal_fd_reader_s::line_buffer, LINE_BUFFER_SIZE, terminal_fd_reader_s::line_buffer_size, log_debug, out, SAFE_CALLOC, SAFE_MALLOC, SET_ERRNO, and SET_ERRNO_SYS.

◆ terminal_fd_reader_destroy()

void terminal_fd_reader_destroy ( terminal_fd_reader_t * reader)

Destroy terminal FD reader.

Parameters
readerReader context to destroy

Definition at line 148 of file reader.c.

148 {
149 if (!reader)
150 return;
151 if (reader->fp) {
152 fclose(reader->fp);
153 reader->fp = NULL;
154 }
155 SAFE_FREE(reader->line_buffer);
156 SAFE_FREE(reader);
157}
#define SAFE_FREE(ptr)
Definition common.h:377

References terminal_fd_reader_s::fp, terminal_fd_reader_s::line_buffer, and SAFE_FREE.

◆ terminal_fd_reader_next()

asciichat_error_t terminal_fd_reader_next ( terminal_fd_reader_t * reader,
char ** out_frame )

Read next frame from file descriptor.

Reads frame_height lines from the FD and returns them as a single frame string. Returns NULL in out_frame when EOF is reached.

Parameters
readerReader context
out_framePointer to store frame data (NULL when EOF, must free with SAFE_FREE)
Returns
ASCIICHAT_OK on success (including EOF)

Definition at line 58 of file reader.c.

58 {
59 if (!reader || !out_frame) {
60 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
61 }
62
63 if (reader->eof_reached) {
64 *out_frame = NULL;
65 return ASCIICHAT_OK; // EOF, but no error
66 }
67
68 // Allocate buffer for complete frame (frame_height lines)
69 // Estimate: 16KB per line should be enough for ANSI + ASCII
70 size_t frame_capacity = reader->frame_height * LINE_BUFFER_SIZE;
71 char *frame_data = SAFE_MALLOC(frame_capacity, char *);
72 size_t frame_pos = 0;
73
74 // Read exactly frame_height lines from FD
75 for (int line_num = 0; line_num < reader->frame_height; line_num++) {
76 // Read one line from FD
77 char *line = fgets(reader->line_buffer, (int)reader->line_buffer_size, reader->fp);
78
79 if (!line) {
80 // EOF reached
81 reader->eof_reached = true;
82
83 // If we read some lines but not a complete frame, that's ok - return partial
84 if (frame_pos > 0) {
85 log_debug("EOF reached after %d/%d lines, returning partial frame", line_num, reader->frame_height);
86 // Remove trailing newline from last line if present
87 if (frame_pos > 0 && frame_data[frame_pos - 1] == '\n') {
88 frame_pos--;
89 }
90 frame_data[frame_pos] = '\0';
91 *out_frame = frame_data;
92 return ASCIICHAT_OK;
93 }
94
95 SAFE_FREE(frame_data);
96 *out_frame = NULL;
97 return ASCIICHAT_OK; // EOF
98 }
99
100 // Append line to frame
101 size_t line_len = strlen(line);
102 if (frame_pos + line_len >= frame_capacity) {
103 log_warn("frame buffer overflow, line %d too large", line_num);
104 // Realloc and continue
105 frame_capacity *= 2;
106 char *new_frame = SAFE_MALLOC(frame_capacity, char *);
107 memcpy(new_frame, frame_data, frame_pos);
108 SAFE_FREE(frame_data);
109 frame_data = new_frame;
110 }
111
112 memcpy(frame_data + frame_pos, line, line_len);
113 frame_pos += line_len;
114 }
115
116 // Remove trailing newline from last line (we'll add it back in display)
117 if (frame_pos > 0 && frame_data[frame_pos - 1] == '\n') {
118 frame_pos--;
119 }
120 frame_data[frame_pos] = '\0';
121
122 // Detect frame width from first frame (count visible chars in first line)
123 if (!reader->first_frame_processed && frame_pos > 0) {
124 reader->first_frame_processed = true;
125
126 // Find first line by looking for newline
127 char *first_newline = strchr(frame_data, '\n');
128 if (first_newline) {
129 // Null-terminate first line temporarily for width calculation
130 char saved_char = *first_newline;
131 *first_newline = '\0';
132 reader->frame_width = display_width(frame_data);
133 *first_newline = saved_char; // Restore
134 } else {
135 // Only one line in frame, use it for width
136 reader->frame_width = display_width(frame_data);
137 }
138
139 log_info("detected frame width %d from first frame", reader->frame_width);
140 }
141
142 *out_frame = frame_data;
143 log_debug_every(NS_PER_SEC_INT, "read frame (%zu bytes, %dx%d)", frame_pos, reader->frame_width,
144 reader->frame_height);
145 return ASCIICHAT_OK;
146}
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
#define NS_PER_SEC_INT
Definition time.h:169
int display_width(const char *text)
Calculate the visible display width of text with ANSI escape codes.
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:749

References ASCIICHAT_OK, display_width(), terminal_fd_reader_s::eof_reached, ERROR_INVALID_PARAM, terminal_fd_reader_s::first_frame_processed, terminal_fd_reader_s::fp, terminal_fd_reader_s::frame_height, terminal_fd_reader_s::frame_width, terminal_fd_reader_s::line_buffer, LINE_BUFFER_SIZE, terminal_fd_reader_s::line_buffer_size, log_debug, log_debug_every, log_info, log_warn, NS_PER_SEC_INT, SAFE_FREE, SAFE_MALLOC, and SET_ERRNO.