diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/powerline.c | 164 | ||||
-rwxr-xr-x | client/powerline.py | 104 | ||||
-rwxr-xr-x | client/powerline.sh | 53 |
3 files changed, 321 insertions, 0 deletions
diff --git a/client/powerline.c b/client/powerline.c new file mode 100644 index 0000000..ff107ec --- /dev/null +++ b/client/powerline.c @@ -0,0 +1,164 @@ +/* vim:fileencoding=utf-8:noet + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <sys/un.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#define HANDLE_ERROR(msg) \ + do { \ + perror(msg); \ + exit(EXIT_FAILURE); \ + } while (0) + +#define TEMP_FAILURE_RETRY(var, expression) \ + do { \ + ptrdiff_t __result; \ + do { \ + __result = (expression); \ + } while (__result == -1L && errno == EINTR); \ + var = __result; \ + } while (0) + +extern char **environ; + +void do_write(int sd, const char *raw, size_t len) { + size_t written = 0; + ptrdiff_t n = -1; + + while (written < len) { + TEMP_FAILURE_RETRY(n, write(sd, raw + written, len - written)); + if (n == -1) { + close(sd); + HANDLE_ERROR("write() failed"); + } + written += (size_t) n; + } +} + +static inline size_t true_sun_len(const struct sockaddr_un *ptr) { +#ifdef __linux__ + /* Because SUN_LEN uses strlen and abstract namespace paths begin + * with a null byte, SUN_LEN is broken for these. Passing the full + * struct size also fails on Linux, so compute manually. The + * abstract namespace is Linux-only. */ + if (ptr->sun_path[0] == '\0') { + return sizeof(ptr->sun_family) + strlen(ptr->sun_path + 1) + 1; + } +#endif +#ifdef SUN_LEN + /* If the vendor provided SUN_LEN, we may as well use it. */ + return SUN_LEN(ptr); +#else + /* SUN_LEN is not POSIX, so if it was not provided, use the struct + * size as a fallback. */ + return sizeof(struct sockaddr_un); +#endif +} + +#ifdef __linux__ +# define ADDRESS_TEMPLATE "powerline-ipc-%d" +# define A +1 +#else +# define ADDRESS_TEMPLATE "/tmp/powerline-ipc-%d" +# define A +#endif + +#define ADDRESS_SIZE sizeof(ADDRESS_TEMPLATE) + (sizeof(uid_t) * 4) +#define NUM_ARGS_SIZE (sizeof(int) * 2 + 1) +#define BUF_SIZE 4096 +#define NEW_ARGV_SIZE 200 + +int main(int argc, char *argv[]) { + int sd = -1; + int i; + ptrdiff_t read_size; + struct sockaddr_un server; + char address_buf[ADDRESS_SIZE]; + const char eof[2] = "\0\0"; + char num_args[NUM_ARGS_SIZE]; + char buf[BUF_SIZE]; + char *newargv[NEW_ARGV_SIZE]; + char *wd = NULL; + char **envp; + const char *address; + int len; + + if (argc < 2) { + printf("Must provide at least one argument.\n"); + return EXIT_FAILURE; + } + + if (argc > 3 && strcmp(argv[1], "--socket") == 0) { + address = argv[2]; + argv += 2; + argc -= 2; + } else { + snprintf(address_buf, ADDRESS_SIZE, ADDRESS_TEMPLATE, getuid()); + address = &(address_buf[0]); + } + + sd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sd == -1) + HANDLE_ERROR("socket() failed"); + + memset(&server, 0, sizeof(struct sockaddr_un)); + server.sun_family = AF_UNIX; + strncpy(server.sun_path A, address, strlen(address)); + + if (connect(sd, (struct sockaddr *) &server, true_sun_len(&server)) < 0) { + close(sd); + /* We failed to connect to the daemon, execute powerline instead */ + argc = (argc < NEW_ARGV_SIZE - 1) ? argc : NEW_ARGV_SIZE - 1; + for (i = 1; i < argc; i++) + newargv[i] = argv[i]; + newargv[0] = "powerline-render"; + newargv[argc] = NULL; + execvp("powerline-render", newargv); + } + + len = snprintf(num_args, NUM_ARGS_SIZE, "%x", argc - 1); + do_write(sd, num_args, len); + do_write(sd, eof, 1); + + for (i = 1; i < argc; i++) { + do_write(sd, argv[i], strlen(argv[i])); + do_write(sd, eof, 1); + } + + wd = getcwd(NULL, 0); + if (wd != NULL) { + do_write(sd, wd, strlen(wd)); + free(wd); + wd = NULL; + } + do_write(sd, eof, 1); + + for(envp=environ; *envp; envp++) { + do_write(sd, *envp, strlen(*envp)); + do_write(sd, eof, 1); + } + + do_write(sd, eof, 2); + + read_size = -1; + while (read_size != 0) { + TEMP_FAILURE_RETRY(read_size, read(sd, buf, BUF_SIZE)); + if (read_size == -1) { + close(sd); + HANDLE_ERROR("read() failed"); + } else if (read_size > 0) { + do_write(STDOUT_FILENO, buf, (size_t) read_size); + } + } + + close(sd); + + return 0; +} diff --git a/client/powerline.py b/client/powerline.py new file mode 100755 index 0000000..28492c1 --- /dev/null +++ b/client/powerline.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import socket +import errno +import os + +try: + from posix import environ +except ImportError: + from os import environ + +# XXX Hack for importing powerline modules to work. +sys.path.pop(0) + +try: + from powerline.lib.encoding import get_preferred_output_encoding +except ImportError: + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) + from powerline.lib.encoding import get_preferred_output_encoding + + +if len(sys.argv) < 2: + print('Must provide at least one argument.', file=sys.stderr) + raise SystemExit(1) + +use_filesystem = not sys.platform.lower().startswith('linux') + +if sys.argv[1] == '--socket': + address = sys.argv[2] + if not use_filesystem: + address = '\0' + address + del sys.argv[1:3] +else: + address = ('/tmp/powerline-ipc-%d' if use_filesystem else '\0powerline-ipc-%d') % os.getuid() + +sock = socket.socket(family=socket.AF_UNIX) + + +def eintr_retry_call(func, *args, **kwargs): + while True: + try: + return func(*args, **kwargs) + except EnvironmentError as e: + if getattr(e, 'errno', None) == errno.EINTR: + continue + raise + + +try: + eintr_retry_call(sock.connect, address) +except Exception: + # Run the powerline renderer + args = ['powerline-render'] + sys.argv[1:] + os.execvp('powerline-render', args) + +fenc = get_preferred_output_encoding() + + +def tobytes(s): + if isinstance(s, bytes): + return s + else: + return s.encode(fenc) + + +args = [tobytes('%x' % (len(sys.argv) - 1))] +args.extend((tobytes(s) for s in sys.argv[1:])) + + +try: + cwd = os.getcwd() +except EnvironmentError: + pass +else: + if not isinstance(cwd, bytes): + cwd = cwd.encode(fenc) + args.append(cwd) + + +args.extend((tobytes(k) + b'=' + tobytes(v) for k, v in environ.items())) + +EOF = b'\0\0' + +for a in args: + eintr_retry_call(sock.sendall, a + b'\0') + +eintr_retry_call(sock.sendall, EOF) + +received = [] +while True: + r = sock.recv(4096) + if not r: + break + received.append(r) + +sock.close() + +if sys.version_info < (3,): + sys.stdout.write(b''.join(received)) +else: + sys.stdout.buffer.write(b''.join(received)) diff --git a/client/powerline.sh b/client/powerline.sh new file mode 100755 index 0000000..ad278c2 --- /dev/null +++ b/client/powerline.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +use_filesystem=1 +darwin= +if test -n "$OSTYPE" ; then + # OSTYPE variable is a shell feature. supported by bash and zsh, but not + # dash, busybox or (m)ksh. + if test "${OSTYPE#linux}" '!=' "${OSTYPE}" ; then + use_filesystem= + elif test "${OSTYPE#darwin}" ; then + darwin=1 + fi +elif command -v uname >/dev/null ; then + if uname -o | grep -iqF linux ; then + use_filesystem= + elif uname -o | grep -iqF darwin ; then + darwin=1 + fi +fi + +if test "$1" = "--socket" ; then + shift + ADDRESS="$1" + shift +else + ADDRESS="powerline-ipc-${UID:-`id -u`}" + test -n "$use_filesystem" && ADDRESS="/tmp/$ADDRESS" +fi + +if test -n "$darwin" ; then + ENV=genv +else + ENV=env +fi + +if test -z "$use_filesystem" ; then + ADDRESS="abstract-client:$ADDRESS" +fi + +# Warning: env -0 does not work in busybox. Consider switching to parsing +# `set` output in this case +( + printf '%x\0' "$#" + for argv in "$@" ; do + printf '%s\0' "$argv" + done + printf '%s\0' "$PWD" + $ENV -0 +) 2>/dev/null | socat -lf/dev/null -t 10 - "$ADDRESS" + +if test $? -ne 0 ; then + powerline-render "$@" +fi |