VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/clipboard/ClipUtil.cpp@ 92176

Last change on this file since 92176 was 92161, checked in by vboxsync, 4 years ago

ValKit/ClipUtil: Listing fix. bugref:10133

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.1 KB
Line 
1/* $Id: ClipUtil.cpp 92161 2021-10-30 01:42:38Z vboxsync $ */
2/** @file
3 * ClipUtil - Clipboard Utility
4 */
5
6/*
7 * Copyright (C) 2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#ifdef RT_OS_OS2
32# define INCL_BASE
33# define INCL_PM
34# define INCL_ERRORS
35# include <os2.h>
36# undef RT_MAX
37#endif
38
39#include <iprt/assert.h>
40#include <iprt/errcore.h>
41#include <iprt/file.h>
42#include <iprt/getopt.h>
43#include <iprt/initterm.h>
44#include <iprt/mem.h>
45#include <iprt/message.h>
46#include <iprt/process.h>
47#include <iprt/string.h>
48#include <iprt/stream.h>
49#include <iprt/utf16.h>
50#include <iprt/zero.h>
51
52#ifdef RT_OS_DARWIN
53/** @todo */
54#elif defined(RT_OS_WINDOWS)
55# include <iprt/nt/nt-and-windows.h>
56#elif !defined(RT_OS_OS2)
57# include <X11/Xlib.h>
58# include <X11/Xatom.h>
59#endif
60
61
62/*********************************************************************************************************************************
63* Defined Constants And Macros *
64*********************************************************************************************************************************/
65#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) || defined(RT_OS_DARWIN)
66# undef MULTI_TARGET_CLIPBOARD
67# undef CU_X11
68#else
69# define MULTI_TARGET_CLIPBOARD
70# define CU_X11
71#endif
72
73
74/*********************************************************************************************************************************
75* Structures and Typedefs *
76*********************************************************************************************************************************/
77/**
78 * Clipboard format descriptor.
79 */
80typedef struct CLIPUTILFORMAT
81{
82 /** Format name. */
83 const char *pszName;
84
85#if defined(RT_OS_WINDOWS)
86 /** Windows integer format (CF_XXXX). */
87 UINT fFormat;
88 /** Windows string format name. */
89 const WCHAR *pwszFormat;
90
91#elif defined(RT_OS_OS2)
92 /** OS/2 integer format. */
93 ULONG fFormat;
94 /** OS/2 string format name. */
95 const char *pszFormat;
96
97#elif defined(RT_OS_DARWIN)
98 /** Native format (flavor). */
99 CFStringRef *hStrFormat;
100#else
101 /** The X11 atom for the format. */
102 Atom uAtom;
103 /** The X11 atom name if uAtom must be termined dynamically. */
104 const char *pszAtomName;
105 /** @todo X11 */
106#endif
107
108 /** Description. */
109 const char *pszDesc;
110 /** CLIPUTILFORMAT_F_XXX. */
111 uint32_t fFlags;
112} CLIPUTILFORMAT;
113/** Pointer to a clipobard format descriptor. */
114typedef CLIPUTILFORMAT const *PCCLIPUTILFORMAT;
115
116/** Convert to/from UTF-8. */
117#define CLIPUTILFORMAT_F_CONVERT_UTF8 RT_BIT_32(0)
118/** Ad hoc entry. */
119#define CLIPUTILFORMAT_F_AD_HOC RT_BIT_32(1)
120
121
122#ifdef MULTI_TARGET_CLIPBOARD
123/**
124 * Clipboard target descriptor.
125 */
126typedef struct CLIPUTILTARGET
127{
128 /** Target name. */
129 const char *pszName;
130 /** The X11 atom for the target. */
131 Atom uAtom;
132 /** The X11 atom name if uAtom must be termined dynamically. */
133 const char *pszAtomName;
134 /** Description. */
135 const char *pszDesc;
136} CLIPUTILTARGET;
137/** Pointer to clipboard target descriptor. */
138typedef CLIPUTILTARGET const *PCCLIPUTILTARGET;
139#endif /* MULTI_TARGET_CLIPBOARD */
140
141
142#ifdef RT_OS_OS2
143/** Header for Odin32 specific clipboard entries.
144 * (Used to get the correct size of the data.)
145 */
146typedef struct _Odin32ClipboardHeader
147{
148 /** Magic (CLIPHEADER_MAGIC) */
149 char achMagic[8];
150 /** Size of the following data.
151 * (The interpretation depends on the type.) */
152 unsigned cbData;
153 /** Odin32 format number. */
154 unsigned uFormat;
155} CLIPHEADER, *PCLIPHEADER;
156
157#define CLIPHEADER_MAGIC "Odin\1\0\1"
158#endif
159
160
161/*********************************************************************************************************************************
162* Global Variables *
163*********************************************************************************************************************************/
164/** Command line parameters */
165static const RTGETOPTDEF g_aCmdOptions[] =
166{
167 { "--list", 'l', RTGETOPT_REQ_NOTHING },
168 { "--get", 'g', RTGETOPT_REQ_STRING },
169 { "--get-file", 'G', RTGETOPT_REQ_STRING },
170 { "--put", 'p', RTGETOPT_REQ_STRING },
171 { "--put-file", 'P', RTGETOPT_REQ_STRING },
172 { "--check", 'c', RTGETOPT_REQ_STRING },
173 { "--check-file", 'C', RTGETOPT_REQ_STRING },
174 { "--check-not", 'n', RTGETOPT_REQ_STRING },
175 { "--zap", 'z', RTGETOPT_REQ_NOTHING },
176#ifdef MULTI_TARGET_CLIPBOARD
177 { "--target", 't', RTGETOPT_REQ_STRING },
178#endif
179 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
180 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
181 { "--version", 'V', RTGETOPT_REQ_NOTHING },
182 { "--help", 'h', RTGETOPT_REQ_NOTHING }, /* for Usage() */
183};
184
185/** Format descriptors. */
186static CLIPUTILFORMAT g_aFormats[] =
187{
188#if defined(RT_OS_WINDOWS)
189 { "text/ansi", CF_TEXT, NULL, "ANSI text", 0 },
190 { "text/utf-16", CF_UNICODETEXT, NULL, "UTF-16 text", 0 },
191 { "text/utf-8", CF_UNICODETEXT, NULL, "UTF-8 text", CLIPUTILFORMAT_F_CONVERT_UTF8 },
192 /* https://docs.microsoft.com/en-us/windows/desktop/dataxchg/html-clipboard-format */
193 { "text/html", 0, L"HTML Format", "HTML text", 0 },
194 { "bitmap", CF_DIB, NULL, "Bitmap (DIB)", 0 },
195 { "bitmap/v5", CF_DIBV5, NULL, "Bitmap version 5 (DIBv5)", 0 },
196#elif defined(RT_OS_OS2)
197 { "text/ascii", CF_TEXT, NULL, "ASCII text", 0 },
198 { "text/utf-8", CF_TEXT, NULL, "UTF-8 text", CLIPUTILFORMAT_F_CONVERT_UTF8 },
199 { "text/utf-16", 0, "Odin32 UnicodeText", "UTF-16 text", 0},
200#elif defined(RT_OS_DARWIN)
201 { "text/utf-8", kUTTypeUTF8PlainText, "UTF-8 text", 0 },
202 { "text/utf-16", kUTTypeUTF16PlainText, "UTF-16 text", 0 },
203#else
204 /** @todo X11 */
205 { "text/utf-8", None, "UTF8_STRING", "UTF-8 text", 0 },
206#endif
207};
208
209#ifdef MULTI_TARGET_CLIPBOARD
210/** Target descriptors. */
211static CLIPUTILTARGET g_aTargets[] =
212{
213 { "clipboard", 0, "CLIPBOARD", "XA_CLIPBOARD: The clipboard (default)" },
214 { "primary", XA_PRIMARY, NULL, "XA_PRIMARY: Primary selected text (middle mouse button)" },
215 { "secondary", XA_SECONDARY, NULL, "XA_SECONDARY: Secondary selected text (with ctrl)" },
216};
217
218/** The current clipboard target. */
219static CLIPUTILTARGET *g_pTarget = &g_aTargets[0];
220#endif /* MULTI_TARGET_CLIPBOARD */
221
222/** The -v/-q state. */
223static unsigned g_uVerbosity = 1;
224
225#ifdef RT_OS_DARWIN
226
227#elif defined(RT_OS_OS2)
228/** Anchorblock handle. */
229static HAB g_hOs2Hab = NULLHANDLE;
230/** The message queue handle. */
231static HMQ g_hOs2MsgQueue = NULLHANDLE;
232/** Windows that becomes clipboard owner when setting data. */
233static HWND g_hOs2Wnd = NULLHANDLE;
234/** Set if we've opened the clipboard. */
235static bool g_fOs2OpenedClipboard = false;
236/** Set if we're the clipboard owner. */
237static bool g_fOs2ClipboardOwner = false;
238
239#elif defined(RT_OS_WINDOWS)
240/** Set if we've opened the clipboard. */
241static bool g_fOpenedClipboard = false;
242
243#else
244/** Number of errors (incremented by error handle callback). */
245static uint32_t volatile g_cX11Errors;
246/** The X11 display. */
247static Display *g_pX11Display = NULL;
248/** The X11 dummy window. */
249static Window g_hX11Window = 0;
250/** TARGETS */
251static Atom g_uX11AtomTargets;
252/** MULTIPLE */
253static Atom g_uX11AtomMultiple;
254
255#endif
256
257
258/**
259 * Gets a format descriptor, complaining if invalid format.
260 *
261 * @returns Pointer to the descriptor if found, NULL + msg if not.
262 * @param pszFormat The format to get.
263 */
264static PCCLIPUTILFORMAT GetFormatDesc(const char *pszFormat)
265{
266 for (size_t i = 0; i < RT_ELEMENTS(g_aFormats); i++)
267 if (strcmp(pszFormat, g_aFormats[i].pszName) == 0)
268 {
269#if defined(RT_OS_DARWIN)
270 /** @todo */
271
272#elif defined(RT_OS_OS2)
273 if (g_aFormats[i].pszFormat && g_aFormats[i].fFormat == 0)
274 {
275 g_aFormats[i].fFormat = WinAddAtom(WinQuerySystemAtomTable(), g_aFormats[i].pszFormat);
276 if (g_aFormats[i].fFormat == 0)
277 RTMsgError("WinAddAtom(,%s) failed: %#x", g_aFormats[i].pszFormat, WinGetLastError(g_hOs2Hab));
278 }
279
280#elif defined(RT_OS_WINDOWS)
281 if (g_aFormats[i].pwszFormat && g_aFormats[i].fFormat == 0)
282 {
283 g_aFormats[i].fFormat = RegisterClipboardFormatW(g_aFormats[i].pwszFormat);
284 if (g_aFormats[i].fFormat == 0)
285 RTMsgError("RegisterClipboardFormatW(%ls) failed: %u (%#x)",
286 g_aFormats[i].pwszFormat, GetLastError(), GetLastError());
287 }
288#elif defined(CU_X11)
289 if (g_aFormats[i].pszAtomName && g_aFormats[i].uAtom == 0)
290 g_aFormats[i].uAtom = XInternAtom(g_pX11Display, g_aFormats[i].pszAtomName, False);
291#endif
292 return &g_aFormats[i];
293 }
294
295 /*
296 * Try register the format.
297 */
298 static CLIPUTILFORMAT AdHoc;
299 AdHoc.pszName = pszFormat;
300 AdHoc.pszDesc = pszFormat;
301 AdHoc.fFlags = CLIPUTILFORMAT_F_AD_HOC;
302#ifdef RT_OS_DARWIN
303/** @todo */
304
305#elif defined(RT_OS_OS2)
306 AdHoc.pszFormat = pszFormat;
307 AdHoc.fFormat = WinAddAtom(WinQuerySystemAtomTable(), pszFormat);
308 if (AdHoc.fFormat == 0)
309 {
310 RTMsgError("Invalid format '%s' (%#x)", pszFormat, WinGetLastError(g_hOs2Hab));
311 return NULL;
312 }
313
314#elif defined(RT_OS_WINDOWS)
315 AdHoc.pwszFormat = NULL;
316 AdHoc.fFormat = RegisterClipboardFormatA(pszFormat);
317 if (AdHoc.fFormat == 0)
318 {
319 RTMsgError("RegisterClipboardFormatA(%s) failed: %u (%#x)", pszFormat, GetLastError(), GetLastError());
320 return NULL;
321 }
322
323#else
324 AdHoc.pszAtomName = pszFormat;
325 AdHoc.uAtom = XInternAtom(g_pX11Display, pszFormat, False);
326 if (AdHoc.uAtom == None)
327 {
328 RTMsgError("Invalid format '%s' or out of memory for X11 atoms", pszFormat);
329 return NULL;
330 }
331
332#endif
333 return &AdHoc;
334}
335
336
337#ifdef RT_OS_DARWIN
338
339/** @todo */
340
341
342#elif defined(RT_OS_OS2)
343
344/**
345 * The window procedure for the object window.
346 *
347 * @returns Message result.
348 *
349 * @param hwnd The window handle.
350 * @param msg The message.
351 * @param mp1 Message parameter 1.
352 * @param mp2 Message parameter 2.
353 */
354static MRESULT EXPENTRY CuOs2WinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
355{
356 if (g_uVerbosity > 2)
357 RTMsgInfo("CuOs2WinProc: hwnd=%#lx msg=%#lx mp1=%#lx mp2=%#lx\n", hwnd, msg, mp1, mp2);
358
359 switch (msg)
360 {
361 case WM_CREATE:
362 return NULL; /* FALSE(/NULL) == Continue*/
363 case WM_DESTROY:
364 break;
365
366 /*
367 * Clipboard viewer message - the content has been changed.
368 * This is sent *after* releasing the clipboard sem
369 * and during the WinSetClipbrdViewer call.
370 */
371 case WM_DRAWCLIPBOARD:
372 break;
373
374 /*
375 * Clipboard owner message - the content was replaced.
376 * This is sent by someone with an open clipboard, so don't try open it now.
377 */
378 case WM_DESTROYCLIPBOARD:
379 break;
380
381 /*
382 * Clipboard owner message - somebody is requesting us to render a format.
383 * This is called by someone which owns the clipboard, but that's fine.
384 */
385 case WM_RENDERFMT:
386 break;
387
388 /*
389 * Clipboard owner message - we're about to quit and should render all formats.
390 */
391 case WM_RENDERALLFMTS:
392 break;
393
394 /*
395 * Clipboard owner messages dealing with owner drawn content.
396 * We shouldn't be seeing any of these.
397 */
398 case WM_PAINTCLIPBOARD:
399 case WM_SIZECLIPBOARD:
400 case WM_HSCROLLCLIPBOARD:
401 case WM_VSCROLLCLIPBOARD:
402 AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
403 break;
404
405 /*
406 * We shouldn't be seeing any other messages according to the docs.
407 * But for whatever reason, PM sends us a WM_ADJUSTWINDOWPOS message
408 * during WinCreateWindow. So, ignore that and assert on anything else.
409 */
410 default:
411 AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
412 case WM_ADJUSTWINDOWPOS:
413 break;
414 }
415 return NULL;
416}
417
418/**
419 * Initialize the OS/2 bits.
420 */
421static RTEXITCODE CuOs2Init(void)
422{
423 g_hOs2Hab = WinInitialize(0);
424 if (g_hOs2Hab == NULLHANDLE)
425 return RTMsgErrorExitFailure("WinInitialize failed!");
426
427 g_hOs2MsgQueue = WinCreateMsgQueue(g_hOs2Hab, 10);
428 if (g_hOs2MsgQueue == NULLHANDLE)
429 return RTMsgErrorExitFailure("WinCreateMsgQueue failed: %#x", WinGetLastError(g_hOs2Hab));
430
431 static char s_szClass[] = "VBox-ClipUtilClipboardClass";
432 if (!WinRegisterClass(g_hOs2Wnd, (PCSZ)s_szClass, CuOs2WinProc, 0, 0))
433 return RTMsgErrorExitFailure("WinRegisterClass failed: %#x", WinGetLastError(g_hOs2Hab));
434
435 g_hOs2Wnd = WinCreateWindow(HWND_OBJECT, /* hwndParent */
436 (PCSZ)s_szClass, /* pszClass */
437 (PCSZ)"VirtualBox Clipboard Utility", /* pszName */
438 0, /* flStyle */
439 0, 0, 0, 0, /* x, y, cx, cy */
440 NULLHANDLE, /* hwndOwner */
441 HWND_BOTTOM, /* hwndInsertBehind */
442 42, /* id */
443 NULL, /* pCtlData */
444 NULL); /* pPresParams */
445 if (g_hOs2Wnd == NULLHANDLE)
446 return RTMsgErrorExitFailure("WinCreateWindow failed: %#x", WinGetLastError(g_hOs2Hab));
447
448 return RTEXITCODE_SUCCESS;
449}
450
451
452/**
453 * Terminates the OS/2 bits.
454 */
455static RTEXITCODE CuOs2Term(void)
456{
457 if (g_fOs2OpenedClipboard)
458 {
459 if (!WinCloseClipbrd(g_hOs2Hab))
460 return RTMsgErrorExitFailure("WinCloseClipbrd failed: %#x", WinGetLastError(g_hOs2Hab));
461 g_fOs2OpenedClipboard = false;
462 }
463
464 WinDestroyWindow(g_hOs2Wnd);
465 g_hOs2Wnd = NULLHANDLE;
466
467 WinDestroyMsgQueue(g_hOs2MsgQueue);
468 g_hOs2MsgQueue = NULLHANDLE;
469
470 WinTerminate(g_hOs2Hab);
471 g_hOs2Hab = NULLHANDLE;
472
473 return RTEXITCODE_SUCCESS;
474}
475
476
477/**
478 * Opens the OS/2 clipboard.
479 */
480static RTEXITCODE CuOs2OpenClipboardIfNecessary(void)
481{
482 if (g_fOs2OpenedClipboard)
483 return RTEXITCODE_SUCCESS;
484 if (WinOpenClipbrd(g_hOs2Hab))
485 {
486 g_fOs2OpenedClipboard = true;
487 return RTEXITCODE_SUCCESS;
488 }
489 return RTMsgErrorExitFailure("WinOpenClipbrd failed: %#x", WinGetLastError(g_hOs2Hab));
490}
491
492
493#elif defined(RT_OS_WINDOWS)
494
495/**
496 * Terminates the Windows bits.
497 */
498static RTEXITCODE CuWinTerm(void)
499{
500 if (g_fOpenedClipboard)
501 {
502 if (!CloseClipboard())
503 return RTMsgErrorExitFailure("CloseClipboard failed: %u (%#x)", GetLastError(), GetLastError());
504 g_fOpenedClipboard = false;
505 }
506 return RTEXITCODE_SUCCESS;
507}
508
509
510/**
511 * Opens the window clipboard.
512 */
513static RTEXITCODE WinOpenClipboardIfNecessary(void)
514{
515 if (g_fOpenedClipboard)
516 return RTEXITCODE_SUCCESS;
517 if (OpenClipboard(NULL))
518 {
519 g_fOpenedClipboard = true;
520 return RTEXITCODE_SUCCESS;
521 }
522 return RTMsgErrorExitFailure("OpenClipboard failed: %u (%#x)", GetLastError(), GetLastError());
523}
524
525
526#else /* X11: */
527
528/**
529 * Error handler callback.
530 */
531static int CuX11ErrorCallback(Display *pX11Display, XErrorEvent *pErrEvt)
532{
533 g_cX11Errors++;
534 char szErr[2048];
535 XGetErrorText(pX11Display, pErrEvt->error_code, szErr, sizeof(szErr));
536 RTMsgError("An X Window protocol error occurred: %s\n"
537 " Request code: %u\n"
538 " Minor code: %u\n"
539 " Serial number of the failed request: %u\n",
540 szErr, pErrEvt->request_code, pErrEvt->minor_code, pErrEvt->serial);
541 return 0;
542}
543
544
545/**
546 * Initialize the X11 bits.
547 */
548static RTEXITCODE CuX11Init(void)
549{
550 /*
551 * Open the X11 display and create a little dummy window.
552 */
553 XSetErrorHandler(CuX11ErrorCallback);
554 g_pX11Display = XOpenDisplay(NULL);
555 if (!g_pX11Display)
556 return RTMsgErrorExitFailure("XOpenDisplay failed");
557
558 int const iDefaultScreen = DefaultScreen(g_pX11Display);
559 g_hX11Window = XCreateSimpleWindow(g_pX11Display,
560 RootWindow(g_pX11Display, iDefaultScreen),
561 0 /*x*/, 0 /*y*/,
562 1 /*cx*/, 1 /*cy*/,
563 0 /*cPxlBorder*/,
564 BlackPixel(g_pX11Display, iDefaultScreen) /*Border*/,
565 WhitePixel(g_pX11Display, iDefaultScreen) /*Background*/);
566
567 /*
568 * Resolve any unknown atom values we might need later.
569 */
570 for (size_t i = 0; i < RT_ELEMENTS(g_aTargets); i++)
571 if (g_aTargets[i].pszAtomName)
572 {
573 g_aTargets[i].uAtom = XInternAtom(g_pX11Display, g_aTargets[i].pszAtomName, False);
574 if (g_uVerbosity > 2)
575 RTPrintf("target %s atom=%#x\n", g_aTargets[i].pszName, g_aTargets[i].uAtom);
576 }
577
578 g_uX11AtomTargets = XInternAtom(g_pX11Display, "TARGETS", False);
579 g_uX11AtomMultiple = XInternAtom(g_pX11Display, "MULTIPLE", False);
580
581 return RTEXITCODE_SUCCESS;
582}
583
584#endif /* X11 */
585
586
587/**
588 * Lists the clipboard format.
589 */
590static RTEXITCODE ListClipboardContent(void)
591{
592#if defined(RT_OS_OS2)
593 RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
594 if (rcExit == RTEXITCODE_SUCCESS)
595 {
596 HATOMTBL const hAtomTbl = WinQuerySystemAtomTable();
597 uint32_t idx = 0;
598 ULONG fFormat = 0;
599 while ((fFormat = WinEnumClipbrdFmts(g_hOs2Hab)) != 0)
600 {
601 char szName[256] = {0};
602 ULONG cchRet = WinQueryAtomName(hAtomTbl, fFormat, szName, sizeof(szName));
603 if (cchRet != 0)
604 RTPrintf("#%02u: %#06x - %s\n", idx, fFormat, szName);
605 else
606 {
607 const char *pszName = NULL;
608 switch (fFormat)
609 {
610 case CF_TEXT: pszName = "CF_TEXT"; break;
611 case CF_BITMAP: pszName = "CF_BITMAP"; break;
612 case CF_DSPTEXT: pszName = "CF_DSPTEXT"; break;
613 case CF_DSPBITMAP: pszName = "CF_DSPBITMAP"; break;
614 case CF_METAFILE: pszName = "CF_METAFILE"; break;
615 case CF_DSPMETAFILE: pszName = "CF_DSPMETAFILE"; break;
616 case CF_PALETTE: pszName = "CF_PALETTE"; break;
617 default:
618 break;
619 }
620 if (pszName)
621 RTPrintf("#%02u: %#06x - %s\n", idx, fFormat, pszName);
622 else
623 RTPrintf("#%02u: %#06x\n", idx, fFormat);
624 }
625
626 idx++;
627 }
628 }
629
630 return rcExit;
631
632#elif defined(RT_OS_WINDOWS)
633 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
634 if (rcExit == RTEXITCODE_SUCCESS)
635 {
636 SetLastError(0);
637 uint32_t idx = 0;
638 UINT fFormat = 0;
639 while ((fFormat = EnumClipboardFormats(fFormat)) != 0)
640 {
641 WCHAR wszName[256];
642 int cchName = GetClipboardFormatNameW(fFormat, wszName, RT_ELEMENTS(wszName));
643 if (cchName > 0)
644 RTPrintf("#%02u: %#06x - %ls\n", idx, fFormat, wszName);
645 else
646 {
647 const char *pszName = NULL;
648 switch (fFormat)
649 {
650 case CF_TEXT: pszName = "CF_TEXT"; break;
651 case CF_BITMAP: pszName = "CF_BITMAP"; break;
652 case CF_METAFILEPICT: pszName = "CF_METAFILEPICT"; break;
653 case CF_SYLK: pszName = "CF_SYLK"; break;
654 case CF_DIF: pszName = "CF_DIF"; break;
655 case CF_TIFF: pszName = "CF_TIFF"; break;
656 case CF_OEMTEXT: pszName = "CF_OEMTEXT"; break;
657 case CF_DIB: pszName = "CF_DIB"; break;
658 case CF_PALETTE: pszName = "CF_PALETTE"; break;
659 case CF_PENDATA: pszName = "CF_PENDATA"; break;
660 case CF_RIFF: pszName = "CF_RIFF"; break;
661 case CF_WAVE: pszName = "CF_WAVE"; break;
662 case CF_UNICODETEXT: pszName = "CF_UNICODETEXT"; break;
663 case CF_ENHMETAFILE: pszName = "CF_ENHMETAFILE"; break;
664 case CF_HDROP: pszName = "CF_HDROP"; break;
665 case CF_LOCALE: pszName = "CF_LOCALE"; break;
666 case CF_DIBV5: pszName = "CF_DIBV5"; break;
667 default:
668 break;
669 }
670 if (pszName)
671 RTPrintf("#%02u: %#06x - %s\n", idx, fFormat, pszName);
672 else
673 RTPrintf("#%02u: %#06x\n", idx, fFormat);
674 }
675
676 idx++;
677 }
678 if (idx == 0)
679 RTPrintf("Empty\n");
680 }
681 return rcExit;
682
683#elif defined(CU_X11)
684 /* Request the TARGETS property: */
685 Atom uAtomDst = g_uX11AtomTargets;
686 int rc = XConvertSelection(g_pX11Display, g_pTarget->uAtom, g_uX11AtomTargets, uAtomDst, g_hX11Window, CurrentTime);
687 if (g_uVerbosity > 1)
688 RTPrintf("XConvertSelection -> %d\n", rc);
689
690 /* Wait for the reply: */
691 for (;;)
692 {
693 XEvent Evt = {0};
694 rc = XNextEvent(g_pX11Display, &Evt);
695 if (Evt.type == SelectionNotify)
696 {
697 if (g_uVerbosity > 1)
698 RTPrintf("XNextEvent -> %d; type=SelectionNotify\n", rc);
699 if (Evt.xselection.selection == g_pTarget->uAtom)
700 {
701 if (Evt.xselection.property == None)
702 return RTMsgErrorExitFailure("XConvertSelection(,%s,TARGETS,) failed", g_pTarget->pszName);
703
704 /* Get the TARGETS property data: */
705 Atom uAtomRetType = 0;
706 int iActualFmt = 0;
707 unsigned long cbLeftToRead = 0;
708 unsigned long cItems = 0;
709 unsigned char *pbData = NULL;
710 rc = XGetWindowProperty(g_pX11Display, g_hX11Window, uAtomDst,
711 0 /*offset*/, sizeof(Atom) * 4096 /* should be enough */, True /*fDelete*/, XA_ATOM,
712 &uAtomRetType, &iActualFmt, &cItems, &cbLeftToRead, &pbData);
713 if (g_uVerbosity > 1)
714 RTPrintf("XConvertSelection -> %d; uAtomRetType=%u iActualFmt=%d cItems=%lu cbLeftToRead=%lu pbData=%p\n",
715 rc, uAtomRetType, iActualFmt, cItems, cbLeftToRead, pbData);
716 if (pbData && cItems > 0)
717 {
718 /* Display the TARGETS: */
719 Atom const *paTargets = (Atom const *)pbData;
720 for (unsigned long i = 0; i < cItems; i++)
721 {
722 const char *pszName = XGetAtomName(g_pX11Display, paTargets[i]);
723 if (pszName)
724 RTPrintf("#%02u: %#06x - %s\n", i, paTargets[i], pszName);
725 else
726 RTPrintf("#%02u: %#06x\n", i, paTargets[i]);
727 }
728 }
729 else
730 RTMsgInfo("Empty");
731 if (pbData)
732 XFree(pbData);
733 return RTEXITCODE_SUCCESS;
734 }
735 }
736 else if (g_uVerbosity > 1)
737 RTPrintf("XNextEvent -> %d; type=%d\n", rc, Evt.type);
738 }
739
740#else
741 return RTMsgErrorExitFailure("ListClipboardContent is not implemented");
742#endif
743}
744
745
746/**
747 * Reads the given clipboard format and stores it in on the heap.
748 *
749 * @returns Success indicator.
750 * @param pFmtDesc The format to get.
751 * @param ppvData Where to return the pointer to the data. Free using
752 * RTMemFree when done.
753 * @param pcbData Where to return the amount of data returned.
754 */
755static RTEXITCODE ReadClipboardData(PCCLIPUTILFORMAT pFmtDesc, void **ppvData, size_t *pcbData)
756{
757 *ppvData = NULL;
758 *pcbData = 0;
759
760#if defined(RT_OS_OS2)
761 RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
762 if (rcExit == RTEXITCODE_SUCCESS)
763 {
764 ULONG fFmtInfo = 0;
765 if (WinQueryClipbrdFmtInfo(g_hOs2Hab, pFmtDesc->fFormat, &fFmtInfo))
766 {
767 ULONG uData = WinQueryClipbrdData(g_hOs2Hab, pFmtDesc->fFormat);
768 if (fFmtInfo & CFI_POINTER)
769 {
770 PCLIPHEADER pOdinHdr = (PCLIPHEADER)uData;
771 if (pFmtDesc->fFormat == CF_TEXT)
772 {
773 if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
774 {
775 char *pszUtf8 = NULL;
776 int rc = RTStrCurrentCPToUtf8(&pszUtf8, (const char *)uData);
777 if (RT_SUCCESS(rc))
778 {
779 *pcbData = strlen(pszUtf8) + 1;
780 *ppvData = RTMemDup(pszUtf8, *pcbData);
781 RTStrFree(pszUtf8);
782 }
783 else
784 return RTMsgErrorExitFailure("RTStrCurrentCPToUtf8 failed: %Rrc", rc);
785 }
786 else
787 {
788 *pcbData = strlen((const char *)uData) + 1;
789 *ppvData = RTMemDup((const char *)uData, *pcbData);
790 }
791 }
792 else if ( strcmp(pFmtDesc->pszFormat, "Odin32 UnicodeText") == 0
793 && memcmp(pOdinHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pOdinHdr->achMagic)) == 0)
794 {
795 *pcbData = pOdinHdr->cbData;
796 *ppvData = RTMemDup(pOdinHdr + 1, pOdinHdr->cbData);
797 }
798 else
799 {
800 /* We could use DosQueryMem here to figure out the size of the allocation... */
801 *pcbData = PAGE_SIZE - (uData & PAGE_OFFSET_MASK);
802 *ppvData = RTMemDup((void const *)uData, *pcbData);
803 }
804 }
805 else
806 {
807 *pcbData = sizeof(uData);
808 *ppvData = RTMemDup(&uData, sizeof(uData));
809 }
810 if (!*ppvData)
811 rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", *pcbData);
812 }
813 else
814 rcExit = RTMsgErrorExitFailure("WinQueryClipbrdFmtInfo(,%s,) failed: %#x\n",
815 pFmtDesc->pszName, WinGetLastError(g_hOs2Hab));
816 }
817 return rcExit;
818
819#elif defined(RT_OS_WINDOWS)
820 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
821 if (rcExit == RTEXITCODE_SUCCESS)
822 {
823 HANDLE hData = GetClipboardData(pFmtDesc->fFormat);
824 if (hData != NULL)
825 {
826 SIZE_T const cbData = GlobalSize(hData);
827 PVOID const pvData = GlobalLock(hData);
828 if (pvData != NULL)
829 {
830 *pcbData = cbData;
831 if (cbData != 0)
832 {
833 if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
834 {
835 char *pszUtf8 = NULL;
836 size_t cchUtf8 = 0;
837 int rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvData, cbData / sizeof(RTUTF16), &pszUtf8, 0, &cchUtf8);
838 if (RT_SUCCESS(rc))
839 {
840 *pcbData = cchUtf8 + 1;
841 *ppvData = RTMemDup(pszUtf8, cchUtf8 + 1);
842 RTStrFree(pszUtf8);
843 if (!*ppvData)
844 rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", cbData);
845 }
846 else
847 rcExit = RTMsgErrorExitFailure("RTUtf16ToUtf8Ex failed: %Rrc", rc);
848 }
849 else
850 {
851 *ppvData = RTMemDup(pvData, cbData);
852 if (!*ppvData)
853 rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", cbData);
854 }
855 }
856 GlobalUnlock(hData);
857 }
858 else
859 rcExit = RTMsgErrorExitFailure("GetClipboardData(%s) failed: %u (%#x)\n",
860 pFmtDesc->pszName, GetLastError(), GetLastError());
861 }
862 else
863 rcExit = RTMsgErrorExitFailure("GetClipboardData(%s) failed: %u (%#x)\n",
864 pFmtDesc->pszName, GetLastError(), GetLastError());
865 }
866 return rcExit;
867
868#elif defined(CU_X11)
869
870 /* Request the data: */
871 Atom const uAtomDst = pFmtDesc->uAtom;
872 int rc = XConvertSelection(g_pX11Display, g_pTarget->uAtom, pFmtDesc->uAtom, uAtomDst, g_hX11Window, CurrentTime);
873 if (g_uVerbosity > 1)
874 RTPrintf("XConvertSelection -> %d\n", rc);
875
876 /* Wait for the reply: */
877 for (;;)
878 {
879 XEvent Evt = {0};
880 rc = XNextEvent(g_pX11Display, &Evt);
881 if (Evt.type == SelectionNotify)
882 {
883 if (g_uVerbosity > 1)
884 RTPrintf("XNextEvent -> %d; type=SelectionNotify\n", rc);
885 if (Evt.xselection.selection == g_pTarget->uAtom)
886 {
887 if (Evt.xselection.property == None)
888 return RTMsgErrorExitFailure("XConvertSelection(,%s,%s,) failed", g_pTarget->pszName, pFmtDesc->pszName);
889
890 /*
891 * Retrieve the data.
892 */
893 Atom uAtomRetType = 0;
894 int cBitsActualFmt = 0;
895 unsigned long cbLeftToRead = 0;
896 unsigned long cItems = 0;
897 unsigned char *pbData = NULL;
898 rc = XGetWindowProperty(g_pX11Display, g_hX11Window, uAtomDst,
899 0 /*offset*/, _64M, False/*fDelete*/, AnyPropertyType,
900 &uAtomRetType, &cBitsActualFmt, &cItems, &cbLeftToRead, &pbData);
901 if (g_uVerbosity > 1)
902 RTPrintf("XConvertSelection -> %d; uAtomRetType=%u cBitsActualFmt=%d cItems=%lu cbLeftToRead=%lu pbData=%p\n",
903 rc, uAtomRetType, cBitsActualFmt, cItems, cbLeftToRead, pbData);
904 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
905 if (pbData && cItems > 0)
906 {
907 *pcbData = cItems * (cBitsActualFmt / 8);
908 *ppvData = RTMemDup(pbData, *pcbData);
909 if (!*ppvData)
910 rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", *pcbData);
911 }
912 if (pbData)
913 XFree(pbData);
914 XDeleteProperty(g_pX11Display, g_hX11Window, uAtomDst);
915 return rcExit;
916 }
917 }
918 else if (g_uVerbosity > 1)
919 RTPrintf("XNextEvent -> %d; type=%d\n", rc, Evt.type);
920 }
921
922#else
923 RT_NOREF(pFmtDesc);
924 return RTMsgErrorExitFailure("ReadClipboardData is not implemented\n");
925#endif
926}
927
928
929/**
930 * Puts the given data and format on the clipboard.
931 *
932 * @returns Success indicator.
933 * @param pFmtDesc The format.
934 * @param pvData The data.
935 * @param cbData The amount of data in bytes.
936 */
937static RTEXITCODE WriteClipboardData(PCCLIPUTILFORMAT pFmtDesc, void const *pvData, size_t cbData)
938{
939#if defined(RT_OS_OS2)
940 RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
941 if (rcExit == RTEXITCODE_SUCCESS)
942 {
943 /** @todo do we need to become owner? */
944
945 /* Convert to local code page if needed: */
946 char *pszLocale = NULL;
947 if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
948 {
949 int rc = RTStrUtf8ToCurrentCPEx(&pszLocale, (char *)pvData, cbData);
950 if (RT_SUCCESS(rc))
951 {
952 pvData = pszLocale;
953 cbData = strlen(pszLocale) + 1;
954 }
955 else
956 return RTMsgErrorExitFailure("RTStrUtf8ToCurrentCPEx failed: %Rrc\n", rc);
957 }
958
959 /* Allocate a bunch of shared memory for the object. */
960 PVOID pvShared = NULL;
961 APIRET orc = DosAllocSharedMem(&pvShared, NULL, cbData,
962 OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
963 if (orc == NO_ERROR)
964 {
965 memcpy(pvShared, pvData, cbData);
966
967 if (WinSetClipbrdData(g_hOs2Hab, (uintptr_t)pvShared, pFmtDesc->fFormat, CFI_POINTER))
968 rcExit = RTEXITCODE_SUCCESS;
969 else
970 {
971 rcExit = RTMsgErrorExitFailure("WinSetClipbrdData(,%p LB %#x,%s,) failed: %#x\n",
972 pvShared, cbData, pFmtDesc->pszName, WinGetLastError(g_hOs2Hab));
973 DosFreeMem(pvShared);
974 }
975 }
976 else
977 rcExit = RTMsgErrorExitFailure("DosAllocSharedMem(,, %#x,) -> %u", cbData, orc);
978 RTStrFree(pszLocale);
979 }
980 return rcExit;
981
982
983#elif defined(RT_OS_WINDOWS)
984 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
985 if (rcExit == RTEXITCODE_SUCCESS)
986 {
987 /*
988 * Do input data conversion.
989 */
990 PRTUTF16 pwszFree = NULL;
991 if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
992 {
993 size_t cwcConv = 0;
994 int rc = RTStrToUtf16Ex((char const *)pvData, cbData, &pwszFree, 0, &cwcConv);
995 if (RT_SUCCESS(rc))
996 {
997 pvData = pwszFree;
998 cbData = cwcConv * sizeof(RTUTF16);
999 }
1000 else
1001 return RTMsgErrorExitFailure("RTStrToTUtf16Ex failed: %Rrc\n", rc);
1002 }
1003
1004 /*
1005 * Text formats generally include the zero terminator.
1006 */
1007 uint32_t cbZeroPadding = 0;
1008 if (pFmtDesc->fFormat == CF_UNICODETEXT)
1009 cbZeroPadding = sizeof(WCHAR);
1010 else if (pFmtDesc->fFormat == CF_TEXT)
1011 cbZeroPadding = sizeof(char);
1012
1013 HANDLE hDstData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, cbData + cbZeroPadding);
1014 if (hDstData)
1015 {
1016 if (cbData)
1017 {
1018 PVOID pvDstData = GlobalLock(hDstData);
1019 if (pvDstData)
1020 memcpy(pvDstData, pvData, cbData);
1021 else
1022 rcExit = RTMsgErrorExitFailure("GlobalLock failed: %u (%#x)\n", GetLastError(), GetLastError());
1023 }
1024 if (rcExit == RTEXITCODE_SUCCESS)
1025 {
1026 if (!SetClipboardData(pFmtDesc->fFormat, hDstData))
1027 {
1028 rcExit = RTMsgErrorExitFailure("SetClipboardData(%s) failed: %u (%#x)\n",
1029 pFmtDesc->pszName, GetLastError(), GetLastError());
1030 GlobalFree(hDstData);
1031 }
1032 }
1033 else
1034 GlobalFree(hDstData);
1035 }
1036 else
1037 rcExit = RTMsgErrorExitFailure("GlobalAlloc(,%#zx) failed: %u (%#x)\n",
1038 cbData + cbZeroPadding, GetLastError(), GetLastError());
1039 }
1040 return rcExit;
1041
1042#else
1043 RT_NOREF(pFmtDesc, pvData, cbData);
1044 return RTMsgErrorExitFailure("WriteClipboardData is not implemented\n");
1045#endif
1046}
1047
1048
1049/**
1050 * Check if the given data + format matches what's actually on the clipboard.
1051 *
1052 * @returns Success indicator.
1053 * @param pFmtDesc The format to compare.
1054 * @param pvExpect The expected clipboard data.
1055 * @param cbExpect The size of the expected clipboard data.
1056 */
1057static RTEXITCODE CompareDataWithClipboard(PCCLIPUTILFORMAT pFmtDesc, void const *pvExpect, size_t cbExpect)
1058{
1059 void *pvData = NULL;
1060 size_t cbData = 0;
1061 RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
1062 if (rcExit == RTEXITCODE_SUCCESS)
1063 {
1064 if ( cbData == cbExpect
1065 && memcmp(pvData, pvExpect, cbData) == 0)
1066 rcExit = RTEXITCODE_SUCCESS;
1067 else
1068 rcExit = RTMsgErrorExitFailure("Mismatch for '%s' (cbData=%#zx cbExpect=%#zx)\n",
1069 pFmtDesc->pszName, cbData, cbExpect);
1070 RTMemFree(pvData);
1071 }
1072 return rcExit;
1073}
1074
1075
1076/**
1077 * Gets the given clipboard format.
1078 *
1079 * @returns Success indicator.
1080 * @param pFmtDesc The format to get.
1081 * @param pStrmOut Where to output the data.
1082 * @param fIsStdOut Set if @a pStrmOut is standard output, clear if not.
1083 */
1084static RTEXITCODE ClipboardContentToStdOut(PCCLIPUTILFORMAT pFmtDesc)
1085{
1086 void *pvData = NULL;
1087 size_t cbData = 0;
1088 RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
1089 if (rcExit == RTEXITCODE_SUCCESS)
1090 {
1091 int rc = RTStrmWrite(g_pStdOut, pvData, cbData);
1092 RTMemFree(pvData);
1093 if (RT_FAILURE(rc))
1094 rcExit = RTMsgErrorExitFailure("Error writing %#zx bytes to standard output: %Rrc", cbData, rc);
1095 }
1096 return rcExit;
1097}
1098
1099
1100/**
1101 * Gets the given clipboard format.
1102 *
1103 * @returns Success indicator.
1104 * @param pFmtDesc The format to get.
1105 * @param pszFilename The output filename.
1106 */
1107static RTEXITCODE ClipboardContentToFile(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
1108{
1109 void *pvData = NULL;
1110 size_t cbData = 0;
1111 RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
1112 if (rcExit == RTEXITCODE_SUCCESS)
1113 {
1114 RTFILE hFile = NIL_RTFILE;
1115 int rc = RTFileOpen(&hFile, pszFilename,
1116 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE
1117 | (0770 << RTFILE_O_CREATE_MODE_SHIFT));
1118 if (RT_SUCCESS(rc))
1119 {
1120 rc = RTFileWrite(hFile, pvData, cbData, NULL);
1121 int const rc2 = RTFileClose(hFile);
1122 if (RT_FAILURE(rc) || RT_FAILURE(rc2))
1123 {
1124 if (RT_FAILURE_NP(rc))
1125 RTMsgError("Writing %#z bytes to '%s' failed: %Rrc", cbData, pszFilename, rc);
1126 else
1127 RTMsgError("Closing '%s' failed: %Rrc", pszFilename, rc2);
1128 RTMsgInfo("Deleting '%s'.", pszFilename);
1129 RTFileDelete(pszFilename);
1130 rcExit = RTEXITCODE_FAILURE;
1131 }
1132 }
1133 else
1134 rcExit = RTMsgErrorExitFailure("Failed to open '%s' for writing: %Rrc", pszFilename, rc);
1135 RTMemFree(pvData);
1136 }
1137 return rcExit;
1138}
1139
1140
1141/**
1142 * Puts the given format + data onto the clipboard.
1143 *
1144 * @returns Success indicator.
1145 * @param pFmtDesc The format to put.
1146 * @param pszData The string data.
1147 */
1148static RTEXITCODE PutStringOnClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszData)
1149{
1150 return WriteClipboardData(pFmtDesc, pszData, strlen(pszData));
1151}
1152
1153
1154/**
1155 * Puts a format + file content onto the clipboard.
1156 *
1157 * @returns Success indicator.
1158 * @param pFmtDesc The format to put.
1159 * @param pszFilename The filename.
1160 */
1161static RTEXITCODE PutFileOnClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
1162{
1163 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1164 void *pvData = NULL;
1165 size_t cbData = 0;
1166 int rc = RTFileReadAll(pszFilename, &pvData, &cbData);
1167 if (RT_SUCCESS(rc))
1168 {
1169 rcExit = WriteClipboardData(pFmtDesc, pvData, cbData);
1170 RTFileReadAllFree(pvData, cbData);
1171 }
1172 else
1173 rcExit = RTMsgErrorExitFailure("Failed to open and read '%s' into memory: %Rrc", pszFilename, rc);
1174 return rcExit;
1175}
1176
1177
1178/**
1179 * Checks if the given format + data matches what's on the clipboard.
1180 *
1181 * @returns Success indicator.
1182 * @param pFmtDesc The format to check.
1183 * @param pszData The string data.
1184 */
1185static RTEXITCODE CheckStringAgainstClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszData)
1186{
1187 return CompareDataWithClipboard(pFmtDesc, pszData, strlen(pszData));
1188}
1189
1190
1191/**
1192 * Check if the given format + file content matches what's on the clipboard.
1193 *
1194 * @returns Success indicator.
1195 * @param pFmtDesc The format to check.
1196 * @param pszFilename The filename.
1197 */
1198static RTEXITCODE CheckFileAgainstClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
1199{
1200 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1201 void *pvData = NULL;
1202 size_t cbData = 0;
1203 int rc = RTFileReadAll(pszFilename, &pvData, &cbData);
1204 if (RT_SUCCESS(rc))
1205 {
1206 rcExit = CompareDataWithClipboard(pFmtDesc, pvData, cbData);
1207 RTFileReadAllFree(pvData, cbData);
1208 }
1209 else
1210 rcExit = RTMsgErrorExitFailure("Failed to open and read '%s' into memory: %Rrc", pszFilename, rc);
1211 return rcExit;
1212}
1213
1214
1215/**
1216 * Check that the given format is not on the clipboard.
1217 *
1218 * @returns Success indicator.
1219 * @param pFmtDesc The format to check.
1220 */
1221static RTEXITCODE CheckFormatNotOnClipboard(PCCLIPUTILFORMAT pFmtDesc)
1222{
1223#if defined(RT_OS_OS2)
1224 RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
1225 if (rcExit == RTEXITCODE_SUCCESS)
1226 {
1227 ULONG fFmtInfo = 0;
1228 if (WinQueryClipbrdFmtInfo(g_hOs2Hab, pFmtDesc->fFormat, &fFmtInfo))
1229 rcExit = RTMsgErrorExitFailure("Format '%s' is present");
1230 }
1231 return rcExit;
1232
1233#elif defined(RT_OS_WINDOWS)
1234 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
1235 if (rcExit == RTEXITCODE_SUCCESS)
1236 {
1237 if (IsClipboardFormatAvailable(pFmtDesc->fFormat))
1238 rcExit = RTMsgErrorExitFailure("Format '%s' is present");
1239 }
1240 return rcExit;
1241
1242#else
1243 RT_NOREF(pFmtDesc);
1244 return RTMsgErrorExitFailure("CheckFormatNotOnClipboard is not implemented");
1245#endif
1246}
1247
1248
1249/**
1250 * Empties the clipboard.
1251 *
1252 * @returns Success indicator.
1253 */
1254static RTEXITCODE ZapAllClipboardData(void)
1255{
1256#if defined(RT_OS_OS2)
1257 RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
1258 if (rcExit == RTEXITCODE_SUCCESS)
1259 {
1260 ULONG fFmtInfo = 0;
1261 if (WinEmptyClipbrd(g_hOs2Hab))
1262 {
1263 WinSetClipbrdOwner(g_hOs2Hab, g_hOs2Wnd); /* Probably unnecessary? */
1264 WinSetClipbrdOwner(g_hOs2Hab, NULLHANDLE);
1265 g_fOs2ClipboardOwner = false;
1266 }
1267 else
1268 rcExit = RTMsgErrorExitFailure("WinEmptyClipbrd() failed: %#x\n", WinGetLastError(g_hOs2Hab));
1269 }
1270 return rcExit;
1271
1272#elif defined(RT_OS_WINDOWS)
1273 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
1274 if (rcExit == RTEXITCODE_SUCCESS)
1275 {
1276 if (!EmptyClipboard())
1277 rcExit = RTMsgErrorExitFailure("EmptyClipboard() failed: %u (%#x)\n", GetLastError(), GetLastError());
1278 }
1279 return rcExit;
1280
1281#else
1282 return RTMsgErrorExitFailure("ZapAllClipboardData is not implemented");
1283#endif
1284}
1285
1286
1287/**
1288 * Display the usage to @a pStrm.
1289 */
1290static void Usage(PRTSTREAM pStrm)
1291{
1292 RTStrmPrintf(pStrm,
1293 "usage: %s [--get <fmt> [--get ...]] [--get-file <fmt> <file> [--get-file ...]]\n"
1294 " %s [--zap] [--put <fmt> <content> [--put ...]] [--put-file <fmt> <file> [--put-file ...]]\n"
1295 " %s [--check <fmt> <expected> [--check ...]] [--check-file <fmt> <file> [--check-file ...]]\n"
1296 " [--check-no <fmt> [--check-no ...]]\n"
1297 , RTProcShortName(), RTProcShortName(), RTProcShortName());
1298 RTStrmPrintf(pStrm, "\n");
1299 RTStrmPrintf(pStrm, "Options: \n");
1300
1301 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
1302 {
1303 const char *pszHelp;
1304 switch (g_aCmdOptions[i].iShort)
1305 {
1306 case 'l': pszHelp = "List the clipboard content."; break;
1307 case 'g': pszHelp = "Get given clipboard format and writes it to standard output."; break;
1308 case 'G': pszHelp = "Get given clipboard format and writes it to the specified file."; break;
1309 case 'p': pszHelp = "Puts given format and content on the clipboard."; break;
1310 case 'P': pszHelp = "Puts given format and file content on the clipboard."; break;
1311 case 'c': pszHelp = "Checks that the given format and content matches the clipboard."; break;
1312 case 'C': pszHelp = "Checks that the given format and file content matches the clipboard."; break;
1313 case 'n': pszHelp = "Checks that the given format is not on the clipboard."; break;
1314 case 'z': pszHelp = "Zaps the clipboard content."; break;
1315#ifdef MULTI_TARGET_CLIPBOARD
1316 case 't': pszHelp = "Selects the target clipboard."; break;
1317#endif
1318 case 'v': pszHelp = "More verbose execution."; break;
1319 case 'q': pszHelp = "Quiet execution."; break;
1320 case 'h': pszHelp = "Displays this help and exit"; break;
1321 case 'V': pszHelp = "Displays the program revision"; break;
1322
1323 default:
1324 pszHelp = "Option undocumented";
1325 break;
1326 }
1327 if ((unsigned)g_aCmdOptions[i].iShort < 127U)
1328 {
1329 char szOpt[64];
1330 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
1331 RTStrmPrintf(pStrm, " %-19s %s\n", szOpt, pszHelp);
1332 }
1333 else
1334 RTStrmPrintf(pStrm, " %-19s %s\n", g_aCmdOptions[i].pszLong, pszHelp);
1335 }
1336 RTStrmPrintf(pStrm, "Note! Options are processed in the order they are given.\n");
1337
1338 RTStrmPrintf(pStrm, "\nFormats:\n");
1339 for (size_t i = 0; i < RT_ELEMENTS(g_aFormats); i++)
1340 RTStrmPrintf(pStrm, " %-12s: %s\n", g_aFormats[i].pszName, g_aFormats[i].pszDesc);
1341
1342#ifdef MULTI_TARGET_CLIPBOARD
1343 RTStrmPrintf(pStrm, "\nTarget:\n");
1344 for (size_t i = 0; i < RT_ELEMENTS(g_aTargets); i++)
1345 RTStrmPrintf(pStrm, " %-12s: %s\n", g_aTargets[i].pszName, g_aTargets[i].pszDesc);
1346#endif
1347}
1348
1349
1350int main(int argc, char *argv[])
1351{
1352 /*
1353 * Init IPRT.
1354 */
1355 int rc = RTR3InitExe(argc, &argv, 0);
1356 if (RT_FAILURE(rc))
1357 return RTMsgInitFailure(rc);
1358
1359 /*
1360 * Host specific init.
1361 */
1362#ifdef RT_OS_DARWIN
1363 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1364#elif defined(RT_OS_OS2)
1365 RTEXITCODE rcExit = CuOs2Init();
1366#elif defined(RT_OS_WINDOWS)
1367 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1368#else
1369 RTEXITCODE rcExit = CuX11Init();
1370#endif
1371 if (rcExit != RTEXITCODE_SUCCESS)
1372 return rcExit;
1373
1374 /*
1375 * Process options (in order).
1376 */
1377 RTGETOPTUNION ValueUnion;
1378 RTGETOPTSTATE GetState;
1379 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
1380 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
1381 {
1382 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
1383 switch (rc)
1384 {
1385 case 'l':
1386 rcExit2 = ListClipboardContent();
1387 break;
1388
1389 case 'g':
1390 {
1391 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
1392 if (pFmtDesc)
1393 rcExit2 = ClipboardContentToStdOut(pFmtDesc);
1394 else
1395 rcExit2 = RTEXITCODE_FAILURE;
1396 break;
1397 }
1398
1399 case 'G':
1400 {
1401 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
1402 if (pFmtDesc)
1403 {
1404 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
1405 if (RT_SUCCESS(rc))
1406 rcExit2 = ClipboardContentToFile(pFmtDesc, ValueUnion.psz);
1407 else
1408 return RTMsgErrorExitFailure("No filename given with --get-file");
1409 }
1410 else
1411 rcExit2 = RTEXITCODE_FAILURE;
1412 break;
1413 }
1414
1415 case 'p':
1416 {
1417 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
1418 if (pFmtDesc)
1419 {
1420 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
1421 if (RT_SUCCESS(rc))
1422 rcExit2 = PutStringOnClipboard(pFmtDesc, ValueUnion.psz);
1423 else
1424 return RTMsgErrorExitFailure("No data string given with --put");
1425 }
1426 else
1427 rcExit2 = RTEXITCODE_FAILURE;
1428 break;
1429 }
1430
1431 case 'P':
1432 {
1433 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
1434 if (pFmtDesc)
1435 {
1436 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
1437 if (RT_SUCCESS(rc))
1438 rcExit2 = PutFileOnClipboard(pFmtDesc, ValueUnion.psz);
1439 else
1440 return RTMsgErrorExitFailure("No filename given with --put-file");
1441 }
1442 else
1443 rcExit2 = RTEXITCODE_FAILURE;
1444 break;
1445 }
1446
1447 case 'c':
1448 {
1449 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
1450 if (pFmtDesc)
1451 {
1452 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
1453 if (RT_SUCCESS(rc))
1454 rcExit2 = CheckStringAgainstClipboard(pFmtDesc, ValueUnion.psz);
1455 else
1456 return RTMsgErrorExitFailure("No data string given with --check");
1457 }
1458 else
1459 rcExit2 = RTEXITCODE_FAILURE;
1460 break;
1461 }
1462
1463 case 'C':
1464 {
1465 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
1466 if (pFmtDesc)
1467 {
1468 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
1469 if (RT_SUCCESS(rc))
1470 rcExit2 = CheckFileAgainstClipboard(pFmtDesc, ValueUnion.psz);
1471 else
1472 return RTMsgErrorExitFailure("No filename given with --check-file");
1473 }
1474 else
1475 rcExit2 = RTEXITCODE_FAILURE;
1476 break;
1477 }
1478
1479 case 'n':
1480 {
1481 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
1482 if (pFmtDesc)
1483 rcExit2 = CheckFormatNotOnClipboard(pFmtDesc);
1484 else
1485 rcExit2 = RTEXITCODE_FAILURE;
1486 break;
1487 }
1488
1489
1490 case 'z':
1491 rcExit2 = ZapAllClipboardData();
1492 break;
1493
1494#ifdef MULTI_TARGET_CLIPBOARD
1495 case 't':
1496 {
1497 CLIPUTILTARGET *pNewTarget = NULL;
1498 for (size_t i = 0; i < RT_ELEMENTS(g_aTargets); i++)
1499 if (strcmp(ValueUnion.psz, g_aTargets[i].pszName) == 0)
1500 {
1501 pNewTarget = &g_aTargets[i];
1502 break;
1503 }
1504 if (!pNewTarget)
1505 return RTMsgErrorExitFailure("Unknown target '%s'", ValueUnion.psz);
1506 if (pNewTarget != g_pTarget && g_uVerbosity > 0)
1507 RTMsgInfo("Switching from '%s' to '%s'\n", g_pTarget->pszName, pNewTarget->pszName);
1508 g_pTarget = pNewTarget;
1509 break;
1510 }
1511#endif
1512
1513 case 'q':
1514 g_uVerbosity = 0;
1515 break;
1516
1517 case 'v':
1518 g_uVerbosity++;
1519 break;
1520
1521 case 'h':
1522 Usage(g_pStdOut);
1523 return RTEXITCODE_SUCCESS;
1524
1525 case 'V':
1526 {
1527 char szRev[] = "$Revision: 92161 $";
1528 szRev[RT_ELEMENTS(szRev) - 2] = '\0';
1529 RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
1530 return RTEXITCODE_SUCCESS;
1531 }
1532
1533 default:
1534 return RTGetOptPrintError(rc, &ValueUnion);
1535 }
1536
1537 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
1538 rcExit = rcExit2;
1539 }
1540
1541 /*
1542 * Host specific cleanup.
1543 */
1544#if defined(RT_OS_OS2)
1545 RTEXITCODE rcExit2 = CuOs2Term();
1546#elif defined(RT_OS_WINDOWS)
1547 RTEXITCODE rcExit2 = CuWinTerm();
1548#else
1549 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
1550#endif
1551 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit != RTEXITCODE_SUCCESS)
1552 rcExit = rcExit2;
1553
1554 return rcExit;
1555}
1556
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette