From 19aa8f50c7f6243de263db7c4a61d23fce575b5a Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 7 Feb 2008 00:14:20 +0000 Subject: [PATCH] Massive advances in cowsay technology git-svn-id: http://svn.nickg.me.uk/work/xcowsay@272 a97b1542-0b21-0410-a459-e47997c36f34 --- Cowsay_glue.h | 120 ++++++++++++++++++++++++++ Makefile.am | 7 +- config.h.in | 52 +++++++++++ configure.ac | 30 ++++++- display_cow.c | 10 ++- settings.c | 71 +++++++++++---- settings.h | 3 + xcowsay.c | 69 +++++++++++---- xcowsayd.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++ xcowsayd.h | 8 ++ 10 files changed, 563 insertions(+), 42 deletions(-) create mode 100644 Cowsay_glue.h create mode 100644 xcowsayd.c create mode 100644 xcowsayd.h diff --git a/Cowsay_glue.h b/Cowsay_glue.h new file mode 100644 index 0000000..de015b5 --- /dev/null +++ b/Cowsay_glue.h @@ -0,0 +1,120 @@ +/* Generated by dbus-binding-tool; do not edit! */ + + +#ifndef __dbus_glib_marshal_cowsay_MARSHAL_H__ +#define __dbus_glib_marshal_cowsay_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* BOOLEAN:STRING,POINTER (/tmp/dbus-binding-tool-c-marshallers.330A6T:1) */ +extern void dbus_glib_marshal_cowsay_BOOLEAN__STRING_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +void +dbus_glib_marshal_cowsay_BOOLEAN__STRING_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_POINTER) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_BOOLEAN__STRING_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__STRING_POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_pointer (param_values + 2), + data2); + + g_value_set_boolean (return_value, v_return); +} + +G_END_DECLS + +#endif /* __dbus_glib_marshal_cowsay_MARSHAL_H__ */ + +#include +static const DBusGMethodInfo dbus_glib_cowsay_methods[] = { + { (GCallback) cowsay_show_cow, dbus_glib_marshal_cowsay_BOOLEAN__STRING_POINTER, 0 }, +}; + +const DBusGObjectInfo dbus_glib_cowsay_object_info = { + 0, + dbus_glib_cowsay_methods, + 1, +"uk.me.doof.Cowsay\0ShowCow\0S\0mess\0I\0s\0\0\0", +"\0", +"\0" +}; + diff --git a/Makefile.am b/Makefile.am index d7f55ef..85ca78d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,11 @@ bin_PROGRAMS = xcowsay -xcowsay_CFLAGS = $(XCOWSAY_CFLAGS) -xcowsay_LDADD = $(XCOWSAY_LIBS) +AM_CFLAGS = $(XCOWSAY_CFLAGS) -DDATADIR=\"$(pkgdatadir)\" +LDADD = $(XCOWSAY_LIBS) xcowsay_SOURCES = xcowsay.c display_cow.c display_cow.h floating_shape.h \ - floating_shape.c settings.h settings.c + floating_shape.c settings.h settings.c Cowsay_glue.h xcowsayd.h \ + xcowsayd.c dist_pkgdata_DATA = cow_small.png cow_med.png cow_large.png EXTRA_DIST = cow.svg \ No newline at end of file diff --git a/config.h.in b/config.h.in index c364eda..3de8d60 100644 --- a/config.h.in +++ b/config.h.in @@ -1,5 +1,45 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + /* Name of package */ #undef PACKAGE @@ -18,5 +58,17 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + /* Version number of package */ #undef VERSION + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/configure.ac b/configure.ac index 6937fea..e52f815 100644 --- a/configure.ac +++ b/configure.ac @@ -1,12 +1,38 @@ -AC_INIT([xcowsay], [0.2], +AC_INIT([xcowsay], [0.2.1], [Nick Gasson ], [xcowsay]) AM_INIT_AUTOMAKE([-Wall]) AC_PROG_CC AC_PROG_INSTALL +# Checks for libraries. + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([stdlib.h string.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_CHECK_FUNCS([strtol]) + # Check for pkg-config packages -PKG_CHECK_MODULES(XCOWSAY, gtk+-2.0) +modules="gtk+-2.0" +xcowsayd_modules="$modules dbus-glib-1 gthread-2.0" +AC_ARG_ENABLE(dbus, + [AS_HELP_STRING([--disable-dbus], [Do not build DBus daemon.])], + [if test "$enableval" != "no" ; then + modules="$xcowsayd_modules" + fi + CFLAGS="$CFLAGS -DWITHOUT_DBUS"], + [modules="$xcowsayd_modules"]) + +echo $modules +PKG_CHECK_MODULES(XCOWSAY, $modules) AC_CONFIG_HEADERS([config.h]) AC_OUTPUT(Makefile) diff --git a/display_cow.c b/display_cow.c index 87b904e..19ecabb 100644 --- a/display_cow.c +++ b/display_cow.c @@ -51,11 +51,15 @@ static cowstate_t next_state(cowstate_t state) } } +#define MAX_COW_PATH 256 static GdkPixbuf *load_cow() { - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("cow_med.png", NULL); + char cow_path[MAX_COW_PATH]; + snprintf(cow_path, MAX_COW_PATH, "%s/cow_med.png", DATADIR); + + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(cow_path, NULL); if (NULL == pixbuf) { - fprintf(stderr, "Failed to load cow image!\n"); + fprintf(stderr, "Failed to load cow image: %s\n", cow_path); exit(EXIT_FAILURE); } return pixbuf; @@ -243,7 +247,7 @@ static gboolean tick(gpointer data) } } - return TRUE; + return (xcowsay.state != csCleanup); } void cowsay_init(int *argc, char ***argv) diff --git a/settings.c b/settings.c index 53a1012..057f711 100644 --- a/settings.c +++ b/settings.c @@ -45,39 +45,52 @@ static option_t *get_option(const char *name) exit(EXIT_FAILURE); } -int get_int_option(const char *name) + +static void assert_string(const option_t *opt) { - option_t *opt = get_option(name); - if (optInt == opt->type) - return opt->u.ival; - else { - fprintf(stderr, "Error: Option %s is not of type integer\n", name); + if (optString != opt->type) { + fprintf(stderr, "Error: Option %s is not of type string\n", opt->name); exit(EXIT_FAILURE); } } -bool get_bool_option(const char *name) +static void assert_int(const option_t *opt) { - option_t *opt = get_option(name); - if (optBool == opt->type) - return opt->u.bval; - else { - fprintf(stderr, "Error: Option %s is not of type Boolean\n", name); + if (optInt != opt->type) { + fprintf(stderr, "Error: Option %s is not of type integer\n", opt->name); exit(EXIT_FAILURE); } } -const char *get_string_option(const char *name) +static void assert_bool(const option_t *opt) { - option_t *opt = get_option(name); - if (optString == opt->type) - return opt->u.sval; - else { - fprintf(stderr, "Error: Option %s is not of type string\n", name); + if (optBool != opt->type) { + fprintf(stderr, "Error: Option %s is not of type Boolean\n", opt->name); exit(EXIT_FAILURE); } } +int get_int_option(const char *name) +{ + option_t *opt = get_option(name); + assert_int(opt); + return opt->u.ival; +} + +bool get_bool_option(const char *name) +{ + option_t *opt = get_option(name); + assert_bool(opt); + return opt->u.bval; +} + +const char *get_string_option(const char *name) +{ + option_t *opt = get_option(name); + assert_string(opt); + return opt->u.sval; +} + static void add_option(const char *name, option_type_t type, option_value_t def) { option_t opt = { type, def, name }; @@ -114,3 +127,25 @@ void add_string_option(const char *name, const char *sval) u.sval = copy_string(sval); add_option(name, optString, u); } + +void set_int_option(const char *name, int ival) +{ + option_t *opt = get_option(name); + assert_int(opt); + opt->u.ival = ival; +} + +void set_bool_option(const char *name, bool bval) +{ + option_t *opt = get_option(name); + assert_bool(opt); + opt->u.bval = bval; +} + +void set_string_option(const char *name, const char *sval) +{ + option_t *opt = get_option(name); + assert_string(opt); + free(opt->u.sval); + opt->u.sval = sval; +} diff --git a/settings.h b/settings.h index b63c9f5..71f0984 100644 --- a/settings.h +++ b/settings.h @@ -12,5 +12,8 @@ int get_int_option(const char *name); bool get_bool_option(const char *name); const char *get_string_option(const char *name); +void set_int_option(const char *name, int ival); +void set_bool_option(const char *name, bool bval); +void set_string_option(const char *name, const char *sval); #endif diff --git a/xcowsay.c b/xcowsay.c index 9185b76..df7082a 100644 --- a/xcowsay.c +++ b/xcowsay.c @@ -5,7 +5,7 @@ #include "display_cow.h" #include "settings.h" - +#include "xcowsayd.h" // Default settings #define DEF_LEAD_IN_TIME 250 @@ -15,9 +15,15 @@ #define MAX_STDIN 4096 // Maximum chars to read from stdin +static int daemon_flag = 0; +static int daemon_debug = 0; + static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"time", required_argument, 0, 't'}, + {"font", required_argument, 0, 'f'}, + {"daemon", no_argument, &daemon_flag, 1}, + {"debug", no_argument, &daemon_debug, 1}, {0, 0, 0, 0} }; @@ -38,31 +44,55 @@ static void read_from_stdin(void) static void usage() { static const char *usage_message = - "Usage: xcowsay [OPTION] [MESSAGE]\n" - "Display a cow on your desktop with [MESSAGE] or standard input.\n\n" + "Usage: xcowsay [OPTION]... [MESSAGE]\n" + "Display a cow on your desktop with MESSAGE or standard input.\n\n" "Options:\n" " -h, --help\t\tDisplay this message and exit.\n" - " -t, --time SECONDS\tDisplay message for SECONDS seconds.\n"; + " -t, --time=SECONDS\tDisplay message for SECONDS seconds.\n" + " -f, --font=FONT\tSet message font (Pango format).\n" + " -d, --daemon\t\tRun xcowsay in daemon mode.\n" + " --debug\t\tKeep daemon attached to terminal.\n"; puts(usage_message); } +static int parse_int_option(const char *optarg) +{ + char *endptr; + int r = strtol(optarg, &endptr, 10); + if ('\0' == *endptr) + return r; + else { + fprintf(stderr, "Error: %s is not a valid integer\n", optarg); + exit(EXIT_FAILURE); + } +} + int main(int argc, char **argv) -{ +{ add_int_option("lead_in_time", DEF_LEAD_IN_TIME); add_int_option("display_time", DEF_DISPLAY_TIME); add_int_option("lead_out_time", get_int_option("lead_in_time")); add_string_option("font", DEF_FONT); - - cowsay_init(&argc, &argv); - int c, option_index = 0, failure = 0; - while ((c = getopt_long(argc, argv, "h", long_options, &option_index)) != -1) { + int c, index = 0, failure = 0; + const char *spec = "hdt:f:"; + while ((c = getopt_long(argc, argv, spec, long_options, &index)) != -1) { switch (c) { + case 0: + // Set a flag + break; + case 'd': + daemon_flag = 1; + break; case 'h': usage(); exit(EXIT_SUCCESS); case 't': + set_int_option("display_time", parse_int_option(optarg)*1000); break; + case 'f': + set_string_option("font", optarg); + break; case '?': // getopt_long already printed an error message failure = 1; @@ -75,15 +105,22 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); srand((unsigned)time(NULL)); - - if (optind == argc) { - read_from_stdin(); - } - else if (optind == argc - 1) { - display_cow(argv[optind]); + + if (daemon_flag) { + run_cowsay_daemon(daemon_debug, argc, argv); } else { - fprintf(stderr, "Error: Too many arguments\n"); + cowsay_init(&argc, &argv); + + if (optind == argc) { + read_from_stdin(); + } + else if (optind == argc - 1) { + display_cow(argv[optind]); + } + else { + fprintf(stderr, "Error: Too many arguments\n"); + } } return EXIT_SUCCESS; diff --git a/xcowsayd.c b/xcowsayd.c new file mode 100644 index 0000000..bffc954 --- /dev/null +++ b/xcowsayd.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include + +#include "xcowsayd.h" + +#ifndef WITHOUT_DBUS + +#include + +#include "display_cow.h" + +typedef struct { + GObject parent; + DBusGConnection *connection; +} Cowsay; + +typedef struct { + GObjectClass parent_class; +} CowsayClass; + + +static void cowsayd_init(Cowsay *server); +static void cowsayd_class_init(CowsayClass *class); + +static gboolean cowsay_show_cow(Cowsay *obj, const gchar *mess, GError **error); + +G_DEFINE_TYPE(Cowsay, cowsayd, G_TYPE_OBJECT); + +#include "Cowsay_glue.h" + +typedef struct _cowsay_queue_t { + struct _cowsay_queue_t *next; + char *message; +} cowsay_queue_t; + +static cowsay_queue_t *requests = NULL; +static GMutex *queue_lock = NULL; +static GCond *request_ready = NULL; + +#define QUEUE_MUTEX_LOCK g_mutex_lock(queue_lock) +#define QUEUE_MUTEX_UNLOCK g_mutex_unlock(queue_lock) +#define REQUEST_READY_WAIT g_cond_wait(request_ready, queue_lock) +#define REQUEST_READY_SIGNAL g_cond_signal(request_ready) + +static void enqueue_request(const char *mess) +{ + cowsay_queue_t *req = (cowsay_queue_t*)malloc(sizeof(cowsay_queue_t)); + req->next = NULL; + req->message = malloc(strlen(mess)+1); + g_assert(req->message); + strcpy(req->message, mess); + + // Append the request to the end of the queue + QUEUE_MUTEX_LOCK; + { + if (NULL == requests) { + requests = req; + } + else { + cowsay_queue_t *it; + for (it = requests; it->next; it = it->next); + it->next = req; + } + REQUEST_READY_SIGNAL; + } + QUEUE_MUTEX_UNLOCK; +} + +static const char *wait_for_request(void) +{ + const char *r = NULL; + QUEUE_MUTEX_LOCK; + { + while (NULL == requests) { + REQUEST_READY_WAIT; + } + r = requests->message; + } + QUEUE_MUTEX_UNLOCK; + return r; +} + +static void request_complete() +{ + QUEUE_MUTEX_LOCK; + { + cowsay_queue_t *req = requests; + g_assert(req); + requests = req->next; + free(req->message); + free(req); + } + QUEUE_MUTEX_UNLOCK; +} + +static gpointer cow_display_thread(gpointer data) +{ + printf("In the cow display thread\n"); + + for (;;) { + const char *mess = wait_for_request(); + printf("Processing request: %s\n", mess); + + // We need to wrap the GTK+ stuff in gdk_threads_X since + // GTK assumes it is being called from the main thread + // (and it isn't here) + gdk_threads_enter(); + display_cow(mess); + gdk_threads_leave(); + + request_complete(); + } + + return NULL; +} + +static void cowsayd_class_init(CowsayClass *class) +{ + // Nothing to do here +} + +static void cowsayd_init(Cowsay *server) +{ + GError *error = NULL; + DBusGProxy *driver_proxy; + int request_ret; + + // Initialise the DBus connection + server->connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (NULL == server->connection) { + g_warning("Unable to connect to DBus: %s", error->message); + g_error_free(error); + exit(EXIT_FAILURE); + } + + dbus_g_object_type_install_info(cowsayd_get_type(), + &dbus_glib_cowsay_object_info); + + // Register DBus path + dbus_g_connection_register_g_object(server->connection, + "/uk/me/doof/Cowsay", + G_OBJECT(server)); + + // Register the service name + driver_proxy = dbus_g_proxy_new_for_name(server->connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + if (!org_freedesktop_DBus_request_name(driver_proxy, + "uk.me.doof.Cowsay", + 0, &request_ret, &error)) { + g_warning("Unable to register service: %s", error->message); + g_error_free(error); + exit(EXIT_FAILURE); + } + + g_object_unref(driver_proxy); + +} + +static gboolean cowsay_show_cow(Cowsay *obj, const gchar *mess, GError **error) +{ + printf("cowsay_show_cow mess=%s\n", mess); + enqueue_request(mess); + return true; +} + +void run_cowsay_daemon(bool debug, int argc, char **argv) +{ + if (!debug) { + // Fork away from the terminal + int pid = fork(); + if (pid < 0) { + perror("fork"); + exit(EXIT_FAILURE); + } + else if (pid > 0) { + exit(EXIT_SUCCESS); + } + + // Detach from the terminal + if (setsid() == -1) { + perror("setsid"); + exit(EXIT_FAILURE); + } + + // Close existing handles + int fd; + for (fd = 0; fd < getdtablesize(); fd++) + close(fd); + + // Redirect everything to /dev/null + fd = open("/dev/null", O_RDWR); // fd = stdin + if (-1 == fd) + abort(); + + dup(fd); // stdout + dup(fd); // stderr + } + + // g_thread_init must come before all other GLib calls + g_thread_init(NULL); + gdk_threads_init(); + + queue_lock = g_mutex_new(); + request_ready = g_cond_new(); + + cowsay_init(&argc, &argv); + + g_type_init(); + Cowsay *server = g_object_new(cowsayd_get_type(), NULL); + + GThread *displ = g_thread_create(cow_display_thread, NULL, FALSE, NULL); + g_assert(displ); + + printf("Cowsay daemon starting...\n"); + GMainLoop *main_loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(main_loop); + + exit(EXIT_SUCCESS); +} + +#else /* #ifndef WITHOUT_DBUS */ + +void run_cowsay_daemon(bool debug) +{ + fprintf(stderr, "Error: Daemon mode unavailable as xcowsay was compiled " + "without DBus support.\n"); + exit(EXIT_FAILURE); +} + +#endif /* #ifndef WITHOUT_DBUS */ diff --git a/xcowsayd.h b/xcowsayd.h new file mode 100644 index 0000000..c1cb6ed --- /dev/null +++ b/xcowsayd.h @@ -0,0 +1,8 @@ +#ifndef INC_XCOWSAYD_H +#define INC_XCOWSAYD_H + +#include + +void run_cowsay_daemon(bool debug, int argc, char **argv); + +#endif -- 2.39.2