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