VirtualBox

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

Last change on this file since 92137 was 92026, checked in by vboxsync, 4 years ago

ValKit/ClipUtil: Adding simple clipboard utility and implemented the windows side of it. [scm fixes] bugref:10133

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 KB
Line 
1/* $Id: ClipUtil.cpp 92026 2021-10-25 11:49:23Z 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_WINDOWS
53# include <iprt/nt/nt-and-windows.h>
54#endif
55
56
57/*********************************************************************************************************************************
58* Defined Constants And Macros *
59*********************************************************************************************************************************/
60#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) || defined(RT_OS_DARWIN)
61# undef MULTI_TARGET_CLIPBOARD
62#else
63# define MULTI_TARGET_CLIPBOARD
64#endif
65
66
67/*********************************************************************************************************************************
68* Structures and Typedefs *
69*********************************************************************************************************************************/
70/**
71 * Clipboard format descriptor.
72 */
73typedef struct CLIPUTILFORMAT
74{
75 /** Format name. */
76 const char *pszName;
77
78#if defined(RT_OS_WINDOWS)
79 /** Windows integer format (CF_XXXX). */
80 UINT fFormat;
81 /** Windows string format name. */
82 const WCHAR *pwszFormat;
83
84#elif defined(RT_OS_OS2)
85 /** OS/2 integer format. */
86 ULONG fFormat;
87 /** OS/2 string format name. */
88 const char *pszFormat;
89
90#elif defined(RT_OS_DARWIN)
91 /** Native format (flavor). */
92 CFStringRef *hStrFormat;
93#else
94 /** @todo X11 */
95#endif
96
97 /** Description. */
98 const char *pszDesc;
99 /** CLIPUTILFORMAT_F_XXX. */
100 uint32_t fFlags;
101} CLIPUTILFORMAT;
102/** Pointer to a clipobard format descriptor. */
103typedef CLIPUTILFORMAT const *PCCLIPUTILFORMAT;
104
105/** Convert to/from UTF-8. */
106#define CLIPUTILFORMAT_F_CONVERT_UTF8 RT_BIT_32(0)
107
108
109/**
110 * Clipboard target descriptor.
111 */
112typedef struct CLIPUTILTARGET
113{
114 /** Target name. */
115 const char *pszName;
116 /** Description. */
117 const char *pszDesc;
118} CLIPUTILTARGET;
119/** Pointer to clipboard target descriptor. */
120typedef CLIPUTILTARGET const *PCCLIPUTILTARGET;
121
122
123/*********************************************************************************************************************************
124* Global Variables *
125*********************************************************************************************************************************/
126/** Command line parameters */
127static const RTGETOPTDEF g_aCmdOptions[] =
128{
129 { "--list", 'l', RTGETOPT_REQ_NOTHING },
130 { "--get", 'g', RTGETOPT_REQ_STRING },
131 { "--get-file", 'G', RTGETOPT_REQ_STRING },
132 { "--put", 'p', RTGETOPT_REQ_STRING },
133 { "--put-file", 'P', RTGETOPT_REQ_STRING },
134 { "--check", 'c', RTGETOPT_REQ_STRING },
135 { "--check-file", 'C', RTGETOPT_REQ_STRING },
136 { "--check-not", 'n', RTGETOPT_REQ_STRING },
137 { "--zap", 'z', RTGETOPT_REQ_NOTHING },
138#ifdef MULTI_TARGET_CLIPBOARD
139 { "--target", 't', RTGETOPT_REQ_STRING },
140#endif
141 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
142 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
143 { "--version", 'V', RTGETOPT_REQ_NOTHING },
144 { "--help", 'h', RTGETOPT_REQ_NOTHING }, /* for Usage() */
145};
146
147/** Format descriptors. */
148static const CLIPUTILFORMAT g_aFormats[] =
149{
150#if defined(RT_OS_WINDOWS)
151 { "text/ansi", CF_TEXT, NULL, "ANSI text", 0 },
152 { "text/utf-16", CF_UNICODETEXT, NULL, "UTF-16 text", 0 },
153 { "text/utf-8", CF_UNICODETEXT, NULL, "UTF-8 text", CLIPUTILFORMAT_F_CONVERT_UTF8 },
154 /* https://docs.microsoft.com/en-us/windows/desktop/dataxchg/html-clipboard-format */
155 { "text/html", 0, L"HTML Format", "HTML text", 0 },
156 { "bitmap", CF_DIB, NULL, "Bitmap (DIB)", 0 },
157 { "bitmap/v5", CF_DIBV5, NULL, "Bitmap version 5 (DIBv5)", 0 },
158#elif defined(RT_OS_OS2)
159 { "text/ascii", CF_TEXT, NULL, "ASCII text", 0 },
160 { "text/utf-8", CF_TEXT, NULL, "UTF-8 text", CLIPUTILFORMAT_F_CONVERT_UTF8 },
161 { "text/utf-16", 0, "Odin32 UnicodeText", "UTF-16 text", 0},
162#elif defined(RT_OS_DARWIN)
163 { "text/utf-8", kUTTypeUTF8PlainText, "UTF-8 text", 0 },
164 { "text/utf-16", kUTTypeUTF16PlainText, "UTF-16 text", 0 },
165#else
166 /** @todo X11 */
167 { "text/utf-8", "UTF-8 text", 0 },
168#endif
169};
170
171/** Target descriptors. */
172static const CLIPUTILTARGET g_aTargets[] =
173{
174#ifndef MULTI_TARGET_CLIPBOARD
175 { "default", "placeholder" }
176#else
177 { "default", "default" }, /** @todo X11 */
178#endif
179};
180
181/** The -v/-q state. */
182static unsigned g_uVerbosity = 1;
183
184#ifdef RT_OS_WINDOWS
185static bool g_fOpenedClipboard = false;
186#endif
187
188
189/**
190 * Gets a format descriptor, complaining if unknown format.
191 *
192 * @returns Pointer to the descriptor if found, NULL + msg if not.
193 * @param pszFormat The format to get.
194 */
195static PCCLIPUTILFORMAT GetFormatDesc(const char *pszFormat)
196{
197 for (size_t i = 0; i < RT_ELEMENTS(g_aFormats); i++)
198 if (strcmp(pszFormat, g_aFormats[i].pszName))
199 return &g_aFormats[i];
200 RTMsgError("Unknown format '%s'", pszFormat);
201 return NULL;
202}
203
204#ifdef RT_OS_WINDOWS
205
206/**
207 * Opens the window clipboard.
208 */
209static RTEXITCODE WinOpenClipboardIfNecessary(void)
210{
211 if (g_fOpenedClipboard)
212 return RTEXITCODE_SUCCESS;
213 if (OpenClipboard(NULL))
214 {
215 g_fOpenedClipboard = true;
216 return RTEXITCODE_SUCCESS;
217 }
218 return RTMsgErrorExitFailure("OpenClipboard failed: %u (%#x)", GetLastError(), GetLastError());
219}
220
221
222/**
223 * Wrapper that automatically registers non-standard formats.
224 */
225static UINT FormatDescToWindows(PCCLIPUTILFORMAT pFmtDesc)
226{
227 if (pFmtDesc->fFormat)
228 return pFmtDesc->fFormat;
229 Assert(pFmtDesc->pwszFormat);
230 UINT fFormat = RegisterClipboardFormatW(pFmtDesc->pwszFormat);
231 if (fFormat == 0)
232 RTMsgError("RegisterClipboardFormatW(%ls) failed: %u (%#x)", pFmtDesc->pwszFormat, GetLastError(), GetLastError());
233 return fFormat;
234}
235
236#endif /* RT_OS_WINDOWS */
237
238
239
240/**
241 * Lists the clipboard format.
242 */
243static RTEXITCODE ListClipboardContent(void)
244{
245#ifdef RT_OS_WINDOWS
246 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
247 if (rcExit == RTEXITCODE_SUCCESS)
248 {
249 SetLastError(0);
250 uint32_t idx = 0;
251 UINT fFormat = 0;
252 while ((fFormat = EnumClipboardFormats(fFormat)) != 0)
253 {
254 WCHAR wszName[256];
255 int cchName = GetClipboardFormatNameW(fFormat, wszName, RT_ELEMENTS(wszName));
256 if (cchName > 0)
257 RTPrintf("#%u: %#06x - %ls\n", idx, fFormat, wszName);
258 else
259 {
260 const char *pszName = NULL;
261 switch (fFormat)
262 {
263 case CF_TEXT: pszName = "CF_TEXT"; break;
264 case CF_BITMAP: pszName = "CF_BITMAP"; break;
265 case CF_METAFILEPICT: pszName = "CF_METAFILEPICT"; break;
266 case CF_SYLK: pszName = "CF_SYLK"; break;
267 case CF_DIF: pszName = "CF_DIF"; break;
268 case CF_TIFF: pszName = "CF_TIFF"; break;
269 case CF_OEMTEXT: pszName = "CF_OEMTEXT"; break;
270 case CF_DIB: pszName = "CF_DIB"; break;
271 case CF_PALETTE: pszName = "CF_PALETTE"; break;
272 case CF_PENDATA: pszName = "CF_PENDATA"; break;
273 case CF_RIFF: pszName = "CF_RIFF"; break;
274 case CF_WAVE: pszName = "CF_WAVE"; break;
275 case CF_UNICODETEXT: pszName = "CF_UNICODETEXT"; break;
276 case CF_ENHMETAFILE: pszName = "CF_ENHMETAFILE"; break;
277 case CF_HDROP: pszName = "CF_HDROP"; break;
278 case CF_LOCALE: pszName = "CF_LOCALE"; break;
279 case CF_DIBV5: pszName = "CF_DIBV5"; break;
280 default:
281 break;
282 }
283 if (pszName)
284 RTPrintf("#%u: %#06x - %s\n", idx, fFormat, pszName);
285 else
286 RTPrintf("#%u: %#06x\n", idx, fFormat);
287 }
288
289 idx++;
290 }
291 if (idx == 0)
292 RTPrintf("Empty\n");
293 }
294 return rcExit;
295
296#else
297 return RTMsgErrorExitFailure("ListClipboardContent is not implemented");
298#endif
299}
300
301
302/**
303 * Reads the given clipboard format and stores it in on the heap.
304 *
305 * @returns Success indicator.
306 * @param pFmtDesc The format to get.
307 * @param ppvData Where to return the pointer to the data. Free using
308 * RTMemFree when done.
309 * @param pcbData Where to return the amount of data returned.
310 */
311static RTEXITCODE ReadClipboardData(PCCLIPUTILFORMAT pFmtDesc, void **ppvData, size_t *pcbData)
312{
313 *ppvData = NULL;
314 *pcbData = 0;
315#ifdef RT_OS_WINDOWS
316 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
317 if (rcExit == RTEXITCODE_SUCCESS)
318 {
319 HANDLE hData = GetClipboardData(FormatDescToWindows(pFmtDesc));
320 if (hData != NULL)
321 {
322 SIZE_T const cbData = GlobalSize(hData);
323 PVOID const pvData = GlobalLock(hData);
324 if (pvData != NULL)
325 {
326 *pcbData = cbData;
327 if (cbData != 0)
328 {
329 if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
330 {
331 char *pszUtf8 = NULL;
332 size_t cchUtf8 = 0;
333 int rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvData, cbData / sizeof(RTUTF16), &pszUtf8, 0, &cchUtf8);
334 if (RT_SUCCESS(rc))
335 {
336 *pcbData = cchUtf8 + 1;
337 *ppvData = RTMemDup(pszUtf8, cchUtf8 + 1);
338 RTStrFree(pszUtf8);
339 if (!*ppvData)
340 rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", cbData);
341 }
342 else
343 rcExit = RTMsgErrorExitFailure("RTUtf16ToUtf8Ex failed: %Rrc", rc);
344 }
345 else
346 {
347 *ppvData = RTMemDup(pvData, cbData);
348 if (!*ppvData)
349 rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", cbData);
350 }
351 }
352 GlobalUnlock(hData);
353 }
354 else
355 rcExit = RTMsgErrorExitFailure("GetClipboardData(%s) failed: %u (%#x)\n",
356 pFmtDesc->pszName, GetLastError(), GetLastError());
357 }
358 else
359 rcExit = RTMsgErrorExitFailure("GetClipboardData(%s) failed: %u (%#x)\n",
360 pFmtDesc->pszName, GetLastError(), GetLastError());
361 }
362 return rcExit;
363
364#else
365 RT_NOREF(pFmtDesc);
366 return RTMsgErrorExitFailure("ReadClipboardData is not implemented\n");
367#endif
368}
369
370
371/**
372 * Puts the given data and format on the clipboard.
373 *
374 * @returns Success indicator.
375 * @param pFmtDesc The format.
376 * @param pvData The data.
377 * @param cbData The amount of data in bytes.
378 */
379static RTEXITCODE WriteClipboardData(PCCLIPUTILFORMAT pFmtDesc, void const *pvData, size_t cbData)
380{
381#ifdef RT_OS_WINDOWS
382 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
383 if (rcExit == RTEXITCODE_SUCCESS)
384 {
385 /*
386 * Do input data conversion.
387 */
388 PRTUTF16 pwszFree = NULL;
389 if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
390 {
391 size_t cwcConv = 0;
392 int rc = RTStrToUtf16Ex((char const *)pvData, cbData, &pwszFree, 0, &cwcConv);
393 if (RT_SUCCESS(rc))
394 {
395 pvData = pwszFree;
396 cbData = cwcConv * sizeof(RTUTF16);
397 }
398 else
399 return RTMsgErrorExitFailure("RTStrToTUtf16Ex failed: %Rrc\n", rc);
400 }
401
402 /*
403 * Text formats generally include the zero terminator.
404 */
405 uint32_t cbZeroPadding = 0;
406 if (pFmtDesc->fFormat == CF_UNICODETEXT)
407 cbZeroPadding = sizeof(WCHAR);
408 else if (pFmtDesc->fFormat == CF_TEXT)
409 cbZeroPadding = sizeof(char);
410
411 HANDLE hDstData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, cbData + cbZeroPadding);
412 if (hDstData)
413 {
414 if (cbData)
415 {
416 PVOID pvDstData = GlobalLock(hDstData);
417 if (pvDstData)
418 memcpy(pvDstData, pvData, cbData);
419 else
420 rcExit = RTMsgErrorExitFailure("GlobalLock failed: %u (%#x)\n", GetLastError(), GetLastError());
421 }
422 if (rcExit == RTEXITCODE_SUCCESS)
423 {
424 if (!SetClipboardData(FormatDescToWindows(pFmtDesc), hDstData))
425 {
426 rcExit = RTMsgErrorExitFailure("SetClipboardData(%s) failed: %u (%#x)\n",
427 pFmtDesc->pszName, GetLastError(), GetLastError());
428 GlobalFree(hDstData);
429 }
430 }
431 else
432 GlobalFree(hDstData);
433 }
434 else
435 rcExit = RTMsgErrorExitFailure("GlobalAlloc(,%#zx) failed: %u (%#x)\n",
436 cbData + cbZeroPadding, GetLastError(), GetLastError());
437 }
438 return rcExit;
439
440#else
441 RT_NOREF(pFmtDesc, pvData, cbData);
442 return RTMsgErrorExitFailure("WriteClipboardData is not implemented\n");
443#endif
444}
445
446
447/**
448 * Check if the given data + format matches what's actually on the clipboard.
449 *
450 * @returns Success indicator.
451 * @param pFmtDesc The format to compare.
452 * @param pvExpect The expected clipboard data.
453 * @param cbExpect The size of the expected clipboard data.
454 */
455static RTEXITCODE CompareDataWithClipboard(PCCLIPUTILFORMAT pFmtDesc, void const *pvExpect, size_t cbExpect)
456{
457 void *pvData = NULL;
458 size_t cbData = 0;
459 RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
460 if (rcExit == RTEXITCODE_SUCCESS)
461 {
462 if ( cbData == cbExpect
463 && memcmp(pvData, pvExpect, cbData) == 0)
464 rcExit = RTEXITCODE_SUCCESS;
465 else
466 rcExit = RTMsgErrorExitFailure("Mismatch for '%s' (cbData=%#zx cbExpect=%#zx)\n",
467 pFmtDesc->pszName, cbData, cbExpect);
468 RTMemFree(pvData);
469 }
470 return rcExit;
471}
472
473
474/**
475 * Gets the given clipboard format.
476 *
477 * @returns Success indicator.
478 * @param pFmtDesc The format to get.
479 * @param pStrmOut Where to output the data.
480 * @param fIsStdOut Set if @a pStrmOut is standard output, clear if not.
481 */
482static RTEXITCODE ClipboardContentToStdOut(PCCLIPUTILFORMAT pFmtDesc)
483{
484 void *pvData = NULL;
485 size_t cbData = 0;
486 RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
487 if (rcExit == RTEXITCODE_SUCCESS)
488 {
489 int rc = RTStrmWrite(g_pStdOut, pvData, cbData);
490 RTMemFree(pvData);
491 if (RT_FAILURE(rc))
492 rcExit = RTMsgErrorExitFailure("Error writing %#zx bytes to standard output: %Rrc", cbData, rc);
493 }
494 return rcExit;
495}
496
497
498/**
499 * Gets the given clipboard format.
500 *
501 * @returns Success indicator.
502 * @param pFmtDesc The format to get.
503 * @param pszFilename The output filename.
504 */
505static RTEXITCODE ClipboardContentToFile(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
506{
507 void *pvData = NULL;
508 size_t cbData = 0;
509 RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
510 if (rcExit == RTEXITCODE_SUCCESS)
511 {
512 RTFILE hFile = NIL_RTFILE;
513 int rc = RTFileOpen(&hFile, pszFilename,
514 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE
515 | (0770 << RTFILE_O_CREATE_MODE_SHIFT));
516 if (RT_SUCCESS(rc))
517 {
518 rc = RTFileWrite(hFile, pvData, cbData, NULL);
519 int const rc2 = RTFileClose(hFile);
520 if (RT_FAILURE(rc) || RT_FAILURE(rc2))
521 {
522 if (RT_FAILURE_NP(rc))
523 RTMsgError("Writing %#z bytes to '%s' failed: %Rrc", cbData, pszFilename, rc);
524 else
525 RTMsgError("Closing '%s' failed: %Rrc", pszFilename, rc2);
526 RTMsgInfo("Deleting '%s'.", pszFilename);
527 RTFileDelete(pszFilename);
528 rcExit = RTEXITCODE_FAILURE;
529 }
530 }
531 else
532 rcExit = RTMsgErrorExitFailure("Failed to open '%s' for writing: %Rrc", pszFilename, rc);
533 RTMemFree(pvData);
534 }
535 return rcExit;
536}
537
538
539/**
540 * Puts the given format + data onto the clipboard.
541 *
542 * @returns Success indicator.
543 * @param pFmtDesc The format to put.
544 * @param pszData The string data.
545 */
546static RTEXITCODE PutStringOnClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszData)
547{
548 return WriteClipboardData(pFmtDesc, pszData, strlen(pszData));
549}
550
551
552/**
553 * Puts a format + file content onto the clipboard.
554 *
555 * @returns Success indicator.
556 * @param pFmtDesc The format to put.
557 * @param pszFilename The filename.
558 */
559static RTEXITCODE PutFileOnClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
560{
561 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
562 void *pvData = NULL;
563 size_t cbData = 0;
564 int rc = RTFileReadAll(pszFilename, &pvData, &cbData);
565 if (RT_SUCCESS(rc))
566 {
567 rcExit = WriteClipboardData(pFmtDesc, pvData, cbData);
568 RTFileReadAllFree(pvData, cbData);
569 }
570 else
571 rcExit = RTMsgErrorExitFailure("Failed to open and read '%s' into memory: %Rrc", pszFilename, rc);
572 return rcExit;
573}
574
575
576/**
577 * Checks if the given format + data matches what's on the clipboard.
578 *
579 * @returns Success indicator.
580 * @param pFmtDesc The format to check.
581 * @param pszData The string data.
582 */
583static RTEXITCODE CheckStringAgainstClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszData)
584{
585 return CompareDataWithClipboard(pFmtDesc, pszData, strlen(pszData));
586}
587
588
589/**
590 * Check if the given format + file content matches what's on the clipboard.
591 *
592 * @returns Success indicator.
593 * @param pFmtDesc The format to check.
594 * @param pszFilename The filename.
595 */
596static RTEXITCODE CheckFileAgainstClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
597{
598 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
599 void *pvData = NULL;
600 size_t cbData = 0;
601 int rc = RTFileReadAll(pszFilename, &pvData, &cbData);
602 if (RT_SUCCESS(rc))
603 {
604 rcExit = CompareDataWithClipboard(pFmtDesc, pvData, cbData);
605 RTFileReadAllFree(pvData, cbData);
606 }
607 else
608 rcExit = RTMsgErrorExitFailure("Failed to open and read '%s' into memory: %Rrc", pszFilename, rc);
609 return rcExit;
610}
611
612
613/**
614 * Check that the given format is not on the clipboard.
615 *
616 * @returns Success indicator.
617 * @param pFmtDesc The format to check.
618 */
619static RTEXITCODE CheckFormatNotOnClipboard(PCCLIPUTILFORMAT pFmtDesc)
620{
621#ifdef RT_OS_WINDOWS
622 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
623 if (rcExit == RTEXITCODE_SUCCESS)
624 {
625 if (IsClipboardFormatAvailable(FormatDescToWindows(pFmtDesc)))
626 rcExit = RTMsgErrorExitFailure("Format '%s' is present");
627 }
628 return rcExit;
629
630#else
631 RT_NOREF(pFmtDesc);
632 return RTMsgErrorExitFailure("CheckFormatNotOnClipboard is not implemented");
633#endif
634}
635
636
637/**
638 * Empties the clipboard.
639 *
640 * @returns Success indicator.
641 */
642static RTEXITCODE ZapAllClipboardData(void)
643{
644#ifdef RT_OS_WINDOWS
645 RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
646 if (rcExit == RTEXITCODE_SUCCESS)
647 {
648 if (!EmptyClipboard())
649 rcExit = RTMsgErrorExitFailure("EmptyClipboard() failed: %u (%#x)\n", GetLastError(), GetLastError());
650 }
651 return rcExit;
652
653#else
654 return RTMsgErrorExitFailure("ZapAllClipboardData is not implemented");
655#endif
656}
657
658
659/**
660 * Display the usage to @a pStrm.
661 */
662static void Usage(PRTSTREAM pStrm)
663{
664 RTStrmPrintf(pStrm,
665 "usage: %s [--get <fmt> [--get ...]] [--get-file <fmt> <file> [--get-file ...]]\n"
666 " %s [--zap] [--put <fmt> <content> [--put ...]] [--put-file <fmt> <file> [--put-file ...]]\n"
667 " %s [--check <fmt> <expected> [--check ...]] [--check-file <fmt> <file> [--check-file ...]]\n"
668 " [--check-no <fmt> [--check-no ...]]\n"
669 , RTProcShortName(), RTProcShortName(), RTProcShortName());
670 RTStrmPrintf(pStrm, "\n");
671 RTStrmPrintf(pStrm, "Options: \n");
672
673 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
674 {
675 const char *pszHelp;
676 switch (g_aCmdOptions[i].iShort)
677 {
678 case 'l': pszHelp = "List the clipboard content."; break;
679 case 'g': pszHelp = "Get given clipboard format and writes it to standard output."; break;
680 case 'G': pszHelp = "Get given clipboard format and writes it to the specified file."; break;
681 case 'p': pszHelp = "Puts given format and content on the clipboard."; break;
682 case 'P': pszHelp = "Puts given format and file content on the clipboard."; break;
683 case 'c': pszHelp = "Checks that the given format and content matches the clipboard."; break;
684 case 'C': pszHelp = "Checks that the given format and file content matches the clipboard."; break;
685 case 'n': pszHelp = "Checks that the given format is not on the clipboard."; break;
686 case 'z': pszHelp = "Zaps the clipboard content."; break;
687#ifdef MULTI_TARGET_CLIPBOARD
688 case 't': pszHelp = "Selects the target clipboard." break;
689#endif
690 case 'v': pszHelp = "More verbose execution."; break;
691 case 'q': pszHelp = "Quiet execution."; break;
692 case 'h': pszHelp = "Displays this help and exit"; break;
693 case 'V': pszHelp = "Displays the program revision"; break;
694
695 default:
696 pszHelp = "Option undocumented";
697 break;
698 }
699 if ((unsigned)g_aCmdOptions[i].iShort < 127U)
700 {
701 char szOpt[64];
702 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
703 RTStrmPrintf(pStrm, " %-19s %s\n", szOpt, pszHelp);
704 }
705 else
706 RTStrmPrintf(pStrm, " %-19s %s\n", g_aCmdOptions[i].pszLong, pszHelp);
707 }
708 RTStrmPrintf(pStrm, "Note! Options are processed in the order they are given.\n");
709
710 RTStrmPrintf(pStrm, "\nFormats:\n");
711 for (size_t i = 0; i < RT_ELEMENTS(g_aFormats); i++)
712 RTStrmPrintf(pStrm, " %-12s: %s\n", g_aFormats[i].pszName, g_aFormats[i].pszDesc);
713
714#ifdef MULTI_TARGET_CLIPBOARD
715 RTStrmPrintf(pStrm, "\nTarget:\n");
716 for (size_t i = 0; i < RT_ELEMENTS(g_aTargets); i++)
717 RTStrmPrintf(pStrm, " %-12s: %s\n", g_aTargets[i].pszName, g_aTargets[i].pszDesc);
718#endif
719}
720
721
722int main(int argc, char *argv[])
723{
724 /*
725 * Init IPRT.
726 */
727 int rc = RTR3InitExe(argc, &argv, 0);
728 if (RT_FAILURE(rc))
729 return RTMsgInitFailure(rc);
730
731 /*
732 * Process options (in order).
733 */
734 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
735 RTGETOPTUNION ValueUnion;
736 RTGETOPTSTATE GetState;
737 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
738 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
739 {
740 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
741 switch (rc)
742 {
743 case 'l':
744 rcExit2 = ListClipboardContent();
745 break;
746
747 case 'g':
748 {
749 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
750 if (pFmtDesc)
751 rcExit2 = ClipboardContentToStdOut(pFmtDesc);
752 else
753 rcExit2 = RTEXITCODE_FAILURE;
754 break;
755 }
756
757 case 'G':
758 {
759 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
760 if (pFmtDesc)
761 {
762 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
763 if (RT_SUCCESS(rc))
764 rcExit2 = ClipboardContentToFile(pFmtDesc, ValueUnion.psz);
765 else
766 return RTMsgError("No filename given with --get-file");
767 }
768 else
769 rcExit2 = RTEXITCODE_FAILURE;
770 break;
771 }
772
773 case 'p':
774 {
775 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
776 if (pFmtDesc)
777 {
778 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
779 if (RT_SUCCESS(rc))
780 rcExit2 = PutStringOnClipboard(pFmtDesc, ValueUnion.psz);
781 else
782 return RTMsgError("No data string given with --put");
783 }
784 else
785 rcExit2 = RTEXITCODE_FAILURE;
786 break;
787 }
788
789 case 'P':
790 {
791 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
792 if (pFmtDesc)
793 {
794 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
795 if (RT_SUCCESS(rc))
796 rcExit2 = PutFileOnClipboard(pFmtDesc, ValueUnion.psz);
797 else
798 return RTMsgError("No filename given with --put-file");
799 }
800 else
801 rcExit2 = RTEXITCODE_FAILURE;
802 break;
803 }
804
805 case 'c':
806 {
807 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
808 if (pFmtDesc)
809 {
810 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
811 if (RT_SUCCESS(rc))
812 rcExit2 = CheckStringAgainstClipboard(pFmtDesc, ValueUnion.psz);
813 else
814 return RTMsgError("No data string given with --check");
815 }
816 else
817 rcExit2 = RTEXITCODE_FAILURE;
818 break;
819 }
820
821 case 'C':
822 {
823 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
824 if (pFmtDesc)
825 {
826 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
827 if (RT_SUCCESS(rc))
828 rcExit2 = CheckFileAgainstClipboard(pFmtDesc, ValueUnion.psz);
829 else
830 return RTMsgError("No filename given with --check-file");
831 }
832 else
833 rcExit2 = RTEXITCODE_FAILURE;
834 break;
835 }
836
837 case 'n':
838 {
839 PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
840 if (pFmtDesc)
841 rcExit2 = CheckFormatNotOnClipboard(pFmtDesc);
842 else
843 rcExit2 = RTEXITCODE_FAILURE;
844 break;
845 }
846
847
848 case 'z':
849 rcExit2 = ZapAllClipboardData();
850 break;
851
852 case 'q':
853 g_uVerbosity = 0;
854 break;
855
856 case 'v':
857 g_uVerbosity++;
858 break;
859
860 case 'h':
861 Usage(g_pStdOut);
862 return RTEXITCODE_SUCCESS;
863
864 case 'V':
865 {
866 char szRev[] = "$Revision: 92026 $";
867 szRev[RT_ELEMENTS(szRev) - 2] = '\0';
868 RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
869 return RTEXITCODE_SUCCESS;
870 }
871
872 default:
873 return RTGetOptPrintError(rc, &ValueUnion);
874 }
875
876 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
877 rcExit = rcExit2;
878 }
879
880 /*
881 * Host specific cleanup.
882 */
883#ifdef RT_OS_WINDOWS
884 if (g_fOpenedClipboard)
885 {
886 if (!CloseClipboard())
887 rcExit = RTMsgErrorExitFailure("CloseClipboard failed: %u (%#x)", GetLastError(), GetLastError());
888 }
889#endif
890
891 return rcExit;
892}
893
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