summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE27
-rw-r--r--Makefile24
-rw-r--r--README.md17
-rw-r--r--TODO12
-rw-r--r--xcwd.c238
5 files changed, 318 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..19cc560
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright © 2013, Adrien Schildknecht
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the copyright holder nor the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY Adrien Schildknecht ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL Adrien Schildknecht BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ca892a8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+CFILES=xcwd.c
+CC=gcc
+CFLAGS=-Wall -Werror -std=gnu89 #-DDEBUG -g
+LDFLAGS=-lX11
+EXE="xcwd"
+O=${CFILES:.c=.o}
+prefix=/usr/
+
+${EXE}: clean ${O}
+ ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${O}
+
+.SUFFIXES: .c .o
+.c.o:
+ ${CC} ${CFLAGS} -c $<
+
+clean:
+ rm -vf *.o
+
+distclean: clean
+ rm -vf ${EXE}
+
+install: ${EXE}
+ install -m 0755 ${EXE} $(prefix)/bin
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f4dc509
--- /dev/null
+++ b/README.md
@@ -0,0 +1,17 @@
+xcwd - X current working directory
+==================================
+xcwd is a simple tool which print the current working directory of the
+currently focused window.
+The main goal is to launch applications directly into the same directory
+as the focused applications. This is especially useful if you want to open
+a new terminal for debugging or compiling purpose.
+
+Requirements
+------------
+- libX11-dev
+
+Running xwcd
+------------
+Simply invoke the 'xcwd' command.
+You probably want to use it this way:
+ ``urxvt -cd `xcwd` ``
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..3097fad
--- /dev/null
+++ b/TODO
@@ -0,0 +1,12 @@
+TODO
+====
+
+Features
+--------
+check the WM_CLIENT_MACHINE property
+multi-monitor ?
+
+Bugs
+----
+xcwd is unable to get the properties of a gtk window.
+
diff --git a/xcwd.c b/xcwd.c
new file mode 100644
index 0000000..e5cb4fb
--- /dev/null
+++ b/xcwd.c
@@ -0,0 +1,238 @@
+/* This is fcwd written by Adrien Schildknecht (c) 2013
+ * 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>
+#include <unistd.h>
+#include <glob.h>
+#include <sys/stat.h>
+#include <X11/Xlib.h>
+
+#define XA_STRING (XInternAtom(dpy, "STRING", 0))
+
+Display *dpy;
+
+typedef struct processes_s *processes_t;
+struct processes_s {
+ struct proc_s {
+ long pid;
+ long ppid;
+ char name[32];
+ } *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()
+{
+ Window focuswin;
+ int focusrevert;
+
+ dpy = XOpenDisplay (NULL);
+ if (!dpy)
+ exit (1);
+ XGetInputFocus (dpy, &focuswin, &focusrevert);
+#ifdef DEBUG
+ fprintf(stderr, "Window ID = %lu\n", focuswin);
+#endif
+ return focuswin;
+}
+
+static long windowPid(Window focuswin)
+{
+ Atom nameAtom = XInternAtom(dpy, "_NET_WM_PID", 1);
+ Atom cardinalAtom = XInternAtom(dpy, "CARDINAL", 0);
+ Atom type;
+ int format;
+ long pid = -1;
+ unsigned long nitems, after;
+ unsigned char *data = 0;
+
+ if (XGetWindowProperty(dpy, focuswin, nameAtom, 0, 1024, 0, cardinalAtom,
+ &type, &format, &nitems, &after, &data) == Success) {
+ if (data) {
+ pid = *((long*)data);
+ XFree(data);
+ }
+ }
+#ifdef DEBUG
+ if(pid == -1)
+ fprintf(stderr, "_NET_WM_PID not found\n");
+ else
+ fprintf(stderr, "_NET_WM_PID = %lu\n", pid);
+#endif
+ return pid;
+}
+
+static char* windowStrings(Window focuswin, size_t *size, char* hint)
+{
+ Atom nameAtom = XInternAtom(dpy, hint, 1);
+ Atom type;
+ int format;
+ int i;
+ 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) {
+ if(type == XA_STRING) {
+ ret = malloc(sizeof(char) * *size);
+#ifdef DEBUG
+ fprintf(stderr, "%s = ", hint);
+#endif
+ for(i = 0; i < *size; ++i) {
+#ifdef DEBUG
+ fprintf(stderr, "%c", data[i] == 0 ? ' ' : data[i]);
+#endif
+ ret[i] = data[i];
+ }
+#ifdef DEBUG
+ fprintf(stderr, "\n");
+#endif
+ }
+ XFree(data);
+ }
+#ifdef DEBUG
+ else
+ fprintf(stderr, "%s not found\n", hint);
+#endif
+ }
+ return ret;
+}
+
+static void freeProcesses(processes_t p)
+{
+ free(p->ps);
+ free(p);
+}
+
+static processes_t getProcesses(void)
+{
+ glob_t globbuf;
+ unsigned int i, j;
+ processes_t p;
+
+ glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &globbuf);
+ p = malloc(sizeof(struct processes_s));
+ p->ps = malloc(globbuf.gl_pathc * sizeof(struct proc_s));
+
+#ifdef DEBUG
+ fprintf(stderr, "Found %ld processes\n", globbuf.gl_pathc);
+#endif
+ 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;
+ fscanf(tn, "%ld (%32[^)] %*3c %ld", &p->ps[j].pid, p->ps[j].name,
+ &p->ps[j].ppid);
+#ifdef DEBUG
+ fprintf(stderr, "\t%-20s\tpid=%6ld\tppid=%6ld\n", p->ps[j].name,
+ p->ps[j].pid, p->ps[j].ppid);
+#endif
+ fclose(tn);
+ j++;
+ }
+ p->n = j;
+ globfree(&globbuf);
+ return p;
+}
+
+static long lastChild(processes_t p, long pid)
+{
+ struct proc_s key, *res;
+
+ do {
+ key.ppid = pid;
+ res = (struct proc_s *)bsearch(&key, p->ps, p->n,
+ sizeof(struct proc_s), ppidCmp);
+ pid = res ? res->pid : -1;
+ }while(pid != -1);
+ return key.ppid;
+}
+
+static void readPath(long pid)
+{
+ char buf[255];
+ char path[64];
+ snprintf(path, sizeof(path), "/proc/%ld/cwd", pid);
+#ifdef DEBUG
+ fprintf(stderr, "Read %s\n", path);
+#endif
+ readlink(path, buf, 255);
+ fprintf(stdout, "%s\n", buf);
+}
+
+int main(int argc, const char *argv[])
+{
+ processes_t p;
+ Window w = focusedWindow();
+ long pid;
+
+ pid = windowPid(w);
+ p = getProcesses();
+ if(pid != -1) { // WM_NET_PID
+ qsort(p->ps, p->n, sizeof(struct proc_s), ppidCmp);
+ }
+ else {
+ size_t size;
+ char* strings;
+ int i;
+ struct proc_s *res = NULL, key;
+ 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) {
+#ifdef DEBUG
+ fprintf(stderr, "pidof %s\n", strings + i);
+#endif
+ strcpy(key.name, strings + i);
+ res = (struct proc_s *)bsearch(&key, p->ps, p->n,
+ sizeof(struct proc_s), nameCmp);
+ if(res)
+ break;
+ }
+ if(res) {
+ pid = res->pid;
+#ifdef DEBUG
+ fprintf(stderr, "Found %s (%ld)\n", res->name, res->pid);
+#endif
+ }
+ if(size != 0)
+ free(strings);
+ }
+ if(pid != -1) {
+ pid = lastChild(p, pid);
+ readPath(pid);
+ }
+ else {
+#ifdef DEBUG
+ fprintf(stderr, "getenv $HOME...\n");
+#endif
+ fprintf(stdout, "%s\n", getenv("HOME"));
+ }
+ freeProcesses(p);
+
+ return EXIT_SUCCESS;
+}
+