• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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