Move extcmd code from hooks

diff -r fd72b1e3bf26 mcabber/mcabber/Makefile.am
--- a/mcabber/mcabber/Makefile.am	Wed Nov 12 22:23:32 2014 +0200
+++ b/mcabber/mcabber/Makefile.am	Wed Nov 12 22:24:18 2014 +0200
@@ -7,7 +7,8 @@
 		  xmpp.c xmpp.h xmpp_helper.c xmpp_helper.h xmpp_defines.h \
 		  xmpp_iq.c xmpp_iq.h xmpp_iqrequest.c xmpp_iqrequest.h \
 		  xmpp_muc.c xmpp_muc.h xmpp_s10n.c xmpp_s10n.h \
-		  caps.c caps.h help.c help.h carbons.c carbons.h
+		  caps.c caps.h help.c help.h carbons.c carbons.h \
+		  extcmd.c extcmd.h
 
 if OTR
 mcabber_SOURCES += otr.c otr.h nohtml.c nohtml.h
@@ -43,6 +44,7 @@
 			 xmpp_iq.h xmpp_iqrequest.h \
 			 xmpp_muc.h xmpp_s10n.h \
 			 caps.h fifo.h help.h modules.h api.h \
+			 extcmd.h \
 			 $(top_builddir)/include/config.h
 
 if OTR
diff -r fd72b1e3bf26 mcabber/mcabber/extcmd.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/extcmd.c	Wed Nov 12 22:24:18 2014 +0200
@@ -0,0 +1,152 @@
+/*
+ * extcmd.c      -- External event handler command
+ *
+ * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
+ *
+ * This program 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 program 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <stdlib.h>    // mkstemp
+#include <string.h>    // strlen
+#include <signal.h>    // signal
+#include <sys/types.h> // waitpid
+#include <sys/wait.h>  // waitpid
+#include <unistd.h>    // fork, close, write, execl
+#include <glib.h>
+
+#include "screen.h"
+#include "roster.h"
+#include "settings.h"
+#include "utils.h"
+#include "utf8.h"
+
+#ifndef WAIT_ANY
+# define WAIT_ANY -1
+#endif
+
+static char *extcmd = NULL;
+
+//  ext_sig_handler()
+// SIGCHLD handler.
+// wait()s for any terminated childs (to avoid hanging zombies)
+// and beeps, if child terminates with exit code 2.
+static void ext_sig_handler(int signum)
+{
+  int status;
+  pid_t pid;
+  do {
+    pid = waitpid (WAIT_ANY, &status, WNOHANG);
+    // Check the exit status value if 'eventcmd_checkstatus' is set
+    if (settings_opt_get_int("eventcmd_checkstatus")) {
+      if (pid > 0) {
+        // exit status 2 -> beep
+        if (WIFEXITED(status) && WEXITSTATUS(status) == 2) {
+          scr_beep();
+        }
+      }
+    }
+  } while (pid > 0);
+}
+
+//  hk_ext_cmd_init()
+// Initialize external command variable.
+// Can be called with parameter NULL to reset and free memory
+void hk_ext_cmd_init(const char *command)
+{
+  if (extcmd) {
+    signal(SIGCHLD, SIG_DFL);
+    g_free(extcmd);
+    extcmd = NULL;
+  }
+  if (command) {
+    signal(SIGCHLD, ext_sig_handler);
+    extcmd = expand_filename(command);
+  }
+}
+
+//  hk_ext_cmd()
+// Launch an external command (process) for the given event.
+// For now, data should be NULL.
+void hk_ext_cmd(const char *name, const char *arg_type, const char *arg_info, const char *data)
+{
+  pid_t pid;
+  char *arg_data = NULL;
+  char *datafname = NULL;
+
+  if (!arg_type || !arg_info) return;
+
+  if (*name && settings_opt_get_int("eventcmd_use_nickname"))
+    name = roster_getname(name);
+
+  if (data && settings_opt_get_int("event_log_files")) {
+    int fd;
+    const char *prefix;
+    char *prefix_xp = NULL;
+    char *data_locale;
+
+    data_locale = from_utf8(data);
+    prefix = settings_opt_get("event_log_dir");
+    if (prefix)
+      prefix = prefix_xp = expand_filename(prefix);
+    else
+      prefix = ut_get_tmpdir();
+    datafname = g_strdup_printf("%s/mcabber-%d.XXXXXX", prefix, getpid());
+    g_free(prefix_xp);
+
+    // XXX Some old systems may require us to set umask first.
+    fd = mkstemp(datafname);
+    if (fd == -1) {
+      g_free(datafname);
+      datafname = NULL;
+      scr_LogPrint(LPRINT_LOGNORM,
+                   "Unable to create temp file for external command.");
+    } else {
+      size_t data_locale_len = strlen(data_locale);
+      ssize_t a = write(fd, data_locale, data_locale_len);
+      ssize_t b = write(fd, "\n", 1);
+      if ((size_t)a != data_locale_len || b != 1) {
+        g_free(datafname);
+        datafname = NULL;
+        scr_LogPrint(LPRINT_LOGNORM,
+                     "Unable to write to temp file for external command.");
+      }
+      close(fd);
+      arg_data = datafname;
+    }
+    g_free(data_locale);
+  }
+
+  if ((pid=fork()) == -1) {
+    scr_LogPrint(LPRINT_LOGNORM, "Fork error, cannot launch external command.");
+    g_free(datafname);
+    return;
+  }
+
+  if (pid == 0) { // child
+    // Close standard file descriptors
+    close(STDIN_FILENO);
+    close(STDOUT_FILENO);
+    close(STDERR_FILENO);
+    if (execl(extcmd, extcmd, arg_type, arg_info, name, arg_data,
+              (char *)NULL) == -1) {
+      // scr_LogPrint(LPRINT_LOGNORM, "Cannot execute external command.");
+      exit(EXIT_FAILURE);
+    }
+  }
+  g_free(datafname);
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0 ts=2 sw=2:  For Vim users... */
diff -r fd72b1e3bf26 mcabber/mcabber/extcmd.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/mcabber/extcmd.h	Wed Nov 12 22:24:18 2014 +0200
@@ -0,0 +1,15 @@
+#ifndef __MCABBER_EXTCMD_H__
+#define __MCABBER_EXTCMD_H__ 1
+
+#define EXT_CMD_TYPE_MESSAGE   "MSG"
+#define EXT_CMD_TYPE_STATUS    "STATUS"
+#define EXT_CMD_TYPE_UNREAD    "UNREAD"
+
+#define EXT_CMD_INFO_RECEIVED  "IN"
+#define EXT_CMD_INFO_SENT      "OUT"
+#define EXT_CMD_INFO_GROUPCHAT "MUC"
+
+void hk_ext_cmd_init(const char *command);
+void hk_ext_cmd(const char *bjid, const char *type, const char *info, const char *data);
+
+#endif
diff -r fd72b1e3bf26 mcabber/mcabber/hooks.c
--- a/mcabber/mcabber/hooks.c	Wed Nov 12 22:23:32 2014 +0200
+++ b/mcabber/mcabber/hooks.c	Wed Nov 12 22:24:18 2014 +0200
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <ctype.h>
 
 #include "hooks.h"
 #include "screen.h"
@@ -35,6 +36,7 @@
 #include "utf8.h"
 #include "commands.h"
 #include "main.h"
+#include "extcmd.h"
 
 #ifdef MODULES_ENABLE
 #include <glib.h>
@@ -177,8 +179,6 @@
 }
 #endif
 
-static char *extcmd;
-
 static const char *COMMAND_ME = "/me ";
 
 void hk_message_in(const char *bjid, const char *resname,
@@ -195,7 +195,6 @@
   char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
   GSList *roster_usr;
   unsigned mucnicklen = 0;
-  const char *ename = NULL;
   gboolean attention = FALSE, mucprivmsg = FALSE;
   gboolean error_msg_subtype = (type == LM_MESSAGE_SUB_TYPE_ERROR);
 #ifdef MODULES_ENABLE
@@ -374,19 +373,6 @@
       (!is_room || (is_groupchat && log_muc_conf && !timestamp)))
     hlog_write_message(bjid, timestamp, 0, wmsg);
 
-  if (settings_opt_get_int("events_ignore_active_window") &&
-      current_buddy && scr_get_chatmode()) {
-    gpointer bud = BUDDATA(current_buddy);
-    if (bud) {
-      const char *cjid = buddy_getjid(bud);
-      if (cjid && !strcasecmp(cjid, bjid))
-        active_window = TRUE;
-    }
-  }
-
-  if (settings_opt_get_int("eventcmd_use_nickname"))
-    ename = roster_getname(bjid);
-
   // Display the sender in the log window
   if ((!is_groupchat) && !(message_flags & HBB_PREFIX_ERR) &&
       settings_opt_get_int("log_display_sender")) {
@@ -413,12 +399,24 @@
   }
 #endif
 
+  if (settings_opt_get_int("events_ignore_active_window") &&
+      current_buddy && scr_get_chatmode()) {
+    gpointer bud = BUDDATA(current_buddy);
+    if (bud) {
+      const char *cjid = buddy_getjid(bud);
+      if (cjid && !strcasecmp(cjid, bjid))
+        active_window = TRUE;
+    }
+  }
+
   // External command
   // - We do not call hk_ext_cmd() for history lines in MUC
   // - We do call hk_ext_cmd() for private messages in a room
   // - We do call hk_ext_cmd() for messages to the current window
   if (!active_window && ((is_groupchat && !timestamp) || !is_groupchat))
-    hk_ext_cmd(ename ? ename : bjid, (is_groupchat ? 'G' : 'M'), 'R', wmsg);
+    hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE,
+               is_groupchat ? EXT_CMD_INFO_GROUPCHAT : EXT_CMD_INFO_RECEIVED,
+               wmsg);
 
   // Beep, if enabled:
   // - if it's a private message
@@ -496,7 +494,7 @@
 #endif
 
   // External command
-  hk_ext_cmd(bjid, 'M', 'S', NULL);
+  hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE, EXT_CMD_INFO_SENT, NULL);
 
   g_free(bmsg);
   g_free(mmsg);
@@ -511,10 +509,7 @@
   char *bn;
   char *logsmsg;
   const char *rn = (resname ? resname : "");
-  const char *ename = NULL;
-
-  if (settings_opt_get_int("eventcmd_use_nickname"))
-    ename = roster_getname(bjid);
+  char newstatus[2] = { '?', '\0' };
 
   oldstat = roster_getstatus(bjid, resname);
 
@@ -566,27 +561,28 @@
   scr_draw_roster();
   hlog_write_status(bjid, timestamp, status, status_msg);
 
+  newstatus[0] = imstatus2char[status];
+
 #ifdef MODULES_ENABLE
   {
     char os[2] = " \0";
-    char ns[2] = " \0";
     hk_arg_t args[] = {
       { "jid", bjid },
       { "resource", rn },
       { "old_status", os },
-      { "new_status", ns },
+      { "new_status", newstatus },
       { "message", status_msg ? status_msg : "" },
       { NULL, NULL },
     };
     os[0] = imstatus2char[oldstat];
-    ns[0] = imstatus2char[status];
 
     hk_run_handlers(HOOK_STATUS_CHANGE, args);
   }
 #endif
 
   // External command
-  hk_ext_cmd(ename ? ename : bjid, 'S', imstatus2char[status], NULL);
+  newstatus[0] = toupper(newstatus[0]);
+  hk_ext_cmd(bjid, EXT_CMD_TYPE_STATUS, newstatus, status_msg);
 }
 
 void hk_mystatuschange(time_t timestamp, enum imstatus old_status,
@@ -711,7 +707,7 @@
   /* Call external command */
   str_unread = g_strdup_printf("%u %u %u %u", unread_count, attention_count,
                                muc_unread, muc_attention);
-  hk_ext_cmd("", 'U', (guchar)MIN(255, unread_count), str_unread);
+  hk_ext_cmd("", EXT_CMD_TYPE_UNREAD, str_unread, NULL);
   g_free(str_unread);
 }
 
@@ -751,123 +747,4 @@
   return 0;
 }
 
-
-/* External commands */
-
-//  hk_ext_cmd_init()
-// Initialize external command variable.
-// Can be called with parameter NULL to reset and free memory.
-void hk_ext_cmd_init(const char *command)
-{
-  if (extcmd) {
-    g_free(extcmd);
-    extcmd = NULL;
-  }
-  if (command)
-    extcmd = expand_filename(command);
-}
-
-//  hk_ext_cmd()
-// Launch an external command (process) for the given event.
-// For now, data should be NULL.
-void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data)
-{
-  pid_t pid;
-  const char *arg_type = NULL;
-  const char *arg_info = NULL;
-  const char *arg_data = NULL;
-  char status_str[2];
-  char *datafname = NULL;
-
-  if (!extcmd) return;
-
-  // Prepare arg_* (external command parameters)
-  switch (type) {
-    case 'M': /* Normal message */
-        arg_type = "MSG";
-        if (info == 'R')
-          arg_info = "IN";
-        else if (info == 'S')
-          arg_info = "OUT";
-        break;
-    case 'G': /* Groupchat message */
-        arg_type = "MSG";
-        arg_info = "MUC";
-        break;
-    case 'S': /* Status change */
-        arg_type = "STATUS";
-        if (strchr(imstatus2char, tolower(info))) {
-          status_str[0] = toupper(info);
-          status_str[1] = 0;
-          arg_info = status_str;
-        }
-        break;
-    case 'U': /* Unread buffer count */
-        arg_type = "UNREAD";
-        arg_info = data;
-        break;
-    default:
-        return;
-  }
-
-  if (!arg_type || !arg_info) return;
-
-  if (strchr("MG", type) && data && settings_opt_get_int("event_log_files")) {
-    int fd;
-    const char *prefix;
-    char *prefix_xp = NULL;
-    char *data_locale;
-
-    data_locale = from_utf8(data);
-    prefix = settings_opt_get("event_log_dir");
-    if (prefix)
-      prefix = prefix_xp = expand_filename(prefix);
-    else
-      prefix = ut_get_tmpdir();
-    datafname = g_strdup_printf("%s/mcabber-%d.XXXXXX", prefix, getpid());
-    g_free(prefix_xp);
-
-    // XXX Some old systems may require us to set umask first.
-    fd = mkstemp(datafname);
-    if (fd == -1) {
-      g_free(datafname);
-      datafname = NULL;
-      scr_LogPrint(LPRINT_LOGNORM,
-                   "Unable to create temp file for external command.");
-    } else {
-      size_t data_locale_len = strlen(data_locale);
-      ssize_t a = write(fd, data_locale, data_locale_len);
-      ssize_t b = write(fd, "\n", 1);
-      if ((size_t)a != data_locale_len || b != 1) {
-        g_free(datafname);
-        datafname = NULL;
-        scr_LogPrint(LPRINT_LOGNORM,
-                     "Unable to write to temp file for external command.");
-      }
-      close(fd);
-      arg_data = datafname;
-    }
-    g_free(data_locale);
-  }
-
-  if ((pid=fork()) == -1) {
-    scr_LogPrint(LPRINT_LOGNORM, "Fork error, cannot launch external command.");
-    g_free(datafname);
-    return;
-  }
-
-  if (pid == 0) { // child
-    // Close standard file descriptors
-    close(STDIN_FILENO);
-    close(STDOUT_FILENO);
-    close(STDERR_FILENO);
-    if (execl(extcmd, extcmd, arg_type, arg_info, bjid, arg_data,
-              (char *)NULL) == -1) {
-      // scr_LogPrint(LPRINT_LOGNORM, "Cannot execute external command.");
-      exit(1);
-    }
-  }
-  g_free(datafname);
-}
-
 /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
diff -r fd72b1e3bf26 mcabber/mcabber/hooks.h
--- a/mcabber/mcabber/hooks.h	Wed Nov 12 22:23:32 2014 +0200
+++ b/mcabber/mcabber/hooks.h	Wed Nov 12 22:24:18 2014 +0200
@@ -67,9 +67,6 @@
 guint hk_subscription(LmMessageSubType mstype, const gchar *bjid,
                       const gchar *msg);
 
-void hk_ext_cmd_init(const char *command);
-void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data);
-
 #endif /* __MCABBER_HOOKS_H__ */
 
 /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
diff -r fd72b1e3bf26 mcabber/mcabber/main.c
--- a/mcabber/mcabber/main.c	Wed Nov 12 22:23:32 2014 +0200
+++ b/mcabber/mcabber/main.c	Wed Nov 12 22:24:18 2014 +0200
@@ -19,14 +19,18 @@
  * USA
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <signal.h>
-#include <termios.h>
-#include <sys/types.h>
-#include <sys/wait.h>
+#include <stdio.h>     // fprintf, fileno, fgets, puts
+#include <stdlib.h>    // exit, getenv
+#include <unistd.h>    // tcsetattr, tcgetattr, getopt
+#include <string.h>    // strchr, strlen, memset
+#include <signal.h>    // signal
+#include <termios.h>   // tcsetattr, tcgetattr
+#include <sys/types.h> // waitpid
+#include <sys/wait.h>  // waitpid
+#include <glib.h>
+#include <config.h>
+#include <poll.h>      // POLLIN, POLLERR, POLLPRI
+
 #include <glib.h>
 #include <config.h>
 #include <poll.h>
@@ -45,6 +49,7 @@
 #include "help.h"
 #include "events.h"
 #include "compl.h"
+#include "extcmd.h"
 
 #ifndef MODULES_ENABLE
 # include "fifo.h"
@@ -351,10 +356,10 @@
       case 'h':
       case '?':
         printf("Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
-        return (c == 'h' ? 0 : -1);
+        return (c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE);
       case 'V':
         compile_options();
-        return 0;
+        return EXIT_SUCCESS;
       case 'f':
         configFile = g_strdup(optarg);
         break;
@@ -363,7 +368,7 @@
 
   if (optind < argc) {
     fprintf(stderr, "Usage: %s [-h|-V|-f mcabberrc_file]\n\n", argv[0]);
-    return -1;
+    return EXIT_FAILURE;
   }
 
   /* Initialize command system, roster and default key bindings */
@@ -518,7 +523,7 @@
 
   printf("\n\nThanks for using mcabber!\n");
 
-  return 0;
+  return EXIT_SUCCESS;
 }
 
 /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
