Pārlūkot izejas kodu

libdpkg: New command module

This should ease refactoring some of the duplicate code dealing with
command execution. It will also make the code clearer.
Guillem Jover 16 gadi atpakaļ
vecāks
revīzija
08c0dd2529

+ 1 - 0
lib/dpkg/Makefile.am

@@ -22,6 +22,7 @@ libdpkg_a_SOURCES = \
 	ar.c ar.h \
 	ar.c ar.h \
 	buffer.c buffer.h \
 	buffer.c buffer.h \
 	cleanup.c \
 	cleanup.c \
+	command.c command.h \
 	compress.c compress.h \
 	compress.c compress.h \
 	database.c \
 	database.c \
 	dbmodify.c \
 	dbmodify.c \

+ 184 - 0
lib/dpkg/command.c

@@ -0,0 +1,184 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * command.c - command execution support
+ *
+ * Copyright © 2010 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dpkg/dpkg.h>
+#include <dpkg/i18n.h>
+#include <dpkg/command.h>
+
+/**
+ * Initialize a command structure.
+ *
+ * If name is NULL, then the last component of the filename path will be
+ * used to initialize the name member.
+ *
+ * @param cmd The command structure to initialize.
+ * @param filename The filename of the command to execute.
+ * @param name The description of the command to execute.
+ */
+void
+command_init(struct command *cmd, const char *filename, const char *name)
+{
+	cmd->filename = filename;
+	if (name == NULL) {
+		const char *progname = strrchr(filename, '/');
+
+		cmd->name = progname ? progname + 1 : filename;
+	} else
+		cmd->name = name;
+	cmd->argc = 0;
+	cmd->argv_size = 10;
+	cmd->argv = m_malloc(cmd->argv_size * sizeof(const char *));
+	cmd->argv[0] = NULL;
+}
+
+/**
+ * Destroy a command structure.
+ *
+ * Free the members managed by the command functions (i.e. the argv pointer
+ * array), and zero all members of a command structure.
+ *
+ * @param cmd The command structure to free.
+ */
+void
+command_destroy(struct command *cmd)
+{
+	cmd->filename = NULL;
+	cmd->name = NULL;
+	cmd->argc = 0;
+	cmd->argv_size = 0;
+	free(cmd->argv);
+	cmd->argv = NULL;
+}
+
+static void
+command_grow_argv(struct command *cmd, int need)
+{
+	/* Check if we already have enough room. */
+	if ((cmd->argv_size - cmd->argc) >= need)
+		return;
+
+	cmd->argv_size = (cmd->argv_size + need) * 2;
+	cmd->argv = m_realloc(cmd->argv, cmd->argv_size * sizeof(const char *));
+}
+
+/**
+ * Append an argument to the command's argv.
+ *
+ * @param cmd The command structure to act on.
+ * @param arg The argument to append to argv.
+ */
+void
+command_add_arg(struct command *cmd, const char *arg)
+{
+	command_grow_argv(cmd, 1);
+
+	cmd->argv[cmd->argc++] = arg;
+	cmd->argv[cmd->argc] = NULL;
+}
+
+/**
+ * Append an argument array to the command's argv.
+ *
+ * @param cmd The command structure to act on.
+ * @param argv The NULL terminated argument array to append to argv.
+ */
+void
+command_add_argl(struct command *cmd, const char **argv)
+{
+	int i, add_argc = 0;
+
+	while (argv[add_argc] != NULL)
+		add_argc++;
+
+	command_grow_argv(cmd, add_argc);
+
+	for (i = 0; i < add_argc; i++)
+		cmd->argv[cmd->argc++] = argv[i];
+
+	cmd->argv[cmd->argc] = NULL;
+}
+
+/**
+ * Append a va_list of argument to the command's argv.
+ *
+ * @param cmd The command structure to act on.
+ * @param al The NULL terminated va_list of argument array to append to argv.
+ */
+void
+command_add_argv(struct command *cmd, va_list al)
+{
+	va_list at;
+	int i, add_argc = 0;
+
+	va_copy(at, al);
+	while (va_arg(at, const char *) != NULL)
+		add_argc++;
+	va_end(at);
+
+	command_grow_argv(cmd, add_argc);
+
+	for (i = 0; i < add_argc; i++)
+		cmd->argv[cmd->argc++] = va_arg(al, const char *);
+
+	cmd->argv[cmd->argc] = NULL;
+}
+
+/**
+ * Append a variable list of argument to the command's argv.
+ *
+ * @param cmd The command structure to act on.
+ * @param ... The NULL terminated variable list of argument to append to argv.
+ */
+void
+command_add_args(struct command *cmd, ...)
+{
+	va_list al;
+
+	va_start(al, cmd);
+	command_add_argv(cmd, al);
+	va_end(al);
+}
+
+/**
+ * Execute the command specified.
+ *
+ * The command is executed searching the PATH if the filename does not
+ * contain any slashes, or using the full path if it's either a relative or
+ * absolute pathname. This functions does not return.
+ *
+ * @param cmd The command structure to act on.
+ */
+void
+command_exec(struct command *cmd)
+{
+	if (strchr(cmd->filename, '/'))
+		execv(cmd->filename, (char * const *)cmd->argv);
+	else
+		execvp(cmd->filename, (char * const *)cmd->argv);
+	ohshite(_("unable to execute %s (%s)"), cmd->name, cmd->filename);
+}
+

+ 54 - 0
lib/dpkg/command.h

@@ -0,0 +1,54 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * command.h - command execution support
+ *
+ * Copyright © 2010 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBDPKG_COMMAND_H
+#define LIBDPKG_COMMAND_H
+
+#include <dpkg/macros.h>
+
+DPKG_BEGIN_DECLS
+
+/**
+ * Describe a command to execute.
+ */
+struct command {
+	/** Descriptive name of the command, used when printing. */
+	const char *name;
+	/** Filename to execute; either a path or the progname. */
+	const char *filename;
+	int argc;
+	int argv_size;
+	const char **argv;
+};
+
+void command_init(struct command *cmd, const char *filename, const char *name);
+void command_destroy(struct command *cmd);
+
+void command_add_arg(struct command *cmd, const char *arg);
+void command_add_argl(struct command *cmd, const char **argv);
+void command_add_argv(struct command *cmd, va_list al);
+void command_add_args(struct command *cmd, ...) DPKG_ATTR_SENTINEL;
+
+void command_exec(struct command *cmd) DPKG_ATTR_NORET;
+
+DPKG_END_DECLS
+
+#endif /* LIBDPKG_COMMAND_H */
+

+ 1 - 0
lib/dpkg/test/.gitignore

@@ -1,5 +1,6 @@
 t-ar
 t-ar
 t-buffer
 t-buffer
+t-command
 t-macros
 t-macros
 t-path
 t-path
 t-pkginfo
 t-pkginfo

+ 2 - 0
lib/dpkg/test/Makefile.am

@@ -13,6 +13,7 @@ check_PROGRAMS = \
 	t-string \
 	t-string \
 	t-buffer \
 	t-buffer \
 	t-path \
 	t-path \
+	t-command \
 	t-varbuf \
 	t-varbuf \
 	t-ar \
 	t-ar \
 	t-version \
 	t-version \
@@ -21,6 +22,7 @@ check_PROGRAMS = \
 CHECK_LDADD = ../libdpkg.a
 CHECK_LDADD = ../libdpkg.a
 
 
 t_ar_LDADD = $(CHECK_LDADD)
 t_ar_LDADD = $(CHECK_LDADD)
+t_command_LDADD = $(CHECK_LDADD)
 t_macros_LDADD = $(CHECK_LDADD)
 t_macros_LDADD = $(CHECK_LDADD)
 t_path_LDADD = $(CHECK_LDADD)
 t_path_LDADD = $(CHECK_LDADD)
 t_pkginfo_LDADD = $(CHECK_LDADD)
 t_pkginfo_LDADD = $(CHECK_LDADD)

+ 140 - 0
lib/dpkg/test/t-command.c

@@ -0,0 +1,140 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-command.c - test command implementation
+ *
+ * Copyright © 2010 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <dpkg/test.h>
+#include <dpkg/command.h>
+
+static void
+test_command_init(void)
+{
+	struct command cmd;
+
+	command_init(&cmd, "/absolute/path/to/progname", NULL);
+	test_str(cmd.filename, ==, "/absolute/path/to/progname");
+	test_str(cmd.name, ==, "progname");
+	test_pass(cmd.argc == 0);
+	test_pass(cmd.argv[0] == NULL);
+
+	command_destroy(&cmd);
+	test_pass(cmd.filename == NULL);
+	test_pass(cmd.name == NULL);
+	test_pass(cmd.argc == 0);
+	test_pass(cmd.argv == NULL);
+
+	command_init(&cmd, "progname", NULL);
+	test_str(cmd.filename, ==, "progname");
+	test_str(cmd.name, ==, "progname");
+	test_pass(cmd.argc == 0);
+	test_pass(cmd.argv[0] == NULL);
+
+	command_destroy(&cmd);
+
+	command_init(&cmd, "progname", "description");
+	test_str(cmd.filename, ==, "progname");
+	test_str(cmd.name, ==, "description");
+	test_pass(cmd.argc == 0);
+	test_pass(cmd.argv[0] == NULL);
+
+	command_destroy(&cmd);
+}
+
+static void
+test_command_add_arg(void)
+{
+	struct command cmd;
+
+	command_init(&cmd, "test", NULL);
+
+	command_add_arg(&cmd, "arg 0");
+	test_pass(cmd.argc == 1);
+	test_str(cmd.argv[0], ==, "arg 0");
+	test_pass(cmd.argv[1] == NULL);
+
+	command_add_arg(&cmd, "arg 1");
+	test_pass(cmd.argc == 2);
+	test_str(cmd.argv[0], ==, "arg 0");
+	test_str(cmd.argv[1], ==, "arg 1");
+	test_pass(cmd.argv[2] == NULL);
+
+	command_add_arg(&cmd, "arg 2");
+	test_pass(cmd.argc == 3);
+	test_str(cmd.argv[0], ==, "arg 0");
+	test_str(cmd.argv[1], ==, "arg 1");
+	test_str(cmd.argv[2], ==, "arg 2");
+	test_pass(cmd.argv[3] == NULL);
+
+	command_destroy(&cmd);
+}
+
+static void
+test_command_add_argl(void)
+{
+	struct command cmd;
+	const char *args[] = {
+		"arg 1",
+		"arg 2",
+		"arg 3",
+		NULL,
+	};
+
+	command_init(&cmd, "test", NULL);
+
+	command_add_arg(&cmd, "arg 0");
+
+	command_add_argl(&cmd, args);
+	test_pass(cmd.argc == 4);
+	test_str(cmd.argv[0], ==, "arg 0");
+	test_str(cmd.argv[1], ==, "arg 1");
+	test_str(cmd.argv[2], ==, "arg 2");
+	test_str(cmd.argv[3], ==, "arg 3");
+	test_pass(cmd.argv[4] == NULL);
+
+	command_destroy(&cmd);
+}
+
+static void
+test_command_add_args(void)
+{
+	struct command cmd;
+
+	command_init(&cmd, "test", NULL);
+
+	command_add_arg(&cmd, "arg 0");
+
+	command_add_args(&cmd, "arg 1", "arg 2", "arg 3", NULL);
+	test_pass(cmd.argc == 4);
+	test_str(cmd.argv[0], ==, "arg 0");
+	test_str(cmd.argv[1], ==, "arg 1");
+	test_str(cmd.argv[2], ==, "arg 2");
+	test_str(cmd.argv[3], ==, "arg 3");
+	test_pass(cmd.argv[4] == NULL);
+
+	command_destroy(&cmd);
+}
+
+static void
+test(void)
+{
+	test_command_init();
+	test_command_add_arg();
+	test_command_add_argl();
+	test_command_add_args();
+}
+

+ 1 - 0
po/POTFILES.in

@@ -3,6 +3,7 @@
 lib/dpkg/ar.c
 lib/dpkg/ar.c
 lib/dpkg/buffer.c
 lib/dpkg/buffer.c
 lib/dpkg/cleanup.c
 lib/dpkg/cleanup.c
+lib/dpkg/command.c
 lib/dpkg/compression.c
 lib/dpkg/compression.c
 lib/dpkg/database.c
 lib/dpkg/database.c
 lib/dpkg/dbmodify.c
 lib/dpkg/dbmodify.c