1 #include <alloca.h>
2 #include <errno.h>
3 #include <stdbool.h>
4 #include <stdint.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include <fcntl.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #if CPUINFO_MOCK
14 #include <cpuinfo-mock.h>
15 #endif
16 #include <cpuinfo/log.h>
17 #include <linux/api.h>
18
cpuinfo_linux_parse_multiline_file(const char * filename,size_t buffer_size,cpuinfo_line_callback callback,void * context)19 bool cpuinfo_linux_parse_multiline_file(
20 const char* filename,
21 size_t buffer_size,
22 cpuinfo_line_callback callback,
23 void* context) {
24 int file = -1;
25 bool status = false;
26 char* buffer = (char*)alloca(buffer_size);
27
28 #if CPUINFO_MOCK
29 file = cpuinfo_mock_open(filename, O_RDONLY);
30 #else
31 file = open(filename, O_RDONLY);
32 #endif
33 if (file == -1) {
34 cpuinfo_log_info("failed to open %s: %s", filename, strerror(errno));
35 goto cleanup;
36 }
37
38 /* Only used for error reporting */
39 size_t position = 0;
40 uint64_t line_number = 1;
41 const char* buffer_end = &buffer[buffer_size];
42 char* data_start = buffer;
43 ssize_t bytes_read;
44 do {
45 #if CPUINFO_MOCK
46 bytes_read = cpuinfo_mock_read(file, data_start, (size_t)(buffer_end - data_start));
47 #else
48 bytes_read = read(file, data_start, (size_t)(buffer_end - data_start));
49 #endif
50 if (bytes_read < 0) {
51 cpuinfo_log_info(
52 "failed to read file %s at position %zu: %s", filename, position, strerror(errno));
53 goto cleanup;
54 }
55
56 position += (size_t)bytes_read;
57 const char* data_end = data_start + (size_t)bytes_read;
58 const char* line_start = buffer;
59
60 if (bytes_read == 0) {
61 /* No more data in the file: process the remaining text
62 * in the buffer as a single entry */
63 const char* line_end = data_end;
64 if (!callback(line_start, line_end, context, line_number)) {
65 goto cleanup;
66 }
67 } else {
68 const char* line_end;
69 do {
70 /* Find the end of the entry, as indicated by
71 * newline character ('\n')
72 */
73 for (line_end = line_start; line_end != data_end; line_end++) {
74 if (*line_end == '\n') {
75 break;
76 }
77 }
78
79 /*
80 * If we located separator at the end of the
81 * entry, parse it. Otherwise, there may be more
82 * data at the end; read the file once again.
83 */
84 if (line_end != data_end) {
85 if (!callback(line_start, line_end, context, line_number++)) {
86 goto cleanup;
87 }
88 line_start = line_end + 1;
89 }
90 } while (line_end != data_end);
91
92 /* Move remaining partial line data at the end to the
93 * beginning of the buffer */
94 const size_t line_length = (size_t)(line_end - line_start);
95 memmove(buffer, line_start, line_length);
96 data_start = &buffer[line_length];
97 }
98 } while (bytes_read != 0);
99
100 /* Commit */
101 status = true;
102
103 cleanup:
104 if (file != -1) {
105 #if CPUINFO_MOCK
106 cpuinfo_mock_close(file);
107 #else
108 close(file);
109 #endif
110 file = -1;
111 }
112 return status;
113 }
114