ohmu
·
2026-10-15
1#include "fastfetch.h"
2#include "common/properties.h"
3#include "common/io.h"
4#include "common/mallocHelper.h"
5
6#include <stdlib.h>
7#include <ctype.h>
8#ifdef _WIN32
9 #include "common/windows/getline.h"
10#endif
11
12bool ffParsePropLinePointer(const char** line, const char* start, FFstrbuf* buffer) {
13 if (**line == '\0') {
14 return false;
15 }
16
17 // Skip any amount of whitespace at the begin of line
18 while (**line == ' ' || **line == '\t') {
19 ++(*line);
20 }
21
22 while (*start != '\0') {
23 // Any amount of whitespace in the format string matches any amount of whitespace in the line, even none
24 if (*start == ' ' || *start == '\t') {
25 while (*start == ' ' || *start == '\t') {
26 ++start;
27 }
28
29 while (**line == ' ' || **line == '\t') {
30 ++(*line);
31 }
32
33 continue;
34 }
35
36 // Line doesn't match start, skip it
37 if (tolower(**line) != tolower(*start) || **line == '\0') {
38 return false;
39 }
40
41 // Line and start match, continue testing
42 ++(*line);
43 ++start;
44 }
45
46 char valueEnd = '\n';
47
48 // Allow faster parsing of XML
49 if (*(*line - 1) == '>') {
50 valueEnd = '<';
51 }
52
53 // Skip any amount of whitespace at the begin of the value
54 while (**line == ' ' || **line == '\t') {
55 ++(*line);
56 }
57
58 // Allow faster parsing of quoted values
59 if (**line == '"' || **line == '\'') {
60 valueEnd = **line;
61 ++(*line);
62 }
63
64 // Copy the value to the buffer
65 while (**line != valueEnd && **line != '\n' && **line != '\0') {
66 ffStrbufAppendC(buffer, **line);
67 ++(*line);
68 }
69
70 ffStrbufTrimRight(buffer, ' ');
71
72 return true;
73}
74
75bool ffParsePropLines(const char* lines, const char* start, FFstrbuf* buffer) {
76 while (!ffParsePropLinePointer(&lines, start, buffer)) {
77 while (*lines != '\0' && *lines != '\n') {
78 ++lines;
79 }
80
81 if (*lines == '\0') {
82 return false;
83 }
84
85 // Skip '\n'
86 ++lines;
87 }
88
89 return true;
90}
91
92// The following functions return true if the file was found, independently if start was found
93// Buffers which already contain content are not overwritten
94// The last occurrence of start in the first file will be the one used
95
96bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries) {
97 FF_AUTO_CLOSE_FILE FILE* file = fopen(filename, "r");
98 if (file == nullptr) {
99 return false;
100 }
101
102 bool valueStorage[32];
103 bool* unsetValues = valueStorage;
104
105 if (numQueries > ARRAY_SIZE(valueStorage)) {
106 unsetValues = malloc(sizeof(bool) * numQueries);
107 }
108
109 bool allSet = true;
110 for (uint32_t i = 0; i < numQueries; i++) {
111 unsetValues[i] = queries[i].buffer->length == 0;
112 if (unsetValues[i]) {
113 allSet = false;
114 }
115 }
116
117 if (!allSet) {
118 FF_AUTO_FREE char* line = nullptr;
119 size_t len = 0;
120
121 while (getline(&line, &len, file) != -1) {
122 for (uint32_t i = 0; i < numQueries; i++) {
123 if (!unsetValues[i]) {
124 continue;
125 }
126
127 uint32_t currentLength = queries[i].buffer->length;
128 queries[i].buffer->length = 0;
129 if (!ffParsePropLine(line, queries[i].start, queries[i].buffer)) {
130 queries[i].buffer->length = currentLength;
131 }
132 }
133 }
134 }
135
136 if (unsetValues != valueStorage) {
137 free(unsetValues);
138 }
139 return true;
140}
141
142bool ffParsePropFileHomeValues(const char* relativeFile, uint32_t numQueries, FFpropquery* queries) {
143 FF_STRBUF_AUTO_DESTROY absolutePath = ffStrbufCreateF("%s/%s", instance.state.platform.homeDir.chars, relativeFile);
144 return ffParsePropFileValues(absolutePath.chars, numQueries, queries);
145}
146
147bool ffParsePropFileListValues(const FFlist* list, const char* relativeFile, uint32_t numQueries, FFpropquery* queries) {
148 bool foundAFile = false;
149
150 FF_LIST_FOR_EACH (FFstrbuf, dirPrefix, *list) {
151 const uint32_t dirPrefixLength = dirPrefix->length;
152 ffStrbufAppendS(dirPrefix, relativeFile);
153 if (ffParsePropFileValues(dirPrefix->chars, numQueries, queries)) {
154 foundAFile = true;
155 }
156 ffStrbufSubstrBefore(dirPrefix, dirPrefixLength);
157
158 bool allSet = true;
159 for (uint32_t k = 0; k < numQueries; k++) {
160 if (queries[k].buffer->length == 0) {
161 allSet = false;
162 break;
163 }
164 }
165
166 if (allSet) {
167 break;
168 }
169 }
170
171 return foundAFile;
172}