summary refs log tree commit diff
diff options
context:
space:
mode:
authorniallo <niallo>2007-06-26 19:34:26 +0000
committerniallo <niallo>2007-06-26 19:34:26 +0000
commita1d4169eb318f504f3a869ec429517de25eb5427 (patch)
tree1812ecd40728155f98ea4e415efa34e152f6bcf9
parent80d08270b87adce365904cfa43a8135cf7b13fb1 (diff)
downloadcwm-a1d4169eb318f504f3a869ec429517de25eb5427.tar.gz
cwm-a1d4169eb318f504f3a869ec429517de25eb5427.tar.xz
cwm-a1d4169eb318f504f3a869ec429517de25eb5427.zip
modify "exec" dialog so that it auto-completes based on executables in
_PATH_DEFPATH

add an "ssh-to" dialog which auto-completes based on contents of
 ~/.ssh/known_hosts (M-.)

testing and eyeballing by Simon Kuhnle <simonkuhnle at web.de>, todd@, pedro@
mk@ and David Cathcart <david at cathcart.cx>

ok todd@
-rw-r--r--calmwm.h5
-rw-r--r--conf.c1
-rw-r--r--cwm.14
-rw-r--r--kbfunc.c154
-rw-r--r--search.c53
5 files changed, 199 insertions, 18 deletions
diff --git a/calmwm.h b/calmwm.h
index d8158ea..5bbd3c0 100644
--- a/calmwm.h
+++ b/calmwm.h
@@ -257,6 +257,7 @@ struct menu {
 	char  print[MENU_MAXENTRY + 1];
 	void *ctx;
 	short lasthit;
+	short dummy;
 };
 
 TAILQ_HEAD(menu_q, menu);
@@ -427,6 +428,7 @@ void kbfunc_client_maximize(struct client_ctx *, void *);
 void kbfunc_client_vmaximize(struct client_ctx *, void *);
 void kbfunc_menu_search(struct client_ctx *, void *);
 void kbfunc_exec(struct client_ctx *, void *);
+void kbfunc_ssh(struct client_ctx *, void *);
 void kbfunc_term(struct client_ctx *cc, void *arg);
 void kbfunc_lock(struct client_ctx *cc, void *arg);
 
@@ -437,10 +439,11 @@ struct menu *search_start(struct menu_q *menuq,
     void (*match)(struct menu_q *, struct menu_q *, char *),
     void (*rank)(struct menu_q *, char *),
     void (*print)(struct menu *mi, int),
-    char *);
+    char *, int);
 void  search_match_client(struct menu_q *, struct menu_q *, char *);
 void  search_print_client(struct menu *mi, int list);
 void  search_match_text(struct menu_q *, struct menu_q *, char *);
+void  search_match_exec(struct menu_q *, struct menu_q *, char *);
 void  search_rank_text(struct menu_q *, char *);
 
 void group_init(void);
diff --git a/conf.c b/conf.c
index 011ed80..299d480 100644
--- a/conf.c
+++ b/conf.c
@@ -189,6 +189,7 @@ conf_setup(struct conf *c)
 		conf_bindkey(c, kbfunc_lock,
 		    XK_Delete, ControlMask|Mod1Mask, 0, NULL);
 		conf_bindkey(c, kbfunc_exec, XK_question, Mod1Mask, 0, NULL);
+		conf_bindkey(c, kbfunc_ssh, XK_period, Mod1Mask, 0, NULL);
 		conf_bindkey(c, kbfunc_client_hide,
 		    XK_Return, Mod1Mask, KBFLAG_NEEDCLIENT, 0);
 		conf_bindkey(c, kbfunc_client_lower,
diff --git a/cwm.1 b/cwm.1
index d592ae6..6092c88 100644
--- a/cwm.1
+++ b/cwm.1
@@ -87,6 +87,10 @@ Toggle full-screen size of window.
 Toggle vertical maximization of window.
 .It Fa M-?
 Spawn \&"Exec program\&" dialog.
+.It Fa M-.
+Spawn \&"Ssh to\&" dialog.
+This parses your $HOME/.ssh/known_hosts file to provide host auto-completion.
+Ssh will be executed via the configured terminal emulator.
 .El
 .Pp
 The mouse bindings are also important, they are:
diff --git a/kbfunc.c b/kbfunc.c
index 99a7ee2..23a0d1c 100644
--- a/kbfunc.c
+++ b/kbfunc.c
@@ -7,9 +7,14 @@
  * $Id$
  */
 
+#include <paths.h>
+
 #include "headers.h"
 #include "calmwm.h"
 
+#define KNOWN_HOSTS ".ssh/known_hosts"
+#define HASH_MARKER "|1|"
+
 void
 kbfunc_client_lower(struct client_ctx *cc, void *arg)
 {
@@ -41,7 +46,7 @@ kbfunc_client_search(struct client_ctx *scratch, void *arg)
 
 	if ((mi = search_start(&menuq,
 		    search_match_client, NULL,
-		    search_print_client, "window")) != NULL) {
+		    search_print_client, "window", 0)) != NULL) {
 		cc = (struct client_ctx *)mi->ctx;
 		if (cc->flags & CLIENT_HIDDEN)
 			client_unhide(cc);
@@ -75,7 +80,7 @@ kbfunc_menu_search(struct client_ctx *scratch, void *arg)
 	}
 
 	if ((mi = search_start(&menuq,
-		    search_match_text, NULL, NULL, "application")) != NULL)
+		    search_match_text, NULL, NULL, "application", 0)) != NULL)
 		u_spawn(((struct cmd *)mi->ctx)->image);
 
 	while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
@@ -125,7 +130,150 @@ kbfunc_lock(struct client_ctx *cc, void *arg)
 void
 kbfunc_exec(struct client_ctx *scratch, void *arg)
 {
-	grab_exec();
+	char **ap, *paths[256], *path, tpath[MAXPATHLEN];
+	int l, i, j, ngroups;
+	gid_t mygroups[NGROUPS_MAX];
+	uid_t ruid, euid, suid;
+	DIR *dirp;
+	struct dirent *dp;
+	struct stat sb;
+	struct menu_q menuq;
+	struct menu *mi;
+
+	if (getgroups(0, mygroups) == -1)
+		err(1, "getgroups failure");
+	if ((ngroups = getresuid(&ruid, &euid, &suid)) == -1)
+		err(1, "getresuid failure");
+
+	TAILQ_INIT(&menuq);
+	/* just use default path until we have config to set this */
+	path = xstrdup(_PATH_DEFPATH);
+	for (ap = paths; ap < &paths[sizeof(paths) - 1] &&
+	    (*ap = strsep(&path, ":")) != NULL;) {
+		if (**ap != '\0')
+			ap++;
+	}
+	*ap = NULL;
+	for (i = 0; i < sizeof(paths) && paths[i] != NULL; i++) {
+		if ((dirp = opendir(paths[i])) == NULL)
+			continue;
+
+		while ((dp = readdir(dirp)) != NULL) {
+			/* skip everything but regular files and symlinks */
+			if (dp->d_type != DT_REG && dp->d_type != DT_LNK)
+				continue;
+			memset(tpath, '\0', sizeof(tpath));
+			l = snprintf(tpath, sizeof(tpath), "%s/%s", paths[i],
+			    dp->d_name);
+			/* check for truncation etc */
+			if (l == -1 || l >= (int)sizeof(tpath))
+				continue;
+			/* just ignore on stat failure */
+			if (stat(tpath, &sb) == -1)
+				continue;
+			/* may we execute this file? */
+			if (euid == sb.st_uid)
+					if (sb.st_mode & S_IXUSR)
+						goto executable;
+					else
+						continue;
+			for (j = 0; j < ngroups; j++)
+				if (mygroups[j] == sb.st_gid)
+					if (sb.st_mode & S_IXGRP)
+						goto executable;
+					else
+						continue;
+			if (sb.st_mode & S_IXOTH)
+				goto executable;
+			continue;
+		executable:
+			/* the thing in tpath, we may execute */
+			XCALLOC(mi, struct menu);
+			strlcpy(mi->text, dp->d_name, sizeof(mi->text));
+			TAILQ_INSERT_TAIL(&menuq, mi, entry);
+		}
+		(void) closedir(dirp);
+	}
+
+	if ((mi = search_start(&menuq,
+		    search_match_exec, NULL, NULL, "exec", 1)) != NULL)
+		u_spawn(mi->text);
+
+	if (mi != NULL && mi->dummy)
+		xfree(mi);
+	while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
+		TAILQ_REMOVE(&menuq, mi, entry);
+		xfree(mi);
+	}
+	xfree(path);
+}
+
+void
+kbfunc_ssh(struct client_ctx *scratch, void *arg)
+{
+	struct menu_q menuq;
+	struct menu *mi;
+	FILE *fp;
+	size_t len;
+	char *buf, *lbuf, *p, *home;
+	char hostbuf[MAXHOSTNAMELEN], filename[MAXPATHLEN], cmd[256];
+	int l;
+
+	if ((home = getenv("HOME")) == NULL)
+		return;
+
+	l = snprintf(filename, sizeof(filename), "%s/%s", home, KNOWN_HOSTS);
+	if (l == -1 || l >= sizeof(filename))
+		return;
+
+	if ((fp = fopen(filename, "r")) == NULL)
+		return;
+
+	TAILQ_INIT(&menuq);
+	lbuf = NULL;
+	while ((buf = fgetln(fp, &len))) {
+		if (buf[len - 1] == '\n')
+			buf[len - 1] = '\0';
+		else {
+			/* EOF without EOL, copy and add the NUL */
+			lbuf = xmalloc(len + 1);
+			memcpy(lbuf, buf, len);
+			lbuf[len] = '\0';
+			buf = lbuf;
+		}
+		/* skip hashed hosts */
+		if (strncmp(buf, HASH_MARKER, strlen(HASH_MARKER)) == 0)
+			continue;
+		for (p = buf; *p != ',' && *p != ' ' && p != buf + len; p++) {
+			/* do nothing */
+		}
+		/* ignore badness */
+		if (p - buf + 1 > sizeof(hostbuf))
+			continue;
+		(void) strlcpy(hostbuf, buf, p - buf + 1);
+		XCALLOC(mi, struct menu);
+		(void) strlcpy(mi->text, hostbuf, sizeof(mi->text));
+		TAILQ_INSERT_TAIL(&menuq, mi, entry);
+	}
+	xfree(lbuf);
+	fclose(fp);
+
+
+	if ((mi = search_start(&menuq,
+		    search_match_exec, NULL, NULL, "ssh", 1)) != NULL) {
+		conf_cmd_refresh(&Conf);
+		l = snprintf(cmd, sizeof(cmd), "%s -e ssh %s", Conf.termpath,
+		    mi->text);
+		if (l != -1 && l < sizeof(cmd))
+			u_spawn(cmd);
+	}
+
+	if (mi != NULL && mi->dummy)
+		xfree(mi);
+	while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
+		TAILQ_REMOVE(&menuq, mi, entry);
+		xfree(mi);
+	}
 }
 
 void
diff --git a/search.c b/search.c
index 28cd7f2..9a2500d 100644
--- a/search.c
+++ b/search.c
@@ -12,7 +12,7 @@
 
 #define SearchMask (KeyPressMask|ExposureMask)
 
-static int  _strsubmatch(char *, char *);
+static int  _strsubmatch(char *, char *, int);
 
 void
 search_init(struct screen_ctx *sc)
@@ -37,7 +37,7 @@ search_start(struct menu_q *menuq,
     void (*match)(struct menu_q *, struct menu_q *, char *), 
     void (*rank)(struct menu_q *resultq, char *search), 
     void (*print)(struct menu *mi, int print),
-    char *prompt)
+    char *prompt, int dummy)
 {
 	struct screen_ctx *sc = screen_current();
 	int x, y, dx, dy, fontheight,
@@ -47,7 +47,7 @@ search_start(struct menu_q *menuq,
 	char dispstr[MENU_MAXENTRY*2 + 1];
 	char promptstr[MENU_MAXENTRY + 1];
 	Window focuswin;
-	struct menu *mi = NULL;
+	struct menu *mi = NULL, *dummy_mi = NULL;
 	struct menu_q resultq;
 	char chr;
 	enum ctltype ctl;
@@ -133,16 +133,22 @@ search_start(struct menu_q *menuq,
 			case CTL_RETURN:
 				/* This is just picking the match the
 				 * cursor is over. */
-				if ((mi = TAILQ_FIRST(&resultq)) != NULL)
+				if ((mi = TAILQ_FIRST(&resultq)) != NULL) {
 					goto found;
-				else
-					goto out;
+				} else if (dummy) {
+					dummy_mi = xmalloc(sizeof *dummy_mi);
+					(void) strlcpy(dummy_mi->text,
+					    searchstr, sizeof(dummy_mi->text));
+					dummy_mi->dummy = 1;
+					goto found;
+				}
+				goto out;
 			case CTL_WIPE:
 				searchstr[0] = '\0';
 				mutated = 1;
 				break;
 			case CTL_ALL:
- 				list = !list;
+				list = !list;
 				break;
 			case CTL_ABORT:
 				goto out;
@@ -273,9 +279,12 @@ out:
 	/* (if no match) */
 	xu_ptr_ungrab();
 	XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
+
 found:
 	XUnmapWindow(X_Dpy, sc->searchwin);
 
+	if (dummy && dummy_mi != NULL)
+		return (dummy_mi);
 	return (mi);
 }
 
@@ -307,7 +316,7 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
 		struct client_ctx *cc = mi->ctx;
 
 		/* First, try to match on labels. */
-		if (cc->label != NULL && _strsubmatch(search, cc->label)) {
+		if (cc->label != NULL && _strsubmatch(search, cc->label, 0)) {
 			cc->matchname = cc->label;
 			tier = 0;
 		} 
@@ -315,7 +324,7 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
 		/* Then, on window names. */
 		if (tier < 0) {
 			TAILQ_FOREACH_REVERSE(wn, &cc->nameq, winname_q, entry)
-			    if (_strsubmatch(search, wn->name)) {
+			    if (_strsubmatch(search, wn->name, 0)) {
 				    cc->matchname = wn->name;
 				    tier = 2;
 				    break;
@@ -327,7 +336,7 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
 		 * name.
 		 */
 
-		if (tier < 0 && _strsubmatch(search, cc->app_class)) {
+		if (tier < 0 && _strsubmatch(search, cc->app_class, 0)) {
 			cc->matchname = cc->app_class;
 			tier = 3;
 		}
@@ -417,7 +426,19 @@ search_match_text(struct menu_q *menuq, struct menu_q *resultq, char *search)
 	TAILQ_INIT(resultq);
 
 	TAILQ_FOREACH(mi, menuq, entry)
-		if (_strsubmatch(search, mi->text))
+		if (_strsubmatch(search, mi->text, 0))
+			TAILQ_INSERT_TAIL(resultq, mi, resultentry);
+}
+
+void
+search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
+{
+	struct menu *mi;
+
+	TAILQ_INIT(resultq);
+
+	TAILQ_FOREACH(mi, menuq, entry)
+		if (_strsubmatch(search, mi->text, 1))
 			TAILQ_INSERT_TAIL(resultq, mi, resultentry);
 }
 
@@ -428,10 +449,10 @@ search_rank_text(struct menu_q *resultq, char *search)
 }
 
 static int
-_strsubmatch(char *sub, char *str)
+_strsubmatch(char *sub, char *str, int zeroidx)
 {
 	size_t len, sublen;
-	u_int n;
+	u_int n, flen;
 
 	if (sub == NULL || str == NULL)
 		return (0);
@@ -442,7 +463,11 @@ _strsubmatch(char *sub, char *str)
 	if (sublen > len)
 		return (0);
 
-	for (n = 0; n <= len - sublen; n++)
+	if (!zeroidx)
+		flen = len - sublen;
+	else
+		flen = 0;
+	for (n = 0; n <= flen; n++)
 		if (strncasecmp(sub, str + n, sublen) == 0)
 			return (1);