/**
 * 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 ();
}


