Initial commit of 2.0. - tthinglaunch - A simple command and password prompter for X11.
       
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit 606c0f24754b284bacbb8de22ef5d0c73f065e8f
 (HTM) Author: Christoph Lohmann <20h@r-36.net>
       Date:   Sun, 27 Mar 2011 18:50:19 +0200
       
       Initial commit of 2.0.
       
       Diffstat:
         LICENSE                             |      23 +++++++++++++++++++++++
         LICENSE.orig                        |      41 +++++++++++++++++++++++++++++++
         Makefile                            |      58 ++++++++++++++++++++++++++++++
         README.md                           |      36 +++++++++++++++++++++++++++++++
         config.h                            |       5 +++++
         config.mk                           |      26 ++++++++++++++++++++++++++
         thinglaunch.c                       |     466 +++++++++++++++++++++++++++++++
       
       7 files changed, 655 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/LICENSE b/LICENSE
       @@ -0,0 +1,23 @@
       +MIT/X Consortium License
       +
       +© 2011 Christoph Lohmann <20h@r-36.net>
       +
       +Permission is hereby granted, free of charge, to any person obtaining a
       +copy of this software and associated documentation files (the "Software"),
       +tto deal in the Software without restriction, including without limitation
       +tthe rights to use, copy, modify, merge, publish, distribute, sublicense,
       +and/or sell copies of the Software, and to permit persons to whom the
       +Software is furnished to do so, subject to the following conditions:
       +
       +The above copyright notice and this permission notice shall be included in
       +all copies or substantial portions of the Software.
       +
       +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
       +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
       +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
       +DEALINGS IN THE SOFTWARE.
       +
       +SEE LICENSE.orig FOR FURTHER APPLYING LICENSE TERMS.
 (DIR) diff --git a/LICENSE.orig b/LICENSE.orig
       @@ -0,0 +1,41 @@
       +/* This program is a quick little launcher program for X.
       + *
       + * You run the program, it grabs the display (hopefully nothing else has it
       + * grabbed), and you type what you want to run. The styling is minimalist.
       + *
       + * (c) 2003 Matt Johnston
       + * matt (at) ucc.asn.au
       + *
       + * Compile it with
       + * cc thinglaunch.c -o thinglaunch -lX11 -L/usr/X11R6/lib -lpthread
       + * (works for Linux and OSF/1 anyway, for static you might need -ldl, and you
       + * mightn't need -lpthread.)
       + *
       + * This program can be freely distributed in source or binary form, under a
       + * quite permissive license. See the bottom of this file for the full license.
       + * If that license is too restrictive, mail me and you can choose another one.
       + *
       + * $Id: thinglaunch.c,v 1.8 2004/09/20 14:27:48 matt Exp $
       + */
       +/*
       +Copyright (c) 2003 Matt Johnston
       +All rights reserved.
       +
       +Permission is hereby granted, free of charge, to any person obtaining a copy
       +of this software and associated documentation files (the "Software"), to deal
       +in the Software without restriction, including without limitation the rights
       +tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       +copies of the Software, and to permit persons to whom the Software is
       +furnished to do so, subject to the following conditions:
       +
       +The above copyright notice and this permission notice shall be included in all
       +copies or substantial portions of the Software.
       +
       +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +SOFTWARE.
       +*/
 (DIR) diff --git a/Makefile b/Makefile
       @@ -0,0 +1,58 @@
       +# thinglaunch
       +# See LICENSE file for copyright and license details.
       +
       +include config.mk
       +
       +SRC = ${NAME}.c
       +
       +OBJ = ${SRC:.c=.o}
       +
       +all: options ${NAME}
       +
       +options:
       +        @echo ${NAME} build options:
       +        @echo "CFLAGS   = ${CFLAGS}"
       +        @echo "LDFLAGS  = ${LDFLAGS}"
       +        @echo "CC       = ${CC}"
       +
       +.c.o:
       +        @echo CC $<
       +        @${CC} -c ${CFLAGS} $<
       +
       +${OBJ}: config.mk
       +
       +${NAME}: ${OBJ}
       +        @echo CC -o $@
       +        @${CC} -o $@ ${OBJ} ${LDFLAGS}
       +        @ln -s ${NAME} thingaskpass
       +
       +clean:
       +        @echo cleaning
       +        @rm -f ${NAME} thingaskpass ${OBJ} ${NAME}-${VERSION}.tar.gz
       +
       +dist: clean
       +        @echo creating dist tarball
       +        @mkdir -p ${NAME}-${VERSION}
       +        @cp -R LICENSE LICENSE.orig Makefile README.md config.mk \
       +                ${SRC} *.h ${NAME}-${VERSION}
       +        @tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION}
       +        @gzip ${NAME}-${VERSION}.tar
       +        @rm -rf ${NAME}-${VERSION}
       +
       +etc:
       +        @echo installing etc files into ${DESTDIR}/etc/${NAME}
       +        @mkdir -p ${DESTDIR}/etc/${NAME}
       +        @cp -R etc/${NAME}/* ${DESTDIR}/etc/${NAME}
       +
       +install: all
       +        @echo installing executable file to ${DESTDIR}${PREFIX}/bin
       +        @mkdir -p ${DESTDIR}${PREFIX}/bin
       +        @cp -f ${NAME} thingaskpass ${DESTDIR}${PREFIX}/bin
       +        @chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
       +
       +uninstall:
       +        @echo removing executable file from ${DESTDIR}${PREFIX}/bin
       +        @rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
       +        @rm -f ${DESTDIR}${PREFIX}/bin/thingaskpass
       +
       +.PHONY: all options clean dist install uninstall
 (DIR) diff --git a/README.md b/README.md
       @@ -0,0 +1,36 @@
       +# Thinglaunch - a simple entry box for X11 
       +
       +The first intention, as done by the original creator Matt Johnston
       +<matt@ucc.asn.au>, was to launch simple commandlines.
       +
       +In 2011 the single file project was extended by features like Unicode
       +support, a prompt and an ssh-askpass compatibility layer.
       +
       +## Installation
       +
       +        % tar -xzvf thinglaunch-*.tar.gz
       +        % cd thinglaunch
       +        % make
       +        % sudo PREFIX=/usr make install
       +
       +This will create the executable »thinglaunch« and »thingaskpass« in
       +»/usr/bin«. Thinglaunch will ask for a command and execute it and
       +tthingaskpass can be used as a SSH_ASKPASS parameter value, which will
       +be used by ssh-agent to gather the password for private keys.
       +
       +## Usage
       +
       +        # Get some input string and print it to stdout. There will
       +        # be the prompt prepended "to stdout> ".
       +        % thinglaunch -o -p "to stdout> "
       +
       +        # Ask for a command, which will be executed. During entering
       +        # the command, the entered string will be replaced by asterisks. 
       +        % thinglaunch -s -p "secret cmd> "
       +
       +        # This symlink predefines -s, -o and -p "secret> ".
       +        % ln -s thinglaunch thingaskpass
       +        % ./thingaskpass
       +
       +Have fun!
       +
 (DIR) diff --git a/config.h b/config.h
       @@ -0,0 +1,5 @@
       +static const char *font = "-*-*-medium-*-*-*-14-*-*-*-*-*-*-*";
       +static const char *prompt = "exec> ";
       +static const char *normbgcolor = "#222222";
       +static const char *normfgcolor = "#cccccc";
       +
 (DIR) diff --git a/config.mk b/config.mk
       @@ -0,0 +1,26 @@
       +# thinglaunch metadata
       +NAME = thinglaunch
       +VERSION = 2.00
       +
       +# Customize below to fit your system
       +
       +# paths
       +PREFIX ?= /usr
       +MANPREFIX = ${PREFIX}/share/man
       +
       +X11INC = /usr/X11R6/include
       +X11LIB = /usr/X11R6/lib
       +
       +# includes and libs
       +INCS = -I. -I/usr/include
       +LIBS = -L/usr/lib -L${X11LIB} -lc -lX11
       +
       +# flags
       +CPPFLAGS = -DVERSION=\"${VERSION}\"
       +CFLAGS = -g -std=gnu99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
       +LDFLAGS = -g ${LIBS}
       +#LDFLAGS = -s ${LIBS}
       +
       +# compiler and linker
       +CC = cc
       +
 (DIR) diff --git a/thinglaunch.c b/thinglaunch.c
       @@ -0,0 +1,466 @@
       +/*
       + * Copy me if you can.
       + * by 20h
       + *
       + * For now this is a slightly modified version of the original from
       + * Matt Johnston <matt@ucc.asn.au>. See LICENSE.orig for his messages.
       + */
       +#include <unistd.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xatom.h>
       +#include <X11/Xutil.h>
       +#include <X11/Xlocale.h>
       +#include <locale.h>
       +#include <unistd.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdarg.h>
       +#include <string.h>
       +#include <strings.h>
       +#include <libgen.h>
       +#include <wchar.h>
       +
       +#include "config.h"
       +
       +unsigned long getcolor(const char *colstr);
       +void createwindow(void);
       +void setupgc(void);
       +void eventloop(void);
       +void grabhack(void);
       +void redraw(void);
       +void keypress(XKeyEvent *keyevent);
       +void execcmd(void);
       +void die(char *errstr, ...);
       +
       +Display *dpy;
       +GC gc;
       +GC rectgc;
       +XIM im;
       +XIC ic;
       +Window win;
       +XFontStruct *font_info;
       +XFontSet fontset;
       +int screen, issecret = 0, tostdout = 0;
       +unsigned long fgcol, bgcol;
       +
       +#define MAXCMD 255
       +#define WINWIDTH 640
       +#define WINHEIGHT 25
       +
       +/* the actual commandline */
       +wchar_t command[MAXCMD+1];
       +wchar_t secret[MAXCMD+1];
       +char cbuf[MAXCMD*4+1];
       +
       +void
       +usage(char *argv0)
       +{
       +        fprintf(stderr, "usage: %s [-hos] [-p prompt]\n", argv0);
       +        exit(1);
       +}
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        int i;
       +
       +        if (strstr(argv[0], "thingaskpass")) {
       +                issecret = 1;
       +                tostdout = 1;
       +                prompt = "secret> ";
       +        }
       +        if (argc > 1) {
       +                for (i = 1; argv[i]; i++) {
       +                        if (argv[i][0] == '-') {
       +                                switch (argv[i][1]) {
       +                                case 'o':
       +                                        tostdout = 1;
       +                                        break;
       +                                case 's':
       +                                        issecret = 1;
       +                                        break;
       +                                case 'p':
       +                                        if (!argv[i+1])
       +                                                usage(argv[0]);
       +                                        prompt = argv[i+1];
       +                                        i++;
       +                                        break;
       +                                default:
       +                                case 'h':
       +                                        usage(argv[0]);
       +                                        break;
       +                                }
       +                        }
       +                }
       +        }
       +
       +        bzero(command, sizeof(command));
       +        bzero(secret, sizeof(secret));
       +
       +        createwindow();
       +        setupgc();
       +        grabhack();
       +        eventloop();
       +
       +        return 0;
       +}
       +
       +unsigned long
       +getcolor(const char *colstr)
       +{
       +        Colormap cmap = DefaultColormap(dpy, screen);
       +        XColor color;
       +
       +        if (!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
       +                die("error, canno allocate color '%s'\n", colstr);
       +        return color.pixel;
       +}
       +
       +/*
       + * Stolen from:
       + * http://menehune.opt.wfu.edu/Kokua/Irix_6.5.21_doc_cd/usr/share/\
       + * Insight/library/SGI_bookshelves/SGI_Developer/books/XLib_PG/sgi_\
       + * html/ch11.html#S2-1002-11-11
       + */
       +XIMStyle
       +choosebetterstyle(XIMStyle style1, XIMStyle style2)
       +{
       +        XIMStyle s,t;
       +        XIMStyle preedit = XIMPreeditArea | XIMPreeditCallbacks |
       +                XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone;
       +        XIMStyle status = XIMStatusArea | XIMStatusCallbacks |
       +                XIMStatusNothing | XIMStatusNone;
       +        if (style1 == 0) return style2;
       +        if (style2 == 0) return style1;
       +        if ((style1 & (preedit | status)) == (style2 & (preedit | status)))
       +                return style1;
       +        s = style1 & preedit;
       +        t = style2 & preedit;
       +        if (s != t) {
       +                if (s | t | XIMPreeditCallbacks)
       +                        return (s == XIMPreeditCallbacks)?style1:style2;
       +                else if (s | t | XIMPreeditPosition)
       +                        return (s == XIMPreeditPosition)?style1:style2;
       +                else if (s | t | XIMPreeditArea)
       +                        return (s == XIMPreeditArea)?style1:style2;
       +                else if (s | t | XIMPreeditNothing)
       +                        return (s == XIMPreeditNothing)?style1:style2;
       +        }
       +        else { /* if preedit flags are the same, compare status flags */
       +                s = style1 & status;
       +                t = style2 & status;
       +                if (s | t | XIMStatusCallbacks)
       +                        return (s == XIMStatusCallbacks)?style1:style2;
       +                else if (s | t | XIMStatusArea)
       +                        return (s == XIMStatusArea)?style1:style2;
       +                else if (s | t | XIMStatusNothing)
       +                        return (s == XIMStatusNothing)?style1:style2;
       +        }
       +}
       +
       +void
       +initim(void)
       +{
       +        XIMStyles *im_supported_styles;
       +        XIMStyle app_supported_styles;
       +        XIMStyle style;
       +        XIMStyle best_style;
       +        XVaNestedList list;
       +        char **missing_charsets;
       +        int num_missing_charsets = 0;
       +        char *default_string;
       +        int i;
       +
       +        fontset = XCreateFontSet(dpy, font, &missing_charsets,
       +                        &num_missing_charsets, &default_string);
       +        if (num_missing_charsets > 0)
       +                XFreeStringList(missing_charsets);
       +
       +        if (!(im = XOpenIM(dpy, NULL, NULL, NULL)))
       +                die("Couldn't open input method.\n");
       +
       +        XGetIMValues(im, XNQueryInputStyle, &im_supported_styles, NULL);
       +        app_supported_styles = XIMPreeditNone | XIMPreeditNothing \
       +               | XIMPreeditArea;
       +        app_supported_styles |= XIMStatusNone | XIMStatusNothing \
       +                                | XIMStatusArea;
       +
       +        for(i = 0, best_style = 0; i < im_supported_styles->count_styles;
       +                        i++) {
       +                style = im_supported_styles->supported_styles[i];
       +                if ((style & app_supported_styles) == style)
       +                        best_style = choosebetterstyle(style, best_style);
       +        }
       +        if (best_style == 0)
       +                die("no common shared interaction style found.\n");
       +        XFree(im_supported_styles);
       +
       +        list = XVaCreateNestedList(0, XNFontSet, fontset, NULL);
       +        ic = XCreateIC(im, XNInputStyle, best_style, XNClientWindow, win,
       +                        XNPreeditAttributes, list, XNStatusAttributes,
       +                        list, NULL);
       +        XFree(list);
       +        if (ic == NULL)
       +                die("Could not create input context.\n");
       +}
       +
       +void
       +createwindow(void)
       +{
       +        char *display_name;
       +        int display_width, display_height;
       +        int top, left;
       +        XSizeHints *win_size_hints;
       +        XSetWindowAttributes attrib;
       +
       +        if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
       +                fprintf(stderr, "warning: no locale support.\n");
       +
       +        display_name = getenv("DISPLAY");
       +        if (display_name == NULL)
       +                die("DISPLAY not set");
       +
       +        dpy = XOpenDisplay(display_name);
       +        if (dpy == NULL)
       +                die("Couldn't connect to DISPLAY");
       +
       +        if (!XSetLocaleModifiers(""))
       +                fprintf(stderr, "warning: could not set local modifiers.\n");
       +
       +        initim();
       +
       +        screen = DefaultScreen(dpy);
       +        display_width = DisplayWidth(dpy, screen);
       +        display_height = DisplayHeight(dpy, screen);
       +
       +        top = (display_height/2 - WINHEIGHT/2);
       +        left = (display_width/2 - WINWIDTH/2);
       +
       +        bgcol = getcolor(normbgcolor);
       +        fgcol = getcolor(normfgcolor);
       +
       +        /*win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen),
       +                        left, top, WINWIDTH, WINHEIGHT, borderwidth,
       +                        bgcol, bgcol);*/
       +
       +        attrib.override_redirect= True;
       +        win = XCreateWindow(dpy, RootWindow(dpy, screen),
       +                        left, top, WINWIDTH, WINHEIGHT,
       +                        0, CopyFromParent,InputOutput,CopyFromParent,
       +                        CWOverrideRedirect,&attrib);
       +
       +        /* set up the window hints etc */
       +        win_size_hints = XAllocSizeHints();
       +        if (!win_size_hints)
       +                die("out of memory allocating hints");
       +
       +        win_size_hints->flags = PMaxSize | PMinSize;
       +        win_size_hints->min_width = win_size_hints->max_width = WINWIDTH;
       +
       +        win_size_hints->min_height = win_size_hints->max_height = WINHEIGHT;
       +        XSetWMNormalHints(dpy, win, win_size_hints);
       +
       +        XFree(win_size_hints);
       +
       +        XMapWindow(dpy, win);
       +}
       +
       +void
       +setupgc(void)
       +{
       +        XGCValues values;
       +        int valuemask = 0;
       +        int line_width = 1;
       +        int line_style = LineSolid;
       +        int cap_style = CapButt;
       +        int join_style = JoinBevel;
       +
       +        gc = XCreateGC(dpy, win, valuemask, &values);
       +        rectgc = XCreateGC(dpy, win, valuemask, &values);
       +        XSetForeground(dpy, gc, fgcol);
       +        XSetBackground(dpy, gc, bgcol);
       +
       +        XSetForeground(dpy, rectgc, bgcol);
       +        XSetBackground(dpy, rectgc, bgcol);
       +
       +        XSetLineAttributes(dpy, gc, line_width, line_style,
       +                        cap_style, join_style);
       +
       +        /* setup the font */
       +        font_info = XLoadQueryFont(dpy, font);
       +        if (!font_info)
       +                die("couldn't load font");
       +
       +        XSetFont(dpy, gc, font_info->fid);
       +}
       +
       +void
       +eventloop(void)
       +{
       +        XEvent e;
       +
       +        redraw();
       +
       +        XSelectInput(dpy, win, ExposureMask | KeyPressMask);
       +
       +        for (;;) {
       +                XNextEvent(dpy, &e);
       +                switch(e.type) {
       +                case Expose:
       +                        redraw();
       +                        break;
       +                case KeyPress:
       +                        keypress(&e.xkey);
       +                        break;
       +                default:
       +                        break;
       +                }
       +
       +        }
       +}
       +
       +/* this loop is required since pwm grabs the keyboard during the event loop */
       +void
       +grabhack(void)
       +{
       +        int maxwait = 3000000; /* 3 seconds */
       +        int interval = 5000; /* 5 millisec */
       +        int i, x;
       +
       +        redraw();
       +
       +        /* if it takes longer than maxwait, just die */
       +        for (i = 0; i < (maxwait / interval); i++) {
       +                usleep(interval);
       +                x = XGrabKeyboard(dpy, win, False, GrabModeAsync,
       +                                GrabModeAsync, CurrentTime);
       +                if (x == 0)
       +                        return;
       +        }
       +
       +        die("Couldn't grab keyboard");
       +}
       +
       +void
       +redraw(void)
       +{
       +        int font_height, textwidth, promptwidth, dir, ascent, descent;
       +        XCharStruct cs;
       +        XRectangle ink, logical;
       +
       +        font_height = font_info->ascent + font_info->descent;
       +        XTextExtents(font_info, prompt, strlen(prompt), &dir, &ascent,
       +                        &descent, &cs);
       +        promptwidth = cs.width;
       +        XwcTextExtents(fontset, command, wcslen(command), &ink, &logical);
       +        textwidth = logical.width;
       +        textwidth += promptwidth;
       +
       +        XFillRectangle(dpy, win, rectgc, 0, 0, WINWIDTH, WINHEIGHT);
       +        XDrawRectangle(dpy, win, gc, 0, 0, WINWIDTH-1, WINHEIGHT-1);
       +        XDrawString(dpy, win, gc, 2, font_height+2, prompt,
       +                        strlen(prompt));
       +        XwcDrawString(dpy, win, fontset, gc, 4 + promptwidth,
       +                        font_height+2, command, wcslen(command));
       +        XDrawLine(dpy, win, gc, 4 + textwidth, font_height + 2,
       +                        4 + textwidth + 10, font_height+2);
       +
       +        XFlush(dpy);
       +}
       +
       +void
       +keypress(XKeyEvent *keyevent)
       +{
       +        KeySym key_symbol;
       +        int len;
       +        wchar_t buffer[3];
       +
       +        len = XwcLookupString(ic, keyevent, buffer, 3, &key_symbol, NULL);
       +        buffer[len] = L'\0';
       +
       +        switch(key_symbol) {
       +        case XK_Escape:
       +                exit(0);
       +                break;
       +        case XK_BackSpace:
       +                len = wcslen(command);
       +                if (len > 0) {
       +                        command[len-1] = L'\0';
       +                        if (issecret)
       +                                secret[len-1] = L'\0';
       +                }
       +                break;
       +        case XK_Return:
       +        case XK_KP_Enter:
       +                execcmd();
       +                break;
       +        default:
       +                if (key_symbol > 255)
       +                        break;
       +
       +                len = wcslen(command);
       +                if (len < MAXCMD) {
       +                        if (issecret) {
       +                                secret[len] = buffer[0];
       +                                secret[len+1] = L'\0';
       +                                command[len] = L'*';
       +                                command[len+1] = L'\0';
       +                        } else {
       +                                command[len] = buffer[0];
       +                                command[len+1] = L'\0';
       +                        }
       +                }
       +                break;
       +        }
       +        redraw();
       +}
       +
       +void
       +execcmd(void)
       +{
       +        char *shell;
       +        char *argv[4];
       +
       +        XDestroyWindow(dpy, win);
       +
       +        bzero(cbuf, sizeof(cbuf));
       +        if (issecret)
       +                wcstombs(cbuf, secret, sizeof(cbuf)-1);
       +        else
       +                wcstombs(cbuf, command, sizeof(cbuf)-1);
       +
       +        if (tostdout) {
       +                printf("%s\n", cbuf);
       +                exit(0);
       +        }
       +
       +        if (fork())
       +                exit(0);
       +
       +        shell = getenv("SHELL");
       +        if (!shell)
       +                shell = "/bin/sh";
       +
       +        argv[0] = basename(shell);
       +        argv[1] = "-c";
       +        argv[2] = cbuf;
       +        argv[3] = NULL;
       +
       +        execv(shell, argv);
       +        die("aiee, after exec");
       +
       +}
       +
       +
       +void
       +die(char *errstr, ...)
       +{
       +        va_list ap;
       +
       +        va_start(ap, errstr);
       +        vfprintf(stderr, errstr, ap);
       +        va_end(ap);
       +
       +        exit(1);
       +}
       +