/* * * Copyright (c) 1989 X Consortium 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 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 X CONSORTIUM 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. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. * * * Author: Ralph Swick, DEC/Project Athena */ /* xsel.c Implements an X selection command line interface. You can copy input from stdin to X selection and paste the current X selection the stdout. ver 0.01 vherva 13082000 ver 0.02 vherva 14082000 - fixed -display switch, a hang with -p and empty selection and cleaned up a bit. ver 0.03 vherva 11062001 - use XSetWMHints to start the program in iconic mode. This workarounds (or fixes, depending on how do you look at it) the orphaned window behaviour some folks were seeing. (You do get an entry to the KDE task bar etc, but that can be seen as useful, hence I'm lazy fix it.) ver 0.04 vherva 23072002 - add --selection, --merge, clean up. ver 0.04.1 vherva 30112002 Fix a typo in WriteStdout() - thanks to José Fonseca This code is entirely based on the XFree86 xcutsel.c. To compile: gcc xsel.c -O2 -o xsel -lX11 -lXt -lXaw -L/usr/X11R6/lib/ For X selection documentation, see http://www.freedesktop.org/standards/clipboards.txt http://www.xfree86.org/~keithp/talks/selection.ps */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef XKB #include #endif String selection_type = "PRIMARY"; Atom selection; int do_paste = 0; int do_copy = 0; int do_merge = 0; static XrmOptionDescRec optionDesc[] = { }; #define Offset(field) XtOffsetOf(OptionsRec, field) void Syntax(call) char *call; { fprintf (stderr, "xsel 0.04 23072002\n\n" "Command line interface to the X selections\n\n" "usage: %s action [options]\n" "actions:\n" " -p or --paste\n" " Paste the current X selection (if any) to stdout and\n" " and exit.\n" " -c or --copy\n" " Copy the input from stdin to the X selection. Note that\n" " the X selection model has no concept of selection buffer\n" " on the server. Rather, the client that holds the current\n" " selection makes it available to the other clients and\n" " has to wait until someone requests it. Hence, %s forks\n" " and serves the selection requests until someone else\n" " claims the selection.\n" " The copied string can also be given on command line\n" " rather than through stdin: --copy \"string to copy\".\n" " -m or --merge\n" " Same as -c, but merge the input to the current selection\n" " instead of overwriting it.\n" "options:\n" " -s SELECTION or --selection SELECTION\n" " Where SELECTION is either \"PRIMARY\", \"SECONDARY\" or\n" " \"CLIPBOARD\". Selects which X selection to use. For -p,\n" " the default is to first try PRIMARY, and if it is empty,\n" " then SECONDARY and finally CLIPBOARD. For -c and -m, the\n" " default is PRIMARY.\n", call, call); exit (1); } char* tmpbuffer = NULL; int tmpbuflen = -1; #define ALLOCCHUNK 1024 static int ReadFd(fd, result) int fd; char** result; { int len = 0; int allocated = 0; *result = NULL; while (1) { int res = 0; if (len - allocated == 0) *result = realloc(*result, allocated += ALLOCCHUNK); if (!*result) return -1; res = read(fd, &(*result)[len], allocated - len); if (!res) return len; else if (res < 0) return res; len += res; } return len; } static int ReadStdin(result) char** result; { return ReadFd(0, result); } static int WriteFd(fd, string, len) int fd; char* string; int len; { int written; for (written = 0; written < len; ) { int res = write(fd, &string[written], len - written); if (!res) return len; else if (res < 0) return res; written += res; } return len; } static int WriteStdout(string, len) char* string; int len; { return WriteFd(1, string, len); } /* ARGSUSED */ static void Quit(w, closure, callData) Widget w; XtPointer closure; /* unused */ XtPointer callData; /* unused */ { XtCloseDisplay( XtDisplay(w) ); free(tmpbuffer); exit(0); } static int selection_current = 0; static void StoreBuffer(w, client_data, selection, type, value, length, format) Widget w; XtPointer client_data; Atom *selection, *type; XtPointer value; unsigned long *length; int *format; { if ((*type == 0 || *type == XT_CONVERT_FAIL || *length == 0) && selection_current == 0) { XtFree(value); selection_type = "SECONDARY"; XmuInternStrings(XtDisplay(w), &selection_type, ONE, selection); selection_current = 1; XtGetSelectionValue(w, *selection, XA_STRING, StoreBuffer, NULL, XtLastTimestampProcessed(XtDisplay(w))); return; } else if ((*type == 0 || *type == XT_CONVERT_FAIL || *length == 0) && selection_current == 1) { XtFree(value); selection_type = "CLIPBOARD"; XmuInternStrings(XtDisplay(w), &selection_type, ONE, selection); selection_current = 2; XtGetSelectionValue(w, *selection, XA_STRING, StoreBuffer, NULL, XtLastTimestampProcessed(XtDisplay(w))); return; } if (do_paste) WriteStdout(value, *length); else if (do_merge) { char* buf = malloc(*length + tmpbuflen + 1); if (!buf) Quit(w, NULL, NULL); bcopy(value, buf, *length); buf[*length] = '\0'; strcat(buf, tmpbuffer); free(tmpbuffer); tmpbuffer = buf; tmpbuflen = strlen(buf); } XtFree(value); if (do_paste) Quit(w, NULL, NULL); } static Boolean ConvertSelection(w, selection, target, type, value, length, format) Widget w; Atom *selection, *target, *type; XtPointer *value; unsigned long *length; int *format; { Display* d = XtDisplay(w); XSelectionRequestEvent* req = XtGetSelectionRequest(w, *selection, (XtRequestId)NULL); if (*target == XA_STRING || *target == XA_TEXT(d)) { *type = XA_STRING; *value = XtMalloc((Cardinal) tmpbuflen + 1); memcpy((char *) *value, tmpbuffer, tmpbuflen); *length = tmpbuflen; *format = 8; return True; } if (XmuConvertStandardSelection(w, req->time, selection, target, type, (XPointer *)value, length, format)) return True; return False; } static void LoseSelection(w, selection) Widget w; Atom *selection; { /* Only remain resident until we lose the selection - after that there will be nothing to do. */ Quit(w, NULL, NULL); } /* ARGSUSED */ static void GetSelection(w, closure, callData) Widget w; XtPointer closure; /* unused */ XtPointer callData; /* unused */ { XtGetSelectionValue(w, selection, XA_STRING, StoreBuffer, NULL, XtLastTimestampProcessed(XtDisplay(w))); } /* ARGSUSED */ static void GetBuffer(w, closure, callData) Widget w; XtPointer closure; XtPointer callData; /* unused */ { if (XtOwnSelection(w, selection, XtLastTimestampProcessed(XtDisplay(w)), ConvertSelection, LoseSelection, NULL)); } int ParseArgs(argc, argv) int argc; char *argv[]; { int i; int errs = 0; for (i = 1; i < argc; i++) { if (strncmp(argv[i], "-p", 2) == 0 || strncmp(argv[i], "--p", 3) == 0) do_paste++; else if (strncmp(argv[i], "-c", 2) == 0 || strncmp(argv[i], "--c", 3) == 0) do_copy++; else if (strncmp(argv[i], "-m", 2) == 0 || strncmp(argv[i], "--m", 3) == 0) do_merge++; else if (strncmp(argv[i], "-s", 2) == 0 || strncmp(argv[i], "--s", 3) == 0) { i++; if (i >= argc) errs = 2; else { selection_current = -1; if (tolower(argv[i][0]) == 'p') selection_type = "PRIMARY"; else if (tolower(argv[i][0]) == 's') selection_type = "SECONDARY"; else if (tolower(argv[i][0]) == 'c') selection_type = "CLIPBOARD"; else goto error; } } else { if (!do_copy && !do_merge) goto error; tmpbuflen += strlen(argv[i]) + 1; tmpbuffer = realloc(tmpbuffer, tmpbuflen); if (!tmpbuffer) return -1; if (tmpbuflen == strlen(argv[i])) tmpbuffer[0] = '\0'; else strcat(tmpbuffer, " "); strcat(tmpbuffer, argv[i]); tmpbuflen = strlen(tmpbuffer); } } if (do_paste + do_copy + do_merge == 1) return 1; error: Syntax(argv[0]); exit(2); } int main(argc, argv) int argc; char *argv[]; { Widget box; XtAppContext appcon; Widget shell; XWMHints hints; XtSetLanguageProc(NULL, NULL, NULL); ParseArgs(argc, argv); if (do_copy || do_merge) { if (tmpbuflen == -1) tmpbuflen = ReadStdin(&tmpbuffer); if (tmpbuflen < 0) { perror("Error in ReadStdin(): "); exit(1); } /* The X selection model has no concept of selection buffer on the server. Rather, the client that holds the current selection makes it available to the other clients and has to wait until someone requests it. Hence, we fork and serve the selection requests until someone else claims the selection. (We exit on LoseSelection callback.) */ if (fork()) return 0; } shell = XtAppInitialize(&appcon, "Xsel", optionDesc, XtNumber(optionDesc), &argc, argv, NULL, NULL, 0); XmuInternStrings(XtDisplay(shell), &selection_type, ONE, &selection); /* Somebody please tell me how I can access X selection without creating dummy widget... */ box = XtCreateManagedWidget("box", boxWidgetClass, shell, NULL, ZERO); XtRealizeWidget(shell); hints.initial_state = IconicState; hints.flags = StateHint; XSetWMHints(XtDisplay(shell), XtWindow(shell), &hints); if (do_paste || do_merge) GetSelection(shell, NULL, NULL); if (do_copy || do_merge) GetBuffer(shell, NULL, NULL); XtAppMainLoop(appcon); return 0; }