From a044e78188411351d798842df1778316c881f5ed Mon Sep 17 00:00:00 2001
From: Andre Noll <maan@systemlinux.org>
Date: Sun, 23 Sep 2007 17:17:45 +0200
Subject: [PATCH] Implement check command.

Currently it only checks the audio file table. Checking moods
and playlists will be implemented in a subsequent patch.
---
 afs.c   | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 afs.cmd |  6 ++++++
 afs.h   |  1 +
 aft.c   | 47 ++++++++++++++++++++++++++++++++++++++++++++
 error.h |  1 +
 5 files changed, 110 insertions(+), 5 deletions(-)

diff --git a/afs.c b/afs.c
index 6d51d4f5..44eeacc9 100644
--- a/afs.c
+++ b/afs.c
@@ -144,11 +144,12 @@ int send_callback_request(callback_function *f, struct osl_object *query,
 	int ret, fd = -1, query_shmid, result_shmid;
 	void *query_shm, *result_shm;
 	char buf[sizeof(afs_socket_cookie) + sizeof(int)];
-//	char *tmpsocket_name;
 	struct sockaddr_un unix_addr;
+	size_t query_shm_size = sizeof(*cq);
 
-	assert(query->data && query->size);
-	ret = shm_new(query->size + sizeof(*cq));
+	if (query)
+		query_shm_size += query->size;
+	ret = shm_new(query_shm_size);
 	if (ret < 0)
 		return ret;
 	query_shmid = ret;
@@ -157,9 +158,10 @@ int send_callback_request(callback_function *f, struct osl_object *query,
 		goto out;
 	cq = query_shm;
 	cq->handler = f;
-	cq->query_size = query->size;
+	cq->query_size = query_shm_size - sizeof(*cq);
 
-	memcpy(query_shm + sizeof(*cq), query->data, query->size);
+	if (query)
+		memcpy(query_shm + sizeof(*cq), query->data, query->size);
 	ret = shm_detach(query_shm);
 	if (ret < 0)
 		goto out;
@@ -855,3 +857,51 @@ int com_init(int fd, int argc, char * const * const argv)
 		return ret;
 	return send_va_buffer(fd, "successfully created afs table(s)\n");
 }
+
+enum com_check_flags {
+	CHECK_AFT = 1,
+	CHECK_MOODS_TABLE = 8,
+	CHECK_PLAYLISTS = 16
+};
+
+int com_check(int fd, int argc, char * const * const argv)
+{
+	unsigned flags = 0;
+	int i, ret;
+	struct osl_object result;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (arg[0] != '-')
+			break;
+		if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		if (!strcmp(arg, "-a")) {
+			flags |= CHECK_AFT;
+			continue;
+		}
+		if (!strcmp(arg, "-p")) {
+			flags |= CHECK_PLAYLISTS;
+			continue;
+		}
+		return -E_AFS_SYNTAX;
+	}
+	if (i < argc)
+		return -E_AFS_SYNTAX;
+	if (!flags)
+		flags = ~0U;
+	if (flags & CHECK_AFT) {
+		ret = send_callback_request(aft_check_callback, NULL, &result);
+		if (ret < 0)
+			return ret;
+		if (ret > 0) {
+			ret = send_buffer(fd, (char *) result.data);
+			free(result.data);
+			if (ret < 0)
+				return ret;
+		}
+	}
+	return 1;
+}
diff --git a/afs.cmd b/afs.cmd
index 9a4bbdcd..b3d3ec91 100644
--- a/afs.cmd
+++ b/afs.cmd
@@ -101,6 +101,12 @@ U: addatt attribute1 [attribute2 ...]
 H: This adds new attributes to the attribute table. At most 64 attributes
 H: may be defined.
 ---
+N: check
+P: AFS_READ
+D: Run integrity checks for the osl tables.
+U: check [-a] [-m] [-p]
+H: FIXME
+---
 N: rmatt
 P: AFS_READ | AFS_WRITE
 D: FIXME
diff --git a/afs.h b/afs.h
index ba8964c7..7adb54bd 100644
--- a/afs.h
+++ b/afs.h
@@ -137,6 +137,7 @@ int get_afsi_of_row(const struct osl_row *row, struct afs_info *afsi);
 int get_audio_file_path_of_row(const struct osl_row *row, char **path);
 int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj);
 int audio_file_loop(void *private_data, osl_rbtree_loop_func *func);
+int aft_check_callback(const struct osl_object *query, struct osl_object *result);
 
 /* mood */
 int mood_open(char *mood_name);
diff --git a/aft.c b/aft.c
index 423731f6..1a0fdc79 100644
--- a/aft.c
+++ b/aft.c
@@ -1695,6 +1695,53 @@ err:
 	return ret;
 }
 
+/* TODO: optionally fix problems by removing offending rows */
+static int check_audio_file(struct osl_row *row, void *data)
+{
+	char *path;
+	struct para_buffer *pb = data;
+	struct stat statbuf;
+	int ret = get_audio_file_path_of_row(row, &path);
+	struct afs_info afsi;
+	char *blob_name;
+
+	if (ret < 0) {
+		para_printf(pb, "%s\n", PARA_STRERROR(-ret));
+		return 1;
+	}
+	if (stat(path, &statbuf) < 0)
+		para_printf(pb, "%s: stat error (%s)\n", path, strerror(errno));
+	else {
+		if (!S_ISREG(statbuf.st_mode))
+			para_printf(pb, "%s: not a regular file\n", path);
+	}
+	ret = get_afsi_of_row(row, &afsi);
+	if (ret < 0) {
+		para_printf(pb, "%s: %s\n", path, PARA_STRERROR(-ret));
+		return 1;
+	}
+	ret = lyr_get_name_by_id(afsi.lyrics_id, &blob_name);
+	if (ret < 0)
+		para_printf(pb, "%s lyrics id: %s\n", path, PARA_STRERROR(-ret));
+	ret = img_get_name_by_id(afsi.image_id, &blob_name);
+	if (ret < 0)
+		para_printf(pb, "%s image id: %s\n", path, PARA_STRERROR(-ret));
+	return 1;
+}
+
+int aft_check_callback(__a_unused const struct osl_object *query, struct osl_object *result)
+{
+	struct para_buffer pb = {.buf = NULL};
+
+	audio_file_loop(&pb, check_audio_file);
+	if (!pb.size)
+		return 0;
+	result->data = pb.buf;
+	result->size = pb.size;
+	return 1;
+
+}
+
 /**
  * Close the audio file table.
  *
diff --git a/error.h b/error.h
index 7d2424cf..ca97dba6 100644
--- a/error.h
+++ b/error.h
@@ -154,6 +154,7 @@ extern const char **para_errlist[];
 	PARA_ERROR(INPUT_TOO_LARGE, "input too large for stdin command"), \
 	PARA_ERROR(READ, "read error"), \
 	PARA_ERROR(ATOL, "failed to convert to long"), \
+	PARA_ERROR(AFS_SYNTAX, "afs syntax error"), \
 
 
 #define MOOD_ERRORS \
-- 
2.39.5