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@
This commit is contained in:
niallo 2007-06-26 19:34:26 +00:00
parent 80d08270b8
commit a1d4169eb3
5 changed files with 199 additions and 18 deletions

View File

@ -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);

1
conf.c
View File

@ -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,

4
cwm.1
View File

@ -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:

154
kbfunc.c
View File

@ -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

View File

@ -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);