xref: /openbmc/linux/tools/lib/api/io.h (revision e9b7b8b3)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Lightweight buffered reading library.
4  *
5  * Copyright 2019 Google LLC.
6  */
7 #ifndef __API_IO__
8 #define __API_IO__
9 
10 #include <errno.h>
11 #include <poll.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <linux/types.h>
16 
17 struct io {
18 	/* File descriptor being read/ */
19 	int fd;
20 	/* Size of the read buffer. */
21 	unsigned int buf_len;
22 	/* Pointer to storage for buffering read. */
23 	char *buf;
24 	/* End of the storage. */
25 	char *end;
26 	/* Currently accessed data pointer. */
27 	char *data;
28 	/* Read timeout, 0 implies no timeout. */
29 	int timeout_ms;
30 	/* Set true on when the end of file on read error. */
31 	bool eof;
32 };
33 
io__init(struct io * io,int fd,char * buf,unsigned int buf_len)34 static inline void io__init(struct io *io, int fd,
35 			    char *buf, unsigned int buf_len)
36 {
37 	io->fd = fd;
38 	io->buf_len = buf_len;
39 	io->buf = buf;
40 	io->end = buf;
41 	io->data = buf;
42 	io->timeout_ms = 0;
43 	io->eof = false;
44 }
45 
46 /* Reads one character from the "io" file with similar semantics to fgetc. */
io__get_char(struct io * io)47 static inline int io__get_char(struct io *io)
48 {
49 	char *ptr = io->data;
50 
51 	if (io->eof)
52 		return -1;
53 
54 	if (ptr == io->end) {
55 		ssize_t n;
56 
57 		if (io->timeout_ms != 0) {
58 			struct pollfd pfds[] = {
59 				{
60 					.fd = io->fd,
61 					.events = POLLIN,
62 				},
63 			};
64 
65 			n = poll(pfds, 1, io->timeout_ms);
66 			if (n == 0)
67 				errno = ETIMEDOUT;
68 			if (n > 0 && !(pfds[0].revents & POLLIN)) {
69 				errno = EIO;
70 				n = -1;
71 			}
72 			if (n <= 0) {
73 				io->eof = true;
74 				return -1;
75 			}
76 		}
77 		n = read(io->fd, io->buf, io->buf_len);
78 
79 		if (n <= 0) {
80 			io->eof = true;
81 			return -1;
82 		}
83 		ptr = &io->buf[0];
84 		io->end = &io->buf[n];
85 	}
86 	io->data = ptr + 1;
87 	return *ptr;
88 }
89 
90 /* Read a hexadecimal value with no 0x prefix into the out argument hex. If the
91  * first character isn't hexadecimal returns -2, io->eof returns -1, otherwise
92  * returns the character after the hexadecimal value which may be -1 for eof.
93  * If the read value is larger than a u64 the high-order bits will be dropped.
94  */
io__get_hex(struct io * io,__u64 * hex)95 static inline int io__get_hex(struct io *io, __u64 *hex)
96 {
97 	bool first_read = true;
98 
99 	*hex = 0;
100 	while (true) {
101 		int ch = io__get_char(io);
102 
103 		if (ch < 0)
104 			return ch;
105 		if (ch >= '0' && ch <= '9')
106 			*hex = (*hex << 4) | (ch - '0');
107 		else if (ch >= 'a' && ch <= 'f')
108 			*hex = (*hex << 4) | (ch - 'a' + 10);
109 		else if (ch >= 'A' && ch <= 'F')
110 			*hex = (*hex << 4) | (ch - 'A' + 10);
111 		else if (first_read)
112 			return -2;
113 		else
114 			return ch;
115 		first_read = false;
116 	}
117 }
118 
119 /* Read a positive decimal value with out argument dec. If the first character
120  * isn't a decimal returns -2, io->eof returns -1, otherwise returns the
121  * character after the decimal value which may be -1 for eof. If the read value
122  * is larger than a u64 the high-order bits will be dropped.
123  */
io__get_dec(struct io * io,__u64 * dec)124 static inline int io__get_dec(struct io *io, __u64 *dec)
125 {
126 	bool first_read = true;
127 
128 	*dec = 0;
129 	while (true) {
130 		int ch = io__get_char(io);
131 
132 		if (ch < 0)
133 			return ch;
134 		if (ch >= '0' && ch <= '9')
135 			*dec = (*dec * 10) + ch - '0';
136 		else if (first_read)
137 			return -2;
138 		else
139 			return ch;
140 		first_read = false;
141 	}
142 }
143 
144 /* Read up to and including the first newline following the pattern of getline. */
io__getline(struct io * io,char ** line_out,size_t * line_len_out)145 static inline ssize_t io__getline(struct io *io, char **line_out, size_t *line_len_out)
146 {
147 	char buf[128];
148 	int buf_pos = 0;
149 	char *line = NULL, *temp;
150 	size_t line_len = 0;
151 	int ch = 0;
152 
153 	/* TODO: reuse previously allocated memory. */
154 	free(*line_out);
155 	while (ch != '\n') {
156 		ch = io__get_char(io);
157 
158 		if (ch < 0)
159 			break;
160 
161 		if (buf_pos == sizeof(buf)) {
162 			temp = realloc(line, line_len + sizeof(buf));
163 			if (!temp)
164 				goto err_out;
165 			line = temp;
166 			memcpy(&line[line_len], buf, sizeof(buf));
167 			line_len += sizeof(buf);
168 			buf_pos = 0;
169 		}
170 		buf[buf_pos++] = (char)ch;
171 	}
172 	temp = realloc(line, line_len + buf_pos + 1);
173 	if (!temp)
174 		goto err_out;
175 	line = temp;
176 	memcpy(&line[line_len], buf, buf_pos);
177 	line[line_len + buf_pos] = '\0';
178 	line_len += buf_pos;
179 	*line_out = line;
180 	*line_len_out = line_len;
181 	return line_len;
182 err_out:
183 	free(line);
184 	return -ENOMEM;
185 }
186 
187 #endif /* __API_IO__ */
188