xcwd/xcwd.c

334 lines
8.4 KiB
C
Raw Normal View History

2013-07-01 20:41:20 -04:00
/* This is xcwd written by Adrien Schildknecht (c) 2013
2013-04-24 15:05:53 -04:00
* Email: adrien+dev@schischi.me
* Feel free to copy and redistribute in terms of the
* BSD license
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
2013-04-25 19:11:46 -04:00
#include <strings.h>
2013-04-24 15:05:53 -04:00
#include <X11/Xlib.h>
2013-07-01 20:41:20 -04:00
#ifdef LINUX
# include <sys/stat.h>
# include <glob.h>
# include <unistd.h>
#endif
#ifdef BSD
# include <sys/sysctl.h>
# include <sys/user.h>
# include <libutil.h>
#endif
#define DEBUG 1
2013-04-25 18:40:51 -04:00
2013-04-25 14:27:50 -04:00
#define XA_STRING (XInternAtom(dpy, "STRING", 0))
#define XA_CARDINAL (XInternAtom(dpy, "CARDINAL", 0))
#define XA_WM_STATE (XInternAtom(dpy, "WM_STATE", 0))
2013-04-24 15:05:53 -04:00
2013-04-25 18:40:51 -04:00
#define LOG(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)
2013-04-24 15:05:53 -04:00
Display *dpy;
typedef struct processes_s *processes_t;
struct processes_s {
struct proc_s {
long pid;
long ppid;
char name[32];
2013-07-01 20:41:20 -04:00
#ifdef BSD
char cwd[MAXPATHLEN];
#endif
2013-04-24 15:05:53 -04:00
} *ps;
size_t n;
};
int nameCmp(const void *p1, const void *p2)
{
return strcasecmp(((struct proc_s *)p1)->name,
((struct proc_s *)p2)->name);
}
int ppidCmp(const void *p1, const void *p2)
{
return ((struct proc_s *)p1)->ppid - ((struct proc_s *)p2)->ppid;
}
static Window focusedWindow()
{
2013-04-25 18:40:51 -04:00
Atom type;
Window focuswin, root, *children;
int format, status, focusrevert;
2013-04-25 14:27:50 -04:00
unsigned long nitems, after;
unsigned char *data;
unsigned int nchildren;
2013-04-24 15:05:53 -04:00
dpy = XOpenDisplay (NULL);
if (!dpy)
exit (1);
XGetInputFocus (dpy, &focuswin, &focusrevert);
2013-04-25 14:27:50 -04:00
root = XDefaultRootWindow(dpy);
do {
2013-04-25 18:40:51 -04:00
status = XGetWindowProperty(dpy, focuswin, XA_WM_STATE, 0, 1024, 0,
2013-04-25 14:27:50 -04:00
XA_WM_STATE, &type, &format, &nitems, &after, &data);
if (status == Success) {
if (data) {
XFree(data);
LOG("Window ID = %lu\n", focuswin);
return focuswin;
}
2013-04-25 14:27:50 -04:00
}
2013-04-25 18:40:51 -04:00
else
return 0;
2013-04-25 14:27:50 -04:00
XQueryTree(dpy, focuswin, &root, &focuswin, &children, &nchildren);
2013-04-25 18:40:51 -04:00
LOG("%s", "Current window does not have WM_STATE, getting parent\n");
2013-04-25 14:27:50 -04:00
} while(focuswin != root);
return 0;
2013-04-24 15:05:53 -04:00
}
static long windowPid(Window focuswin)
{
Atom nameAtom = XInternAtom(dpy, "_NET_WM_PID", 1);
Atom type;
2013-04-25 14:27:50 -04:00
int format, status;
2013-04-24 15:05:53 -04:00
long pid = -1;
unsigned long nitems, after;
2013-04-25 14:27:50 -04:00
unsigned char *data;
2013-04-24 15:05:53 -04:00
2013-04-25 18:40:51 -04:00
status = XGetWindowProperty(dpy, focuswin, nameAtom, 0, 1024, 0,
2013-04-25 14:27:50 -04:00
XA_CARDINAL, &type, &format, &nitems, &after, &data);
2013-04-25 18:40:51 -04:00
if (status == Success && data) {
pid = *((long*)data);
XFree(data);
LOG("_NET_WM_PID = %lu\n", pid);
2013-04-24 15:05:53 -04:00
}
else
2013-04-25 18:40:51 -04:00
LOG("%s", "_NET_WM_PID not found\n");
2013-04-24 15:05:53 -04:00
return pid;
}
static char* windowStrings(Window focuswin, long unsigned int *size, char* hint)
2013-04-24 15:05:53 -04:00
{
Atom nameAtom = XInternAtom(dpy, hint, 1);
Atom type;
int format;
2013-04-25 19:11:46 -04:00
unsigned int i;
2013-04-24 15:05:53 -04:00
unsigned long after;
unsigned char *data = 0;
char *ret = NULL;
if (XGetWindowProperty(dpy, focuswin, nameAtom, 0, 1024, 0, AnyPropertyType,
&type, &format, size, &after, &data) == Success) {
if (data) {
2013-04-25 18:40:51 -04:00
if (type == XA_STRING) {
2013-04-24 15:05:53 -04:00
ret = malloc(sizeof(char) * *size);
2013-04-25 18:40:51 -04:00
LOG("%s = ", hint);
for (i = 0; i < *size; ++i) {
LOG("%c", data[i] == 0 ? ' ' : data[i]);
2013-04-24 15:05:53 -04:00
ret[i] = data[i];
}
2013-04-25 18:40:51 -04:00
fprintf(stderr, "\n");
LOG("%s", "\n");
2013-04-24 15:05:53 -04:00
}
XFree(data);
}
else
2013-04-25 18:40:51 -04:00
LOG("%s not found\n", hint);
2013-04-24 15:05:53 -04:00
}
return ret;
}
static void freeProcesses(processes_t p)
{
free(p->ps);
free(p);
}
static processes_t getProcesses(void)
{
2013-07-01 20:41:20 -04:00
processes_t p = NULL;
#ifdef LINUX
2013-04-24 15:05:53 -04:00
glob_t globbuf;
unsigned int i, j;
glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &globbuf);
p = malloc(sizeof(struct processes_s));
p->ps = malloc(globbuf.gl_pathc * sizeof(struct proc_s));
LOG("Found %zu processes\n", globbuf.gl_pathc);
2013-04-24 15:05:53 -04:00
for (i = j = 0; i < globbuf.gl_pathc; i++) {
char name[32];
FILE *tn;
(void)globbuf.gl_pathv[globbuf.gl_pathc - i - 1];
snprintf(name, sizeof(name), "%s%s",
globbuf.gl_pathv[globbuf.gl_pathc - i - 1], "/stat");
tn = fopen(name, "r");
if (tn == NULL)
continue;
2013-05-21 09:54:12 -04:00
if(fscanf(tn, "%ld (%32[^)] %*3c %ld", &p->ps[j].pid,
2013-07-01 20:41:20 -04:00
p->ps[j].name, &p->ps[j].ppid) != 3)
2013-05-21 09:54:12 -04:00
return NULL;
2013-04-25 18:40:51 -04:00
LOG("\t%-20s\tpid=%6ld\tppid=%6ld\n", p->ps[j].name, p->ps[j].pid,
p->ps[j].ppid);
2013-04-24 15:05:53 -04:00
fclose(tn);
j++;
}
p->n = j;
globfree(&globbuf);
2013-07-01 20:41:20 -04:00
#endif
#ifdef BSD
unsigned int count;
p = malloc(sizeof(struct processes_s));
struct kinfo_proc *kp;
size_t len = 0;
int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
int error;
error = sysctl(name, 4, NULL, &len, NULL, 0);
kp = malloc(len);
error = sysctl(name, 4, kp, &len, NULL, 0);
count = len / sizeof(*kp);
p->ps = malloc(sizeof(struct proc_s) * count);
p->n = count;
unsigned int i;
for(i = 0; i < count; ++i) {
struct kinfo_file *files, *kif;
int cnt, j;
if(kp[i].ki_fd == NULL)
continue;
files = kinfo_getfile(kp[i].ki_pid, &cnt);
for(j = 0; j < cnt; ++j) {
kif = &files[j];
if(kif->kf_fd != KF_FD_TYPE_CWD)
continue;
p->ps[i].pid = kp[i].ki_pid;
p->ps[i].ppid = kp[i].ki_ppid;
strncpy(p->ps[i].name, kp[i].ki_tdname, 32);
strncpy(p->ps[i].cwd, kif->kf_path, MAXPATHLEN);
//fprintf(stderr, "%s (%ld - %ld) - %s\n",p->ps[i].name, p->ps[i].pid,
// p->ps[i].ppid, p->ps[i].cwd);
}
}
#endif
2013-04-24 15:05:53 -04:00
return p;
}
2013-07-01 20:41:20 -04:00
static int readPath(struct proc_s *proc)
2013-04-24 15:05:53 -04:00
{
2013-07-01 20:41:20 -04:00
#ifdef LINUX
2013-04-24 15:05:53 -04:00
char buf[255];
char path[64];
2013-04-25 18:40:51 -04:00
ssize_t len;
2013-07-01 20:41:20 -04:00
snprintf(path, sizeof(path), "/proc/%ld/cwd", proc->pid);
2013-04-25 18:40:51 -04:00
if ((len = readlink(path, buf, 255)) != -1)
buf[len] = '\0';
if(len <= 0) {
LOG("Error readlink %s\n", path);
return 0;
}
LOG("Read %s\n", path);
2013-04-24 15:05:53 -04:00
fprintf(stdout, "%s\n", buf);
2013-07-01 20:41:20 -04:00
#endif
#ifdef BSD
if(!strlen(proc->cwd))
return 0;
fprintf(stdout, "%s\n", proc->cwd);
#endif
return 1;
}
static void cwdOfDeepestChild(processes_t p, long pid)
{
int i;
2013-05-01 15:53:00 -04:00
struct proc_s key = {.ppid = pid}, *res = NULL, *lastRes = NULL;
do {
2013-05-01 15:53:00 -04:00
if(res) {
lastRes = res;
key.ppid = res->pid;
}
res = (struct proc_s *)bsearch(&key, p->ps, p->n,
sizeof(struct proc_s), ppidCmp);
2013-05-01 15:53:00 -04:00
} while(res);
if(!lastRes) {
2013-07-01 20:41:20 -04:00
readPath(&key);
2013-05-01 15:53:00 -04:00
return;
}
for(i = 0; lastRes != p->ps && (lastRes - i)->ppid == lastRes->ppid; ++i)
2013-07-01 20:41:20 -04:00
if(readPath((lastRes - i)))
return;
for(i = 1; lastRes != p->ps + p->n && (lastRes + i)->ppid == lastRes->ppid; ++i)
2013-07-01 20:41:20 -04:00
if(readPath((lastRes + i)))
return;
2013-04-24 15:05:53 -04:00
}
2013-05-21 09:54:12 -04:00
int getHomeDirectory()
{
LOG("%s", "getenv $HOME...\n");
fprintf(stdout, "%s\n", getenv("HOME"));
return EXIT_FAILURE;
}
2013-04-24 15:05:53 -04:00
int main(int argc, const char *argv[])
{
2013-04-25 19:11:46 -04:00
(void)argc;
(void)argv;
2013-04-24 15:05:53 -04:00
processes_t p;
long pid;
2013-04-25 14:27:50 -04:00
Window w = focusedWindow();
2013-05-21 09:54:12 -04:00
if (w == 0)
return getHomeDirectory();
2013-04-24 15:05:53 -04:00
pid = windowPid(w);
p = getProcesses();
2013-05-21 09:54:12 -04:00
if(p == NULL)
return getHomeDirectory();
2013-04-25 18:40:51 -04:00
if (pid != -1) {
2013-04-24 15:05:53 -04:00
qsort(p->ps, p->n, sizeof(struct proc_s), ppidCmp);
}
else {
long unsigned int size;
2013-04-25 19:11:46 -04:00
unsigned int i;
char* strings;
2013-04-24 15:05:53 -04:00
struct proc_s *res = NULL, key;
2013-04-25 19:11:46 -04:00
2013-04-24 15:05:53 -04:00
qsort(p->ps, p->n, sizeof(struct proc_s), nameCmp);
strings = windowStrings(w, &size, "WM_CLASS");
for(i = 0; i < size; i += strlen(strings + i) + 1) {
2013-04-25 18:40:51 -04:00
LOG("pidof %s\n", strings + i);
2013-04-24 15:05:53 -04:00
strcpy(key.name, strings + i);
res = (struct proc_s *)bsearch(&key, p->ps, p->n,
2013-04-25 18:40:51 -04:00
sizeof(struct proc_s), nameCmp);
2013-04-24 15:05:53 -04:00
if(res)
break;
}
2013-04-25 18:40:51 -04:00
if (res) {
2013-04-24 15:05:53 -04:00
pid = res->pid;
2013-04-25 18:40:51 -04:00
LOG("Found %s (%ld)\n", res->name, res->pid);
2013-04-24 15:05:53 -04:00
}
2013-04-25 18:40:51 -04:00
if (size != 0)
2013-04-24 15:05:53 -04:00
free(strings);
}
if (pid != -1)
cwdOfDeepestChild(p, pid);
2013-04-24 15:05:53 -04:00
else {
2013-04-25 18:40:51 -04:00
LOG("%s", "getenv $HOME...\n");
2013-04-24 15:05:53 -04:00
fprintf(stdout, "%s\n", getenv("HOME"));
}
freeProcesses(p);
return EXIT_SUCCESS;
}