master ohmu/fastfetch-openbsd / src / common / impl / properties.c
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}