Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Support.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <cctype>
7#include <cstdarg>
8
15#include "Stroika/Foundation/Execution/Throw.h"
17
18#include "Stroika/Frameworks/Led/Config.h" // For qStroika_Platform_Windows etc defines...
19
20#if qStroika_Platform_Windows
21#include <fcntl.h>
22#include <io.h>
23#endif
24
25#include "Support.h"
26
27#if qStroika_Frameworks_Led_UseActiveXToOpenURLs
28#include <URLMon.h>
29#endif
30
31using namespace Stroika::Foundation;
33using namespace Stroika::Frameworks;
34using namespace Stroika::Frameworks::Led;
35
36#if !qStroika_Foundation_Characters_SDKUseswchar_t
37SDKString Led::Led_tString2SDKString (const Led_tString& s)
38{
39 return CodeCvt<wchar_t>{locale{}}.String2Bytes<SDKString> (span<const wchar_t>{s});
40}
41#endif
42
43/*
44@METHOD: Led_ANSIString2tString
45@DESCRIPTION: <p></p>
46*/
47Led_tString Led::Led_ANSIString2tString (const string& s)
48{
49 // Up until Stroika v3.0d2 (and maybe for a while after) - this converted from CP_ACP, despite being called 'ANSI' - I htink because I was
50 // once confused about the difference between these two --LGP 2023-07-27
51 return CodeCvt<Led_tChar>{locale{}}.Bytes2String<Led_tString> (as_bytes (span{s}));
52}
53
54/*
55@METHOD: Led_tString2ANSIString
56@DESCRIPTION: <p></p>
57*/
58string Led::Led_tString2ANSIString (const Led_tString& s)
59{
60 // Up until Stroika v3.0d2 (and maybe for a while after) - this converted from CP_ACP, despite being called 'ANSI' - I htink because I was
61 // once confused about the difference between these two --LGP 2023-07-27
62 return CodeCvt<Led_tChar>{locale{}}.String2Bytes<string> (span{s});
63}
64
65/*
66@METHOD: Led_BeepNotify
67@DESCRIPTION: <p>Make an audible beep on the users terminal. Used as a simple warning
68 mechanism (like for typing bad characters).</p>
69*/
70void Led::Led_BeepNotify ()
71{
72#if qStroika_Platform_Windows
73 ::MessageBeep (MB_OK);
74#endif
75}
76
77/*
78@METHOD: Led_GetDoubleClickTime
79@DESCRIPTION: <p>Returns the amount of time (in seconds) between clicks which the OS deems should be interpretted
80 as a double click.</p>
81*/
82Time::DurationSeconds Led::Led_GetDoubleClickTime ()
83{
84#if qStroika_Platform_Windows
85 return Time::DurationSeconds{float (::GetDoubleClickTime ()) / 1000.0f};
86#else
87 return 0.25s; // SAME AS DOUBLE_CLICK_TIME FROM gdkevents.c
88#endif
89}
90
91int Led::Led_tStrniCmp (const Led_tChar* l, const Led_tChar* r, size_t n)
92{
96 auto result =
97 String::ThreeWayComparer{eCaseInsensitive}(Characters::String{l}.SafeSubString (0, n), Characters::String{r}.SafeSubString (0, n));
98 if (result == strong_ordering::equal)
99 return 0;
100 if (result == strong_ordering::less)
101 return -1;
102 if (result == strong_ordering::greater)
103 return 1;
105 return 0;
106}
107
108int Led::Led_tStriCmp (const Led_tChar* l, const Led_tChar* r)
109{
110 RequireNotNull (l);
111 RequireNotNull (r);
112 auto result = String::ThreeWayComparer{eCaseInsensitive}(Characters::String{l}, Characters::String{r});
113 if (result == strong_ordering::equal)
114 return 0;
115 if (result == strong_ordering::less)
116 return -1;
117 if (result == strong_ordering::greater)
118 return 1;
120 return 0;
121}
122
123/*
124@METHOD: Led_SkrunchOutSpecialChars
125@DESCRIPTION: <p>This function is useful to remove embedded NUL-characters from a string, or
126 any other such undesirable characters. Returns NEW length of string (after removals).
127 Removes in place.</p>
128*/
129size_t Led::Led_SkrunchOutSpecialChars (Led_tChar* text, size_t textLen, Led_tChar charToRemove)
130{
131 size_t charsSkipped = 0;
132 Led_tChar* end = text + textLen;
133 for (Led_tChar* p = text; p + charsSkipped < end;) {
134 if (*(p + charsSkipped) == charToRemove) {
135 ++charsSkipped;
136 continue;
137 }
138 *p = *(p + charsSkipped);
139 ++p;
140 }
141 return textLen - charsSkipped;
142}
143
144#if qStroika_Frameworks_Led_SupportClipboard
145/*
146 ********************************************************************************
147 ************************** Led_ClipboardObjectAcquire **************************
148 ********************************************************************************
149 */
150
151Led_ClipboardObjectAcquire::Led_ClipboardObjectAcquire (Led_ClipFormat clipType)
152 :
153#if qStroika_Platform_Windows
154 fOSClipHandle (nullptr)
155 ,
156#endif
157 fLockedData (nullptr)
158{
159#if qStroika_Platform_Windows
160 // perhaps rewrite to use exceptions, but for now - when no cliptype avail - set flag so GoodClip() method can check -
161 // just cuz thats what surounding code seems to expect - LGP 980617
162 fOSClipHandle = ::GetClipboardData (clipType);
163#if 0
164 DWORD errResult = ::GetLastError (); // could be helpful for debugging if above fails... - Maybe I should do ThrowIfErrorHRESULT().... - LGP 2000/04/26
165#endif
166 if (fOSClipHandle != nullptr) {
167 fLockedData = ::GlobalLock (fOSClipHandle);
168 }
169#endif
170}
171#endif
172
173#if qStroika_Platform_Windows
174/*
175 ********************************************************************************
176 **************************** VariantArrayPacker ********************************
177 ********************************************************************************
178 */
179VariantArrayPacker::VariantArrayPacker (VARIANT* v, VARTYPE vt, size_t nElts)
180 : fSafeArrayVariant{v}
181{
182 RequireNotNull (v);
183 // assumes passed in variant is CONSTRUCTED (initied) - but not necesarily having the right type
184 ::VariantClear (fSafeArrayVariant);
185 fSafeArrayVariant->parray = ::SafeArrayCreateVector (vt, 0, static_cast<ULONG> (nElts));
186 Execution::ThrowIfNull (fSafeArrayVariant->parray);
187 fSafeArrayVariant->vt = VT_ARRAY | vt;
188 ThrowIfErrorHRESULT (::SafeArrayAccessData (fSafeArrayVariant->parray, &fPtr));
189}
190
191VariantArrayPacker::~VariantArrayPacker ()
192{
193 AssertNotNull (fSafeArrayVariant);
194 AssertNotNull (fSafeArrayVariant->parray);
195 ::SafeArrayUnaccessData (fSafeArrayVariant->parray);
196}
197
198void* VariantArrayPacker::PokeAtData () const
199{
200 return fPtr;
201}
202
203/*
204 ********************************************************************************
205 ***************************** VariantArrayUnpacker *****************************
206 ********************************************************************************
207 */
208VariantArrayUnpacker::VariantArrayUnpacker (const VARIANT& v)
209 : fSafeArray{v.parray}
210{
211 if (not(v.vt & VT_ARRAY) or fSafeArray == nullptr) {
212 throw E_INVALIDARG;
213 }
214 if ((v.vt & ~VT_ARRAY) != GetArrayElementType ()) {
215 // I THINK this is right??? - LGP 2003-06-12 - but I'm not sure this is much reason to check/??
216 throw E_INVALIDARG;
217 }
218 void* useP = nullptr;
219 ThrowIfErrorHRESULT (::SafeArrayAccessData (fSafeArray, &useP));
220 fPtr = useP;
221}
222
223VariantArrayUnpacker::~VariantArrayUnpacker ()
224{
225 AssertNotNull (fSafeArray);
226 ::SafeArrayUnaccessData (fSafeArray);
227}
228
229const void* VariantArrayUnpacker::PeekAtData () const
230{
231 return fPtr;
232}
233
234VARTYPE VariantArrayUnpacker::GetArrayElementType () const
235{
236 AssertNotNull (fSafeArray);
237 VARTYPE vt = VT_EMPTY;
238 ThrowIfErrorHRESULT (::SafeArrayGetVartype (fSafeArray, &vt));
239 return vt;
240}
241
242size_t VariantArrayUnpacker::GetLength () const
243{
244 AssertNotNull (fSafeArray);
245 long lb = 0;
246 long ub = 0;
247 ThrowIfErrorHRESULT (::SafeArrayGetLBound (fSafeArray, 1, &lb));
248 ThrowIfErrorHRESULT (::SafeArrayGetUBound (fSafeArray, 1, &ub));
249 return ub - lb + 1;
250}
251#endif
252
253#if qStroika_Platform_Windows
254/*
255 ********************************************************************************
256 **************************** CreateSafeArrayOfBSTR *****************************
257 ********************************************************************************
258 */
259
260/*
261@METHOD: CreateSafeArrayOfBSTR
262@DESCRIPTION: <p>Overloaded method to create a VARIANT record containing a safe-array of BSTRs.</p>
263*/
264VARIANT Led::CreateSafeArrayOfBSTR (const wchar_t* const* strsStart, const wchar_t* const* strsEnd)
265{
266 size_t nElts = strsEnd - strsStart;
267 VARIANT result;
268 ::VariantInit (&result);
269 try {
270 const bool kEncodeAsVariant = false; // neither seems to work with javascript test code in MSIE, but both work
271 // with my C++-based unpacking code...
272 if (kEncodeAsVariant) {
273 VariantArrayPacker packer (&result, VT_VARIANT, nElts);
274 VARIANT* vptr = reinterpret_cast<VARIANT*> (packer.PokeAtData ());
275 for (size_t i = 0; i < nElts; ++i) {
276 ::VariantInit (&vptr[i]);
277 Execution::ThrowIfNull (vptr[i].bstrVal = ::SysAllocString (strsStart[i]));
278 vptr[i].vt = VT_VARIANT;
279 }
280 }
281 else {
282 VariantArrayPacker packer (&result, VT_BSTR, nElts);
283 BSTR* bptr = reinterpret_cast<BSTR*> (packer.PokeAtData ());
284 for (size_t i = 0; i < nElts; ++i) {
285 Execution::ThrowIfNull (bptr[i] = ::SysAllocString (strsStart[i]));
286 }
287 }
288 }
289 catch (...) {
290 ::VariantClear (&result);
291 throw;
292 }
293 return result;
294}
295
296VARIANT Led::CreateSafeArrayOfBSTR (const vector<const wchar_t*>& v)
297{
298 return CreateSafeArrayOfBSTR (Traversal::Iterator2Pointer (v.begin ()), Traversal::Iterator2Pointer (v.end ()));
299}
300
301VARIANT Led::CreateSafeArrayOfBSTR (const vector<wstring>& v)
302{
303 vector<const wchar_t*> tmp;
304 for (auto i = v.begin (); i != v.end (); ++i) {
305 // I hope (assure?) this doesn't create temporaries and take .c_str () of temporaries
306 tmp.push_back ((*i).c_str ());
307 }
308 return CreateSafeArrayOfBSTR (tmp);
309}
310
311/*
312 ********************************************************************************
313 ***************** UnpackVectorOfStringsFromVariantArray ************************
314 ********************************************************************************
315 */
316vector<wstring> Led::UnpackVectorOfStringsFromVariantArray (const VARIANT& v)
317{
318 vector<wstring> result;
319 VariantArrayUnpacker up (v);
320 size_t nElts = up.GetLength ();
321 switch (up.GetArrayElementType ()) {
322 case VT_BSTR: {
323 const BSTR* bArray = reinterpret_cast<const BSTR*> (up.PeekAtData ());
324 for (size_t i = 0; i < nElts; ++i) {
325 result.push_back (bArray[i]);
326 }
327 } break;
328 case VT_VARIANT: {
329 const VARIANT* vArray = reinterpret_cast<const VARIANT*> (up.PeekAtData ());
330 for (size_t i = 0; i < nElts; ++i) {
331 result.push_back (vArray[i].bstrVal);
332 }
333 } break;
334 default:
335 throw (DISP_E_BADVARTYPE);
336 }
337 return result;
338}
339#endif
340
341#if qStroika_Platform_Windows
342/*
343@METHOD: DumpSupportedInterfaces
344@DESCRIPTION: <p>@'qStroika_Platform_Windows' only</p>
345 <p>Helpful COM debugging utility which dumps to the debugger window all the interfaces
346 supported by a given COM object. The arguments 'objectName' and 'levelPrefix' can be nullptr (optional).
347 </p>
348*/
349void Led::DumpSupportedInterfaces (IUnknown* obj, const char* objectName, const char* levelPrefix)
350{
351 {
352 string label = objectName == nullptr ? string{} : Characters::CString::Format (" (named '%s')", objectName);
353 string msg = Characters::CString::Format ("Dumping interfaces for object%s at %p:\n", label.c_str (), obj);
354 ::OutputDebugStringA (msg.c_str ());
355
356 if (levelPrefix == nullptr) {
357 levelPrefix = "\t";
358 }
359 }
360
361 /*
362 * All interfaces are stored in the registry. Lookup each one, and do a query-interface
363 * with each on the given object to see what interfaces it contains.
364 */
365 HKEY interfaceKey = nullptr;
366 HKEY iKey = nullptr;
367 BSTR tmpStr = nullptr;
368 IUnknown* test = nullptr;
369 try {
370 if (::RegOpenKeyEx (HKEY_CLASSES_ROOT, _T ("Interface"), 0, KEY_READ, &interfaceKey) != ERROR_SUCCESS) {
371 throw GetLastError ();
372 }
373 for (DWORD i = 0;; ++i) {
374 char subKey[MAX_PATH];
375 DWORD subKeySize = sizeof (subKey);
376 if (::RegEnumKeyExA (interfaceKey, i, subKey, &subKeySize, 0, 0, 0, 0) == ERROR_SUCCESS) {
377 try {
378 CLSID iid;
379 (void)::memset (&iid, 0, sizeof (iid));
380 tmpStr = ::SysAllocString (String::FromNarrowSDKString (subKey).As<wstring> ().c_str ());
381 ThrowIfErrorHRESULT (::CLSIDFromString (tmpStr, &iid));
382 if (tmpStr != nullptr) {
383 ::SysFreeString (tmpStr);
384 tmpStr = nullptr;
385 }
386 if (SUCCEEDED (obj->QueryInterface (iid, reinterpret_cast<void**> (&test)))) {
387 // dump that obj interface
388 Assert (iKey == nullptr);
389 if (::RegOpenKeyExA (interfaceKey, subKey, 0, KEY_READ, &iKey) != ERROR_SUCCESS) {
390 throw GetLastError ();
391 }
392 DWORD dwType = 0;
393 TCHAR interfaceNameBuf[1024];
394 DWORD interfaceNameBufSize = sizeof (interfaceNameBuf);
395 if (::RegQueryValueEx (iKey, _T(""), nullptr, &dwType, reinterpret_cast<LPBYTE> (interfaceNameBuf),
396 &interfaceNameBufSize) == ERROR_SUCCESS) {
397 OutputDebugStringA (levelPrefix);
398 OutputDebugString (interfaceNameBuf);
399 OutputDebugStringA ("\n");
400 }
401 if (iKey != nullptr) {
402 ::CloseHandle (iKey);
403 iKey = nullptr;
404 }
405 }
406 if (test != nullptr) {
407 test->Release ();
408 test = nullptr;
409 }
410 }
411 catch (...) {
412 OutputDebugStringA (levelPrefix);
413 OutputDebugStringA ("ERROR LOOKING UP INTERFACE - IGNORING");
414 OutputDebugStringA ("\n");
415 }
416 }
417 else {
418 break; // end of list of interfaces?
419 }
420 }
421 if (test != nullptr) {
422 test->Release ();
423 test = nullptr;
424 }
425 if (tmpStr != nullptr) {
426 ::SysFreeString (tmpStr);
427 tmpStr = nullptr;
428 }
429 if (interfaceKey != nullptr) {
430 ::CloseHandle (interfaceKey);
431 interfaceKey = nullptr;
432 }
433 }
434 catch (...) {
435 if (iKey != nullptr) {
436 ::CloseHandle (iKey);
437 iKey = nullptr;
438 }
439 if (interfaceKey != nullptr) {
440 ::CloseHandle (interfaceKey);
441 interfaceKey = nullptr;
442 }
443 if (tmpStr != nullptr) {
444 ::SysFreeString (tmpStr);
445 tmpStr = nullptr;
446 }
447 if (test != nullptr) {
448 test->Release ();
449 test = nullptr;
450 }
451 }
452}
453
454/*
455@METHOD: DumpObjectsInIterator
456@DESCRIPTION: <p>@'qStroika_Platform_Windows' only</p>
457 <p>Helpful COM debugging utility which dumps to the debugger window all the subobjects of a given COM object,
458 along with the interfaces they supoport (see also @'DumpSupportedInterfaces'). The arguments
459 'iteratorName' and 'levelPrefix' can be nullptr (optional).
460 </p>
461*/
462void Led::DumpObjectsInIterator (IEnumUnknown* iter, const char* iteratorName, const char* levelPrefix)
463{
464 {
465 string label = iteratorName == nullptr ? string{} : Characters::CString::Format (" (named '%s')", iteratorName);
466 string msg = Characters::CString::Format ("Dumping objects (and their interface names) for iterator%s at %p\n", label.c_str (), iter);
467 ::OutputDebugStringA (msg.c_str ());
468 if (levelPrefix == nullptr) {
469 levelPrefix = "\t";
470 }
471 }
472 if (iter == nullptr) {
473 ::OutputDebugStringA (levelPrefix);
474 ::OutputDebugStringA ("<<nullptr ENUM POINTER>>\n"); // try DumpSupportedInterfaces to see what interfaces your enum interface supports!
475 return;
476 }
477 IUnknown* nextObj = nullptr;
478 for (size_t i = 0; SUCCEEDED (iter->Next (1, &nextObj, nullptr)); ++i) {
479 char nameBuf[1024];
480 (void)snprintf (nameBuf, std::size (nameBuf), "obj#%d", static_cast<int> (i));
481 char levelPrefixBuf[1024];
482 Assert (::strlen (levelPrefix) < sizeof (levelPrefixBuf) / 2); // assert MUCH less
483 Characters::CString::Copy (levelPrefixBuf, std::size (levelPrefixBuf), levelPrefix);
484 CString::Cat (levelPrefixBuf, std::size (levelPrefixBuf), "\t");
485 DumpSupportedInterfaces (nextObj, nameBuf, levelPrefixBuf);
486 AssertNotNull (nextObj);
487 nextObj->Release ();
488 nextObj = nullptr;
489 }
490 Assert (nextObj == nullptr);
491}
492#endif
493
494/*
495 ********************************************************************************
496 *************************************** Led_URLD *******************************
497 ********************************************************************************
498 */
499Led_URLD::Led_URLD (const char* url, const char* title)
500 : fData ()
501{
502 RequireNotNull (url);
503 RequireNotNull (title);
504 size_t urlLen = ::strlen (url);
505 size_t titleLen = ::strlen (title);
506
507 size_t resultURLDLen = (urlLen + 1 + titleLen + 1);
508 fData.resize (resultURLDLen, '\0');
509
510 char* data = &fData.front ();
511 Characters::CString::Copy (data, resultURLDLen, url);
512 CString::Cat (data, fData.size () - 1, "\r");
513 CString::Cat (data, fData.size () - 1, title);
514}
515
516Led_URLD::Led_URLD (const void* urlData, size_t nBytes)
517 : fData ()
518{
519 // assure a validly formatted Led_URLD - regardless of input
520 for (size_t i = 0; i < nBytes; i++) {
521 char c = ((char*)urlData)[i];
522 fData.push_back (c);
523 if (c == '\0') {
524 break;
525 }
526 }
527 // make sure NUL-terminated...
528 if (fData.size () == 0 or fData.back () != '\0') {
529 fData.push_back ('\0');
530 }
531}
532
533size_t Led_URLD::GetURLDLength () const
534{
535 // Note we DONT count the terminating NUL - cuz Netscape 2.0 doesn't appear to. And thats our reference.
536 return fData.size () - 1;
537}
538
539size_t Led_URLD::GetURLLength () const
540{
541 size_t len = fData.size ();
542 for (size_t i = 0; i < len; i++) {
543 if (fData[i] == '\r') {
544 return i;
545 }
546 }
547 return len - 1;
548}
549
550size_t Led_URLD::GetTitleLength () const
551{
552 size_t len = fData.size ();
553 for (size_t i = 0; i < len; i++) {
554 if (fData[i] == '\r') {
555 Assert (len >= (i + 1 + 1)); // cuz must be NUL-term & skip \r
556 return len - (i + 1 + 1); // ditto
557 }
558 }
559 return 0;
560}
561
562char* Led_URLD::PeekAtURLD () const
563{
564 return (&const_cast<Led_URLD*> (this)->fData.front ());
565}
566
567char* Led_URLD::PeekAtURL () const
568{
569 return (&const_cast<Led_URLD*> (this)->fData.front ());
570}
571
572char* Led_URLD::PeekAtTitle () const
573{
574 return PeekAtURL () + GetURLLength () + 1; // skip URL and '\r'
575}
576
577string Led_URLD::GetTitle () const
578{
579 return string{PeekAtTitle (), GetTitleLength ()};
580}
581
582string Led_URLD::GetURL () const
583{
584 return string{PeekAtURL (), GetURLLength ()};
585}
586
587/*
588 ********************************************************************************
589 ********************************* Led_URLManager *******************************
590 ********************************************************************************
591 */
592namespace {
593 Led_URLManager* sTheURLMgr_;
594}
595Led_URLManager& Led_URLManager::Get ()
596{
597 if (sTheURLMgr_ == nullptr) {
598 sTheURLMgr_ = new Led_URLManager ();
599 }
600 return *sTheURLMgr_;
601}
602
603void Led_URLManager::Set (Led_URLManager* newURLMgr)
604{
605 if (sTheURLMgr_ != newURLMgr) {
606 delete sTheURLMgr_;
607 sTheURLMgr_ = newURLMgr;
608 }
609}
610
611void Led_URLManager::Open (const string& url)
612{
613#if qStroika_Platform_Windows
614#if qStroika_Frameworks_Led_UseActiveXToOpenURLs
615 Open_ActiveX (url);
616#endif
617#endif
618}
619
620string Led_URLManager::FileSpecToURL ([[maybe_unused]] const filesystem::path& p)
621{
622 AssertNotImplemented (); // nyi (not needed anywhere right now)
623 return "";
624}
625
626#if qStroika_Frameworks_Led_UseActiveXToOpenURLs
627void Led_URLManager::Open_ActiveX (const string& url)
628{
629 Memory::StackBuffer<wchar_t> wideURLBuf{Memory::eUninitialized, url.length () + 1};
630 {
631 int nWideChars = ::MultiByteToWideChar (CP_ACP, 0, url.c_str (), static_cast<int> (url.length ()), wideURLBuf.data (),
632 static_cast<int> (url.length ()));
633 wideURLBuf[nWideChars] = '\0';
634 }
635 IUnknown* pUnk = nullptr; // implies we are an OLE-ignorant app. But I'm not sure what else I'm supposed to pass here!
636
637/*
638 * Use LoadLibrary/GetProcAddress instead of direct call to avoid having to link with
639 * urlmon.lib. This avoidance allows us to run on systems that don't have MSIE installed.
640 *
641 * Personally, I think its pretty weak MS doesn't offer a simpler way to deal with this problem.
642 * (or if they do, their docs are weak).
643 * --LGP 961015
644 */
645#if 1
646 static HINSTANCE urlmonLibrary = ::LoadLibrary (_T ("urlmon"));
647 if (urlmonLibrary == nullptr) {
648 Execution::Throw (bad_alloc{});
649 }
650 static HRESULT (WINAPI * hlinkNavigateString) (IUnknown*, LPCWSTR) =
651 (HRESULT (WINAPI*) (IUnknown*, LPCWSTR))::GetProcAddress (urlmonLibrary, "HlinkNavigateString");
652 if (hlinkNavigateString == nullptr) {
653 Execution::Throw (bad_alloc{});
654 }
655 HRESULT result = (*hlinkNavigateString) (pUnk, wideURLBuf.data ());
656#else
657 HRESULT result = ::HlinkNavigateString (pUnk, wideURLBuf.data ());
658#endif
659 if (not SUCCEEDED (result)) {
660 throw result;
661 }
662}
663#endif
664
665/*
666 ********************************************************************************
667 ********************** Led::MakeSophistsAppNameVersionURL **********************
668 ********************************************************************************
669 */
670string Led::MakeSophistsAppNameVersionURL (const string& relURL, const string& appName, const string& extraArgs)
671{
672 Require (relURL.length () > 0 and relURL[0] == '/');
673 char fullVersionBuf[1024];
674 (void)snprintf (fullVersionBuf, std::size (fullVersionBuf), "%d", qStroika_Version_FullVersion);
675 string fullURL = "http://www.sophists.com" + relURL + "?AppName=" + appName +
676#if qStroika_Platform_Windows
677 string{"&Platform=Windows"} +
678#endif
679 "&MajorMinorVersion=" + qStroika_Version_MajorMinorVersionString + "&LedFullVersion=" + fullVersionBuf +
680 "&ShortVersionString=" + qStroika_Version_ShortVersionString + extraArgs;
681 return fullURL;
682}
683
684/*
685 ********************************************************************************
686 ******************************* Led_CasedCharsEqual ****************************
687 ********************************************************************************
688 */
689bool Led::Led_CasedCharsEqual (char lhs, char rhs, bool ignoreCase)
690{
691 if (lhs == rhs) {
692 return true;
693 }
694 if (ignoreCase and isascii (lhs) and isalpha (lhs) and isascii (rhs) and isalpha (rhs) and toupper (lhs) == toupper (rhs)) {
695 return true;
696 }
697 return false;
698}
699
700CompileTimeFlagChecker_SOURCE (Stroika::Frameworks::Led, qStroika_Frameworks_Led_ProvideIMESupport, qStroika_Frameworks_Led_ProvideIMESupport);
#define AssertNotNull(p)
Definition Assertions.h:336
#define AssertNotImplemented()
Definition Assertions.h:404
#define RequireNotNull(p)
Definition Assertions.h:350
#define AssertNotReached()
Definition Assertions.h:358
#define CompileTimeFlagChecker_SOURCE(NS_PREFIX, NAME, VALUE)
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
Definition CodeCvt.h:118
nonvirtual STRINGISH Bytes2String(span< const byte > from) const
nonvirtual BLOBISH String2Bytes(span< const CHAR_T > from) const
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual String SafeSubString(SZ from) const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString
Definition SDKString.h:38