From: Mark Yacoub <markyacoub@google.com>
To: igt-dev@lists.freedesktop.org
Cc: louis.chauvet@bootlin.com, Mark Yacoub <markyacoub@google.com>
Subject: [PATCH 1/3] android: Implement lightweight GKeyFile INI string parser in glib shim
Date: Wed, 15 Jul 2026 15:29:47 -0400 [thread overview]
Message-ID: <20260715192949.1984280-1-markyacoub@google.com> (raw)
[Why]
Android's Bionic C library lacks libglib2, requiring IGT to build against
an Android-specific mock header (include/android/glib.h). The existing mock
implementation of GKeyFile and g_key_file_load_from_file is a hardcoded
no-op returning NULL, which prevents tests relying on configuration files
(such as .igtrc) from dynamically retrieving values on Android.
[How]
Expand the dummy GKeyFile structure to record the file path upon calling
g_key_file_load_from_file. Implement a minimal internal INI parser function
(__android_g_key_file_get) inside g_key_file_get_string to traverse the file
line-by-line, matching requested [group] and key strings, stripping trailing
newlines, and returning a heap-allocated duplicate string (strdup).
Signed-off-by: Mark Yacoub <markyacoub@google.com>
---
include/android/glib.h | 120 +++++++++++++++++++++++++++++++++++++----
1 file changed, 111 insertions(+), 9 deletions(-)
diff --git a/include/android/glib.h b/include/android/glib.h
index 0b6658287..5b8ba86c7 100644
--- a/include/android/glib.h
+++ b/include/android/glib.h
@@ -16,8 +16,7 @@ typedef struct _GError {
} GError;
typedef struct _GKeyFile {
- char *key;
- char *value;
+ char path[512];
} GKeyFile;
typedef struct _GMatchInfo {
@@ -45,16 +44,119 @@ static inline void g_error_free(GError *error) { }
static inline const char *g_get_home_dir(void) { return "/data/local/tmp/igt"; }
-static inline void g_key_file_free(GKeyFile *file) { }
-static inline GKeyFile *g_key_file_new(void) { return NULL; }
+/*
+ * __android_g_key_file_get: Minimal INI parser to mock GLib's GKeyFile getters.
+ *
+ * This function manually traverses a provided INI-style configuration file
+ * (e.g., .igtrc) searching for a specific [group] and key. It parses line-by-line
+ * without relying on the heavy libglib2 parsing dictionary framework natively
+ * required by standard IGT.
+ *
+ * It operates strictly on the specific file path saved in the GKeyFile struct during
+ * g_key_file_load_from_file(). This ensures it properly acts as a decoupled file
+ * parser and prevents it from hijacking other distinct config operations.
+ *
+ * Parameters:
+ * - key_file: The mocked GKeyFile struct holding the parsed file path.
+ * - target_group: The [group] section to search within (e.g. "Unigraf").
+ * - target_key: The key to matching inside the group (e.g. "Connector").
+ *
+ * Returns:
+ * A newly allocated string (`strdup`) containing the right-side value of the
+ * matched key, or NULL if not found. The caller is responsible for freeing it.
+ */
+static inline char *__android_g_key_file_get(GKeyFile *key_file, const char *target_group, const char *target_key)
+{
+ /* Ensure a valid file path was actively loaded into our GKeyFile mock */
+ if (!key_file || !key_file->path[0]) return NULL;
+
+ FILE *fp = fopen(key_file->path, "r");
+ if (!fp)
+ return NULL;
+
+ char line[256];
+ bool in_group = false;
+ size_t key_len = strlen(target_key);
+
+ while (fgets(line, sizeof(line), fp)) {
+ char *trim = line;
+
+ /* Skip any leading whitespace (spaces or tabs) for the current line */
+ while (*trim == ' ' || *trim == '\t') trim++;
+
+ /* Check if this line declares a new [Group] section */
+ if (trim[0] == '[') {
+ /* Verify if it perfectly matches the user's requested [target_group] */
+ in_group = (strncmp(trim + 1, target_group, strlen(target_group)) == 0 &&
+ trim[1 + strlen(target_group)] == ']');
+ continue; /* Move to the next line of the file */
+ }
+
+ /* If we are inside the correct group, evaluate if the line starts with `target_key=` */
+ if (in_group && strncmp(trim, target_key, key_len) == 0 && trim[key_len] == '=') {
+ /* Pointer to the start of the value (immediately right of '=') */
+ char *val = trim + key_len + 1;
+ char *end = val + strlen(val);
+
+ /* Strip any trailing whitespace, carriage returns, or newlines by inserting null terminators */
+ while (end > val && (end[-1] == '\r' || end[-1] == '\n' || end[-1] == ' ' || end[-1] == '\t'))
+ end[-1] = '\0';
+
+ fclose(fp);
+ /* Return a standard heap-allocated copy of the value string just as GLib would */
+ return strdup(val);
+ }
+ }
+
+ fclose(fp);
+ return NULL;
+}
+
+static inline void g_key_file_free(GKeyFile *file) { free(file); }
+
+static inline GKeyFile *g_key_file_new(void) { return (GKeyFile *)calloc(1, sizeof(GKeyFile)); }
+
static inline int g_key_file_get_integer(GKeyFile *key_file,
- const char *group_name, const char *key, GError **error) { return 0; }
+ const char *group_name, const char *key, GError **error) {
+ char *val = __android_g_key_file_get(key_file, group_name, key);
+ if (!val) return 0;
+ int res = atoi(val);
+ free(val);
+ return res;
+}
+
static inline char *g_key_file_get_string(GKeyFile *key_file,
- const char *group_name, const char *key, GError **error) { return NULL; }
+ const char *group_name, const char *key, GError **error) {
+ return __android_g_key_file_get(key_file, group_name, key);
+}
+
static inline double g_key_file_get_double(GKeyFile *key_file,
- const char *group_name, const char *key, GError **error) { return 0.0; }
-static inline bool g_key_file_load_from_file(GKeyFile *key_file,
- const char *file, int flags, GError **error) { return false; }
+ const char *group_name, const char *key, GError **error) {
+ char *val = __android_g_key_file_get(key_file, group_name, key);
+ if (!val) return 0.0;
+ double res = strtod(val, NULL);
+ free(val);
+ return res;
+}
+
+static inline bool g_key_file_get_boolean(GKeyFile *key_file,
+ const char *group_name, const char *key, GError **error) {
+ char *val = __android_g_key_file_get(key_file, group_name, key);
+ if (!val) return false;
+ bool res = (strcmp(val, "true") == 0 || strcmp(val, "1") == 0 || strcmp(val, "yes") == 0);
+ free(val);
+ return res;
+}
+
+static inline bool
+ g_key_file_load_from_file(GKeyFile *key_file,
+ const char *file, int flags, GError **error) {
+ if (key_file && file) {
+ snprintf(key_file->path, sizeof(key_file->path), "%s", file);
+ return true;
+ }
+ return false;
+}
static inline GRegex *g_regex_new(const char *pattern, int compile_options,
int match_options, GError **error) { return NULL; }
--
2.55.0.141.g00534a21ce-goog
next reply other threads:[~2026-07-15 19:30 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-15 19:29 Mark Yacoub [this message]
2026-07-15 19:29 ` [PATCH 2/3] tests/unigraf: Fix DRM master and heap memory leaks in tests Mark Yacoub
2026-07-15 19:29 ` [PATCH 3/3] tests/unigraf: Enhance link rate support checking and hardware retrain timing Mark Yacoub
2026-07-15 20:27 ` ✓ Xe.CI.BAT: success for series starting with [1/3] android: Implement lightweight GKeyFile INI string parser in glib shim Patchwork
2026-07-15 20:54 ` ✓ i915.CI.BAT: " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260715192949.1984280-1-markyacoub@google.com \
--to=markyacoub@google.com \
--cc=igt-dev@lists.freedesktop.org \
--cc=louis.chauvet@bootlin.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox