"Even if I knew that tomorrow the world would go to pieces, I would still plant my apple tree." Martin Luther

the brown-dragon blog

clip.exe

2009-09-21

I've started using cygwin heavily on Windows XP and it's mostly been great.

One small problem I had is copying output from my cygwin terminal window to the windows clipboard. Mostly paths, text snippets,...things like that.

So I wrote a simple clip utility that will allow me to quickly and easily copy stuff into the windows clipboard from the cygwin prompt. Given the -v option it also writes the clipboard content to the terminal giving me complete access to the clipboard.

Sample Usage:

    which `application` | clip .............#(copy path of application)
    clip < afile.txt .......................#(copy text to clipboard)
    cygpath -w `find -iname 'search'` | clip ...#(copy path to clipboard)
    make | clip -t ...#(copy output of long make)
    clip -v | grep 'searchstring' | clip ...#(filter text)

I've found it very useful when switching between cygwin and windows applications.

NB: Apparently Microsoft also realizes this is a useful functionality! Their latest operating systems (Windows 2003 server and Windows Vista) also provide a clip.exe along these lines.

Check out this blog post for details.

However their version does not handle the paste functionality as elegantly as mine. Plus they seem to have some problem pasting into Notepad!

I think I'll stick to my version after all! :-)

clip.c

/**
 * clip.c
 * Simple command-line utility to copy data to clipboard.
 * Given the "-t" option, echo input to terminal.
 * Given the "-v" option, pastes text data from the clipboard.
 *
 * Sample Usage:
 *      which `application` | clip
 *      clip < afile.txt
 *      make | clip -t
 *      clip -v | grep 'searchstring'
 *
 * NB: Removes trailing '\n' if present in the input.
 *
 * Compiles with gcc.
 *
 * http://the-brown-dragon.com/
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <windows.h>

#define INPUT_BLOCK_SIZE    1024
#define ERR_ALLOC_FAILED    "GlobalAlloc failed!"
#define ERR_LOCK_FAILED     "GlobalLock failed!"
#define ERR_OPENCLIP_FAILED "Failed to open clipboard!"
#define ERR_EMPTYCLIP_FAILED "Failed to empty clipboard!"
#define ERR_SETCLIP_FAILED  "Failed to set data in clipboard!"
#define ERR_GETCLIP_FAILED  "Failed to get data from clipboard!"

#define ERR_FAIL(ermsg, lbl) do { fwrite (ermsg, sizeof (ermsg), 1, stderr); goto lbl; } while (0);

static inline void *
xmalloc (size_t pSize)
{
        void *v;
    /* Allocate or die! */
    if (!(v = malloc (pSize))) {
        perror ("Out of Memory!");
        exit (EXIT_FAILURE);
    }
    /* Return triumphantly bearing memory */
    return v;
}

int copy (int pshow)
{
        char    *buf;
        int      ndx, tot;
        HGLOBAL  hg;
        LPVOID   gd;

    /* Buffer */
    ndx = 0; tot = INPUT_BLOCK_SIZE; buf = xmalloc (tot);
    /* Read input */
    while (fread (buf + ndx, 1, 1, stdin)) {
        if (pshow) printf ("%c", buf [ndx]); /* Echo if asked */
        ++ndx;
        if (ndx == tot) {
                char *tmp = buf;
            /* Expand */
            tot += INPUT_BLOCK_SIZE; buf = xmalloc (tot);
            /* Copy */
            memcpy (buf, tmp, ndx); free (tmp);
        }
    }

    /* Check for error */
    if (!ndx || errno) exit (EXIT_FAILURE);

    /* Terminate read buffer (removing trailing newline) */
    if (buf [ndx - 1] == '\n') buf [ndx - 1] = 0;
    else buf [ndx++] = 0;

    /* Allocate global memory for the text */
    if ((hg = GlobalAlloc (GMEM_MOVEABLE, ndx)) == NULL) ERR_FAIL (ERR_ALLOC_FAILED, err);

    /* Obtain lock for saving data */
    if ((gd = GlobalLock (hg)) == NULL) ERR_FAIL (ERR_LOCK_FAILED, errmc);

    /* Save data */
    memcpy (gd, buf, ndx);

    /* Release lock */
    (void)GlobalUnlock (hg);

    /* Open Clipboard */
    if (!OpenClipboard (NULL)) ERR_FAIL (ERR_OPENCLIP_FAILED, errmc);

    /* Empty Clipboard */
    if (!EmptyClipboard ()) ERR_FAIL (ERR_EMPTYCLIP_FAILED, errcc);

    /* Re-open clipboard */
    (void)CloseClipboard ();
    if (!OpenClipboard (NULL)) ERR_FAIL (ERR_OPENCLIP_FAILED, errmc);

    /* Copy to clipboard */
    if (!SetClipboardData (CF_TEXT, hg)) ERR_FAIL (ERR_SETCLIP_FAILED, errcc);

    /* Close Clipboard */
    (void)CloseClipboard ();

    /* Clean up memory */
    free (buf);

    /* Success */
    return EXIT_SUCCESS;

errcc:
    /* Close Clipboard */
    (void)CloseClipboard ();

errmc:
    /* Free memory allocated */
    (void)GlobalFree (hg);

err:
    /* Clean up memory */
    free (buf);

    /* Report failure */
    return EXIT_FAILURE;
}

int paste (void)
{
        HGLOBAL hg;
        LPVOID  gd;

    /* No text? We're done! */
    if (!IsClipboardFormatAvailable (CF_TEXT)) return EXIT_SUCCESS;

    /* Open clipboard */
    if (!OpenClipboard (NULL)) ERR_FAIL (ERR_OPENCLIP_FAILED, err);

    /* Get Data */
    if ((hg = GetClipboardData (CF_TEXT)) == NULL) ERR_FAIL (ERR_GETCLIP_FAILED, errcc);

    /* Lock Data */
    if ((gd = GlobalLock (hg)) == NULL) ERR_FAIL (ERR_LOCK_FAILED, errcc);

    /* Display data */
    printf ("%s", gd);

    /* Unlock */
    (void)GlobalUnlock (hg);

    /* Close Clipboard */
    (void)CloseClipboard ();

    /* Success */
    return EXIT_SUCCESS;

errcc:
    /* Close Clipboard */
    (void)CloseClipboard ();

err:
    /* Report failure */
    return EXIT_FAILURE;
}

int showerr (void)
{
    printf ("Examples: command | clip\n");
    printf ("          clip < file.txt\n");
    printf ("          make | clip -t\n");
    printf ("          clip -v\n");
    return EXIT_FAILURE;
}

int main (int argc, char *argv[])
{
    /* No arguments */
    if (argc == 1) return copy (0);

    /* Too many arguments */
    if (argc > 2) return showerr ();

    /* Asking for Paste */
    if (!strcmp ("-v", argv [1])) return paste ();

    /* Asking for echo to terminal */
    if (!strcmp ("-t", argv [1])) return copy (1);

    /* Incorrect argument */
    return showerr ();
}

Other Posts

(ordered by Tags then Date)