Igt-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
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


             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