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