]> git.lizzy.rs Git - nothing.git/blob - src/system/line_stream.c
a061793d51809a375fb256a61d66c72842c2db4b
[nothing.git] / src / system / line_stream.c
1 #include "system/stacktrace.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <stdbool.h>
7
8 #include "line_stream.h"
9 #include "lt.h"
10 #include "lt_adapters.h"
11 #include "system/nth_alloc.h"
12 #include "system/log.h"
13 #include "system/str.h"
14
15 struct LineStream
16 {
17     Lt *lt;
18     FILE *stream;
19     char *buffer;
20     size_t capacity;
21     bool unfinished;
22 };
23
24 // TODO(#905): create_line_stream probably does not need mode
25 //   Because LineStream interface doesn't even have anything
26 //   for writing to files. So we can just hardcode the mode
27 //   inside of the ctor.
28 LineStream *create_line_stream(const char *filename,
29                                const char *mode,
30                                size_t capacity)
31 {
32     trace_assert(filename);
33     trace_assert(mode);
34
35     Lt *lt = create_lt();
36
37     LineStream *line_stream = PUSH_LT(
38         lt,
39         nth_calloc(1, sizeof(LineStream)),
40         free);
41     if (line_stream == NULL) {
42         RETURN_LT(lt, NULL);
43     }
44     line_stream->lt = lt;
45
46     line_stream->stream = PUSH_LT(
47         lt,
48         fopen(filename, mode),
49         fclose_lt);
50     if (line_stream->stream == NULL) {
51         log_fail("Could not open file '%s': %s\n", filename, strerror(errno));
52         RETURN_LT(lt, NULL);
53     }
54
55     line_stream->buffer = PUSH_LT(
56         lt,
57         nth_calloc(1, sizeof(char) * capacity),
58         free);
59     if (line_stream->buffer == NULL) {
60         RETURN_LT(lt, NULL);
61     }
62
63     line_stream->capacity = capacity;
64     line_stream->unfinished = false;
65
66     return line_stream;
67 }
68
69 void destroy_line_stream(LineStream *line_stream)
70 {
71     trace_assert(line_stream);
72
73     RETURN_LT0(line_stream->lt);
74 }
75
76
77 const char *line_stream_next_chunk(LineStream *line_stream)
78 {
79     trace_assert(line_stream);
80
81     const char *s = fgets(line_stream->buffer,
82                           (int) line_stream->capacity,
83                           line_stream->stream);
84     if (s == NULL) {
85         return NULL;
86     }
87     size_t n = strlen(s);
88     line_stream->unfinished = s[n - 1] != '\n';
89
90     return s;
91 }
92
93 const char *line_stream_next(LineStream *line_stream)
94 {
95     trace_assert(line_stream);
96
97     while (line_stream->unfinished) {
98         line_stream_next_chunk(line_stream);
99     }
100
101     return line_stream_next_chunk(line_stream);
102 }
103
104 char *line_stream_collect_n_lines(LineStream *line_stream, size_t n)
105 {
106     char *result = string_append(NULL, "");
107     for (size_t i = 0; i < n; ++i) {
108         const char *line = line_stream_next(line_stream);
109         if (line == NULL) {
110             free(result);
111             return NULL;
112         }
113
114         result = string_append(result, line);
115     }
116
117     return result;
118 }
119
120 char *line_stream_collect_until_end(LineStream *line_stream)
121 {
122     char *result = string_append(NULL, "");
123     const char *line = line_stream_next(line_stream);
124
125     /* TODO(#906): line_stream_collect_until_end does not distinguish between EOF and error during reading */
126     while (line != NULL) {
127         result = string_append(result, line);
128         line = line_stream_next(line_stream);
129     }
130
131     return result;
132 }