From acc46c4dd25ccf1d033d3b08896fa82fd6b04346 Mon Sep 17 00:00:00 2001
From: Qball Cow <qball@gmpclient.org>
Date: Wed, 29 Jan 2014 13:45:03 +0100
Subject: [PATCH] Add a test json mode

---
 Makefile                 |   4 +-
 doc/mpc.json             |   1 +
 include/json-dialog.h    |   8 ++
 include/simpleswitcher.h |   1 +
 source/json-dialog.c     | 197 +++++++++++++++++++++++++++++++++++++++
 source/simpleswitcher.c  |   8 ++
 6 files changed, 217 insertions(+), 2 deletions(-)
 create mode 100644 doc/mpc.json
 create mode 100644 include/json-dialog.h
 create mode 100644 source/json-dialog.c

diff --git a/Makefile b/Makefile
index 8356a08e..373325cf 100644
--- a/Makefile
+++ b/Makefile
@@ -51,8 +51,8 @@ ifeq (${PKG_CONFIG},${EMPTY})
 $(error Failed to find pkg-config. Please install pkg-config)
 endif
 
-CFLAGS+=$(shell ${PKG_CONFIG} --cflags x11 xinerama xft libxdg-basedir)
-LDADD+=$(shell ${PKG_CONFIG} --libs x11 xinerama xft libxdg-basedir)
+CFLAGS+=$(shell ${PKG_CONFIG} --cflags x11 xinerama xft libxdg-basedir json)
+LDADD+=$(shell ${PKG_CONFIG} --libs x11 xinerama xft libxdg-basedir json)
 
 ifeq (${LDADD},${EMPTY})
 $(error Failed to find the required dependencies: x11, xinerama, xft)
diff --git a/doc/mpc.json b/doc/mpc.json
new file mode 100644
index 00000000..322dce83
--- /dev/null
+++ b/doc/mpc.json
@@ -0,0 +1 @@
+{"prompt": "mpd ","run-in-terminal":false, "commands": { "play": "mpc play", "stop": "mpc stop", "start": "mpc start"}}
diff --git a/include/json-dialog.h b/include/json-dialog.h
new file mode 100644
index 00000000..21aa52c7
--- /dev/null
+++ b/include/json-dialog.h
@@ -0,0 +1,8 @@
+#ifndef __JSON_DIALOG_H__
+#define __JSON_DIALOG_H__
+
+extern char *json_input_file;
+
+SwitcherMode json_switcher_dialog ( char **input );
+
+#endif
diff --git a/include/simpleswitcher.h b/include/simpleswitcher.h
index 9609d010..7d79e989 100644
--- a/include/simpleswitcher.h
+++ b/include/simpleswitcher.h
@@ -25,6 +25,7 @@ typedef enum {
     PROFILE_DIALOG,
 #endif
     NUM_DIALOGS,
+    JSON_DIALOG,
     MODE_EXIT,
     NEXT_DIALOG
 } SwitcherMode;
diff --git a/source/json-dialog.c b/source/json-dialog.c
new file mode 100644
index 00000000..152872fc
--- /dev/null
+++ b/source/json-dialog.c
@@ -0,0 +1,197 @@
+/**
+ * simpleswitcher
+ *
+ * MIT/X11 License
+ * Copyright 2013-2014 Qball  Cow <qball@gmpclient.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <X11/X.h>
+
+#include <unistd.h>
+#include <signal.h>
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "simpleswitcher.h"
+#include "mark-dialog.h"
+#include <errno.h>
+#include <linux/un.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <i3/ipc.h>
+
+#ifdef TIMING
+#include <time.h>
+#endif
+
+#include <json/json.h>
+
+char *json_input_file = NULL;
+
+static inline int execsh( const char *cmd ,int run_in_term )
+{
+// use sh for args parsing
+    if ( run_in_term )
+        return execlp( config.terminal_emulator, config.terminal_emulator, "-e", "sh", "-c", cmd, NULL );
+
+    return execlp( "/bin/sh", "sh", "-c", cmd, NULL );
+}
+static void exec_json( const char *cmd, int run_in_term)
+{
+    if ( !cmd || !cmd[0] ) return;
+
+    printf("execute: %s\n", cmd);
+    signal( SIGCHLD, catch_exit );
+    pid_t pid = fork();
+
+    if ( !pid ) {
+        setsid();
+        execsh( cmd, run_in_term );
+        exit( EXIT_FAILURE );
+    }
+
+}
+
+typedef struct pmenu {
+    char *prompt;
+    int execute_in_term;
+    unsigned int num_entries;
+    char **entries;
+    char **commands;
+} pmenu;
+
+static pmenu *get_json ( )
+{
+    pmenu *retv = allocate_clear(sizeof(*retv));
+
+#ifdef TIMING
+    struct timespec start, stop;
+    clock_gettime( CLOCK_REALTIME, &start );
+#endif
+
+    struct json_object *jo = NULL;
+    if(json_input_file != NULL) {
+        jo = json_object_from_file(json_input_file);
+    }
+
+    // Create error and exit.
+    if(jo == NULL) {
+        retv->entries= realloc( retv->entries, ( retv->num_entries+2 )*sizeof( char* ) );
+        retv->entries[retv->num_entries] = strdup( "Failed to parse json input file" );
+        retv->entries[retv->num_entries+1] = NULL;
+        retv->commands= realloc( retv->commands, ( retv->num_entries+2 )*sizeof( char* ) );
+        retv->commands[retv->num_entries] = strdup( "" );
+        retv->commands[retv->num_entries+1] = NULL;
+
+        retv->num_entries++;
+        return retv;
+    }
+
+    struct json_object *jo2;
+    if(json_object_object_get_ex(jo, "prompt", &jo2)) {
+        retv->prompt = strdup(json_object_get_string(jo2));
+    }
+    if(json_object_object_get_ex(jo, "run-in-terminal", &jo2)) {
+        retv->execute_in_term = json_object_get_int(jo2);
+    }
+
+    if(json_object_object_get_ex(jo, "commands", &jo2)) {
+        json_object_object_foreach(jo2, key, child) {
+            retv->entries= realloc( retv->entries, ( retv->num_entries+2 )*sizeof( char* ) );
+            retv->entries[retv->num_entries] = strdup( key);
+            retv->entries[retv->num_entries+1] = NULL;
+            retv->commands= realloc( retv->commands, ( retv->num_entries+2 )*sizeof( char* ) );
+            retv->commands[retv->num_entries] = strdup( json_object_get_string(child));
+            retv->commands[retv->num_entries+1] = NULL;
+
+            retv->num_entries++;
+        }
+    }
+    json_object_put(jo);
+
+
+
+#ifdef TIMING
+    clock_gettime( CLOCK_REALTIME, &stop );
+
+    if ( stop.tv_sec != start.tv_sec ) {
+        stop.tv_nsec += ( stop.tv_sec-start.tv_sec )*1e9;
+    }
+
+    long diff = stop.tv_nsec-start.tv_nsec;
+    printf( "Time elapsed: %ld us\n", diff/1000 );
+#endif
+    return retv;
+}
+
+static int token_match ( char **tokens, const char *input,
+                         __attribute__( ( unused ) )int index,
+                         __attribute__( ( unused ) )void *data )
+{
+    int match = 1;
+
+    // Do a tokenized match.
+    if ( tokens ) for ( int j  = 1; match && tokens[j]; j++ ) {
+            match = ( strcasestr( input, tokens[j] ) != NULL );
+        }
+
+    return match;
+}
+
+SwitcherMode json_switcher_dialog ( char **input )
+{
+    SwitcherMode retv = MODE_EXIT;
+    // act as a launcher
+    pmenu *list = get_json( );
+
+    if ( list == NULL ) {
+        return retv;
+    }
+
+    int shift=0;
+    int n = menu( list->entries, input, list->prompt, NULL, &shift,token_match, NULL );
+
+    if ( n == -2 ) {
+        retv = JSON_DIALOG;
+    } else if ( n >=0 && list->commands[n] != NULL ) {
+        exec_json( list->commands[n], list->execute_in_term);
+    }
+
+    for ( unsigned int i=0; i < list->num_entries; i++ ) {
+        free( list->entries[i] );
+        free( list->commands[i] );
+    }
+
+    if(list->entries)free( list->entries );
+    if(list->commands)free( list->commands );
+    if(list->prompt)free( list->prompt );
+
+    return retv;
+}
+
diff --git a/source/simpleswitcher.c b/source/simpleswitcher.c
index bc076182..7c1e466c 100644
--- a/source/simpleswitcher.c
+++ b/source/simpleswitcher.c
@@ -66,6 +66,7 @@
 #include "ssh-dialog.h"
 #include "mark-dialog.h"
 #include "profile-dialog.h"
+#include "json-dialog.h"
 
 #define LINE_MARGIN 4
 
@@ -1199,6 +1200,9 @@ void run_switcher( int fmode, SwitcherMode mode )
         }
 
 #endif // __QC_MODE__
+        else if ( mode == JSON_DIALOG ) {
+            retv = json_switcher_dialog ( &input );
+        }
 
         if ( retv == NEXT_DIALOG ) {
             mode = ( mode+1 )%NUM_DIALOGS;
@@ -1427,6 +1431,10 @@ int main( int argc, char *argv[] )
     } else if ( find_arg( argc, argv, "-mnow" ) >= 0 ) {
         run_switcher( NOFORK, MARK_DIALOG );
 #endif
+    } else if ( find_arg( argc, argv, "-json" ) >= 0 ) {
+        find_arg_str( argc, argv, "-json", &json_input_file);
+        run_switcher( NOFORK, JSON_DIALOG );
+
     } else {
         // Daemon mode, Listen to key presses..