• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ParadoxGameConverters / commonItems / 12846535547

18 Jan 2025 06:41PM UTC coverage: 75.846% (-1.7%) from 77.556%
12846535547

Pull #274

github

web-flow
Merge 8b9494010 into b007cb890
Pull Request #274: Eliminate warnings

279 of 358 new or added lines in 13 files covered. (77.93%)

38 existing lines in 2 files now uncovered.

1837 of 2422 relevant lines covered (75.85%)

240.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/LinuxUtils.cpp
1
#include "Log.h"
2
#include "OSCompatibilityLayer.h"
3
#include <algorithm>
4
#include <cerrno>
5
#include <cstdarg>
6
#include <cstdlib>
7
#include <cstring>
8
#include <dirent.h>
9
#include <fcntl.h>
10
#include <filesystem>
11
#include <iconv.h>
12
#include <iostream>
13
#include <set>
14
#include <sys/sendfile.h>
15
#include <sys/stat.h>
16
#include <unistd.h>
17
#include <vector>
18

19

20
namespace fs = std::filesystem;
21

22

23

24
void sprintf_s_Linux(char* __restrict __s, size_t __maxlength, const char* __restrict __format, ...)
×
25
{
26
        va_list arg_ptr;
27
        va_start(arg_ptr, __format);
×
28
        snprintf(__s, __maxlength, __format, arg_ptr);
×
29
        va_end(arg_ptr);
×
30
}
×
31

32

33
void strcpy_s_Linux(char* __restrict __dest, const char* __restrict __src)
×
34
{
35
        strcpy(__dest, __src);
×
36
}
×
37

38

39
int fopen_s_Linux(FILE** file, const char* filename, const char* mode)
×
40
{
41
        *file = fopen(filename, mode);
×
42
        return *file == NULL;
×
43
}
44

45

46
void fprintf_s_Linux(FILE* file, const char* format, ...)
×
47
{
48
        va_list arg_ptr;
49
        va_start(arg_ptr, format);
×
50
        fprintf(file, format, arg_ptr);
×
51
        va_end(arg_ptr);
×
52
}
×
53

54
#ifdef __linux__
55
HANDLE GetStdHandle(int nothing)
×
56
{
57
        return 1;
×
58
}
59
#endif
60

61
namespace commonItems
62
{
63

64
/*
65
        Helper function to determine that returns false if the filename is a:
66
        - a hidden folder or folder (starting with '.')
67
        - empty
68
        - a parent directory ('.')
69
        - the current directory ('..')
70
*/
71
bool IsRegularNodeName(const std::string& name)
×
72
{
73
        return !name.empty() && *name.begin() != '.';
×
74
}
75

76

77
/*
78
                returns false if a node name is empty or a placeholder for the current or parent directory, true otherwise
79
*/
80
bool IsActualNodeName(const std::string& name)
×
81
{
82
        for (auto i = name.begin(); i != name.end(); ++i)
×
83
        {
84
                if (*i != '.')
×
85
                {
86
                        return true;
×
87
                }
88
        }
89
        return false;
×
90
}
91

92

93
const char* StripTrailingSeparators(const char* path, std::size_t length)
×
94
{
95
        switch (length)
×
96
        {
97
                case 0:
×
98
                        return path;
×
99
                case 1:
×
100
                        if (*path != '/')
×
101
                        {
102
                                ++path;
×
103
                        }
104
                        return path;
×
105
                default:
×
106
                        const char* i = path + length - 1;
×
107
                        while (i != path && *i == '/')
×
108
                        {
109
                                --i;
×
110
                        }
111
                        ++i;
×
112
                        return i;
×
113
        }
114
}
115

116
const char* StripLeadingSeparators(const char* path)
×
117
{
118
        while (true)
119
        {
120
                if (*path != '/')
×
121
                {
122
                        return path;
×
123
                }
124
                ++path;
×
125
        }
126
}
127

128

129
/*
130
        Concatenates two paths and strips out leading and trailing '/'
131
*/
132
std::string ConcatenatePaths(const std::string& first, const std::string& second)
×
133
{
134
        const char* first_begin = first.c_str();
×
135
        const char* first_end = StripTrailingSeparators(first_begin, first.length());
×
136
        const char* second_begin = StripLeadingSeparators(second.c_str());
×
137
        return std::string(first_begin, first_end) + "/" + std::string(second_begin);
×
138
}
139

140
/*
141
        Concatenates a path and a valid node name
142
        A valid node name is a non empty string that does not contain '/'
143
        use ConcatenatePaths for other concatenations
144
*/
145
std::string ConcatenateNodeName(const std::string& path, const std::string& name)
×
146
{
147
        const char* path_begin = path.c_str();
×
148
        const char* path_end = StripTrailingSeparators(path_begin, path.length());
×
149
        return std::string(path_begin, path_end) + "/" + name;
×
150
}
151

152
/*
153
        Splits the node name from the rest of the path and returns both values as a pair (path, node_name)
154
*/
155
std::pair<std::string, std::string> SplitNodeNameFromPath(const std::string& path)
×
156
{
157
        if (path.empty())
×
158
        {
159
                return std::make_pair(std::string(), std::string());
×
160
        }
161
        const char* buffer = path.c_str();
×
162
        const char* end = buffer + path.length() - 1;
×
163
        while (end != buffer && *end == '/')
×
164
        {
165
                --end;
×
166
        }
167
        if (end == buffer)
×
168
        {
169
                return std::make_pair(std::string(buffer, buffer + 1), std::string());
×
170
        }
171
        const char* begin = end;
×
172
        ++end;
×
173
        while (begin != buffer && *begin != '/')
×
174
        {
175
                --begin;
×
176
        }
177
        if (*begin == '/')
×
178
        {
179
                ++begin;
×
180
        }
181

182
        return std::make_pair(std::string(buffer, begin), std::string(begin, end));
×
183
}
184

185
bool IsLinuxPathElementSeparator(char c)
×
186
{
187
        return c == '/';
×
188
}
189

190
bool isLinuxPathCharacter(char c)
×
191
{
192
        return c != '/';
×
193
}
194

195
char* CopyFolderPathElement(const char*& input_begin, const char* input_end, char* output)
×
196
{
197
        while (input_begin != input_end)
×
198
        {
199
                const char c = *input_begin;
×
200
                ++input_begin;
×
201
                if (c == '/')
×
202
                {
203
                        return output;
×
204
                }
205

206
                *output = c;
×
207
                ++output;
×
208
        }
209
        return output;
×
210
}
211

212

213

214
bool GetFileMode(const std::string& path, mode_t& result)
×
215
{
216
        struct stat status;
217
        if (stat(path.c_str(), &status) != 0)
×
218
        {
219
                return false;
×
220
        }
221

222
        result = status.st_mode;
×
223
        return true;
×
224
}
225

226
bool IsRegularFile(const std::string& path)
×
227
{
228
        return !fs::is_directory(fs::u8path(path));
×
229
}
230

231
bool IsDirectory(const std::string& path)
×
232
{
233
        return fs::is_directory(fs::u8path(path));
×
234
}
235

236

237
/*
238
        Note: since the function signature did not allow for a return value, it clears the fileNames set when an
239
        error occurs to make sure no operations are done on an incomplete list of files
240
*/
UNCOV
241
std::set<std::string> GetAllFilesInFolderRecursive(const std::string& path)
×
242
{
UNCOV
243
        auto validatedPath = path;
×
UNCOV
244
        if (validatedPath.ends_with('/') || validatedPath.ends_with('\\'))
×
UNCOV
245
                validatedPath = validatedPath.substr(0, validatedPath.size() - 1); // remove the trailing slash
×
UNCOV
246
        const auto origPathStr = fs::u8path(validatedPath).native();
×
247

UNCOV
248
        if (const auto tempPath = fs::u8path(path); !fs::exists(tempPath) || !fs::is_directory(tempPath))
×
249
        {
UNCOV
250
                return {};
×
UNCOV
251
        }
×
252

UNCOV
253
        std::set<std::string> fileNames;
×
UNCOV
254
        for (const auto& p: fs::recursive_directory_iterator(fs::u8path(path)))
×
255
        {
UNCOV
256
                if (!p.is_directory())
×
257
                {
UNCOV
258
                        const auto currentPath = p.path().native();
×
259

UNCOV
260
                        const auto requestedPath = currentPath.substr(origPathStr.length() + 1, currentPath.length() - origPathStr.length() - 1);
×
UNCOV
261
                        fileNames.insert(requestedPath);
×
UNCOV
262
                }
×
UNCOV
263
        }
×
UNCOV
264
        return fileNames;
×
UNCOV
265
}
×
266

267

268
void WriteToConsole(LogLevel level, const std::string& logMessage)
×
269
{
270
        if (level != LogLevel::Debug) // Don't log debug messages to console.
×
271
        {
272
                std::cout << logMessage;
×
273
        }
274
}
×
275

276
/*
277
        Implemented all messages for error codes used in LinuxUtils.cpp.
278
*/
279
std::string GetLastErrorString()
×
280
{
281
        return strerror(errno);
×
282
}
283

284
bool deleteFile(const std::string& unresolvedFile)
×
285
{
286
        fs::remove(fs::u8path(unresolvedFile));
×
287
        return true;
×
288
}
289

290

291
/*
292
 * Forward declarations for conversion helper function and types
293
 */
294
class ConversionInputBuffer;
295

296
class ConversionOutputBuffer;
297

298
bool ConvertString(const char* toCode, const char* fromCode, ConversionInputBuffer& in, ConversionOutputBuffer& out);
299

300
/*
301
 * helper class to copy buffer from string and keep track of remainder and current position
302
 */
303
class ConversionInputBuffer
304
{
305
        char* data;
306
        char* in_buffer;
307
        std::size_t remainder;
308

309
  public:
310
        explicit ConversionInputBuffer(const std::string& input): data(new char[input.length()]), in_buffer(data), remainder(input.length())
×
311
        {
312
                // POSIX iconv expects a pointer to char *, not to const char * and consequently does not guarantee that the input
313
                // sequence is not modified so we copy it into the buffer before attempting conversion
314
                std::copy(input.begin(), input.end(), data);
×
315
        }
×
316
        ConversionInputBuffer(const ConversionInputBuffer&) = delete;
317
        ConversionInputBuffer& operator=(const ConversionInputBuffer&) = delete;
318
        ConversionInputBuffer(ConversionInputBuffer&&) = delete;
319
        ConversionInputBuffer& operator=(ConversionInputBuffer&&) = delete;
320

321
        template <typename String> explicit ConversionInputBuffer(const String& input)
×
322
        {
323
                typedef typename String::value_type Char;
324
                remainder = sizeof(Char) * input.length();
×
325
                data = new char[remainder];
×
326
                in_buffer = data;
×
327
                const auto* const input_str = reinterpret_cast<const char*>(input.c_str());
×
328
                std::copy(input_str, input_str + remainder, data);
×
329
        }
×
330

331
        ~ConversionInputBuffer() { delete[] data; }
×
332

333
        [[nodiscard]] bool has_remaining_bytes() const { return remainder != 0; }
×
334

335
        friend bool ConvertBuffer(const char* fromCode, const char* toCode, ConversionInputBuffer& from, ConversionOutputBuffer& to);
336
};
337

338
/*
339
 * helper class to manage buffer pointers and sizes while providing access to internal buffers for use in iconv
340
 * functions
341
 */
342
class ConversionOutputBuffer
343
{
344
        std::size_t size;
345
        std::size_t remainder;
346
        std::size_t block_size;
347
        char* data = nullptr;
348
        char* out_buffer = nullptr;
349

350
        template <typename String> struct OutputStrHelper
351
        {
352
                typedef typename String::value_type Char;
353

354
                static void str(String& output, const char* buffer, std::size_t length)
×
355
                {
356
                        size_t output_length = length / sizeof(Char);
×
357
                        wchar_t output_buffer[output_length];
×
358
                        memset(output_buffer, 0, output_length * sizeof(wchar_t));
×
359
                        std::mbstowcs(output_buffer, buffer, output_length);
×
360
                        output.assign(output_buffer, output_length);
×
361
                }
×
362
        };
363

364
        template <typename Traits, typename Alloc> struct OutputStrHelper<std::basic_string<char, Traits, Alloc>>
365
        {
366
                static void str(std::basic_string<char, Traits, Alloc>& output, char* buffer, std::size_t length) { output.assign(buffer, length); }
×
367
        };
368

369
  public:
370
        explicit ConversionOutputBuffer(std::size_t initial_size = 0, std::size_t increment_block_size = 1024 * 1024):
×
371
                 size(initial_size), remainder(initial_size), block_size(increment_block_size)
×
372
        {
373
                if (size != 0)
×
374
                {
375
                        data = new char[size];
×
376
                        out_buffer = data;
×
377
                }
378
        }
×
379
        ConversionOutputBuffer(const ConversionOutputBuffer&) = delete;
380
        ConversionOutputBuffer& operator=(const ConversionOutputBuffer&) = delete;
381
        ConversionOutputBuffer(ConversionOutputBuffer&&) = delete;
382
        ConversionOutputBuffer& operator=(ConversionOutputBuffer&&) = delete;
383

384
        ~ConversionOutputBuffer() { delete[] data; }
×
385

386
        [[nodiscard]] bool full() const { return remainder != 0; }
387

388
        [[nodiscard]] size_t length() const { return size - remainder; }
×
389

390
        [[nodiscard]] std::string str() const { return std::string(data, data + length()); }
391

392
        template <typename String> void str(String& output) const { OutputStrHelper<String>::str(output, data, length()); }
×
393

394
        bool ensure_capacity(std::size_t capacity)
×
395
        {
396
                if (size < capacity)
×
397
                {
398
                        const size_t len = length();
×
399
                        char* new_data = new char[capacity];
×
400
                        std::copy(data, data + len, new_data);
×
401
                        delete[] data;
×
402
                        data = new_data;
×
403
                        out_buffer = data + len;
×
404
                        remainder += capacity - size;
×
405
                        size = capacity;
×
406
                        return true;
×
407
                }
408

409
                return false;
×
410
        }
411

412
        bool grow() { return ensure_capacity(size + block_size); }
×
413

414
        friend bool ConvertBuffer(const char* fromCode, const char* toCode, ConversionInputBuffer& from, ConversionOutputBuffer& to);
415
};
416

417
bool ConvertBuffer(const char* fromCode, const char* toCode, ConversionInputBuffer& from, ConversionOutputBuffer& to)
×
418
{
419
        iconv_t descriptor = iconv_open(toCode, fromCode);
×
420
        if (descriptor == reinterpret_cast<iconv_t>(-1))
×
421
        {
422
                LOG(LogLevel::Error) << "unable to recode string from '" << fromCode << "' to '" << toCode << ": not supported on this system";
×
423
                return false;
×
424
        }
425
        while (from.has_remaining_bytes())
×
426
        {
427
                if (iconv(descriptor, &from.in_buffer, &from.remainder, &to.out_buffer, &to.remainder) == static_cast<size_t>(-1))
×
428
                {
429
                        switch (errno)
×
430
                        {
431
                                case E2BIG:
×
432
                                        to.grow();
×
433
                                        break;
×
434
                                case EILSEQ:
×
435
                                        LOG(LogLevel::Error) << "invalid input sequence encountered during conversion of " << from.data << " from " << fromCode << " to " << toCode
×
436
                                                                                                << " : " << std::hex << *reinterpret_cast<uint16_t*>(from.in_buffer);
×
437
                                        return false;
×
438
                                case EINVAL:
×
439
                                        LOG(LogLevel::Error) << "incomplete input sequence encountered during conversion from " << fromCode << " to " << toCode;
×
440
                                        return false;
×
441
                                default:
×
442
                                        break;
×
443
                        }
444
                }
445
        }
446
        iconv_close(descriptor);
×
447
        return true;
×
448
}
449

450
/*
451
 * InputString and OutputString should be instantiations of basic_string, or have a similar API
452
 * InputString should implement:
453
 * member type value_type
454
 * member function value_type begin() const
455
 * member function value_type end() const
456
 * member function size_t length() const
457
 *
458
 */
459
template <typename InputString, typename OutputString>
460
bool ConvertString(const char* fromCode, const char* toCode, const InputString& from, OutputString& to, std::size_t to_buffer_size = 0)
×
461
{
462
        if (to_buffer_size == 0)
×
463
        {
464
                to_buffer_size = from.length();
×
465
        }
466
        ConversionInputBuffer from_buffer(from);
×
467
        ConversionOutputBuffer to_buffer(to_buffer_size);
×
468
        if (ConvertBuffer(fromCode, toCode, from_buffer, to_buffer))
×
469
        {
470
                to_buffer.str(to);
×
471
                return true;
×
472
        }
473

474
        return false;
×
475
}
×
476

477
template <typename InputString, typename OutputString>
478
OutputString ConvertString(const char* fromCode, const char* toCode, const InputString& from, std::size_t to_buffer_size = 0)
×
479
{
480
        OutputString to;
×
481
        ConvertString(fromCode, toCode, from, to, to_buffer_size);
×
482
        return to;
×
483
}
×
484

485
std::string convertUTF8ToASCII(const std::string& UTF8)
×
486
{
487
        return ConvertString<std::string, std::string>("UTF-8", "ASCII//TRANSLIT", UTF8);
×
488
}
489

490
std::string convertUTF8To8859_15(const std::string& UTF8)
×
491
{
492
        return ConvertString<std::string, std::string>("UTF−8", "ISO−8859−15//TRANSLIT", UTF8);
×
493
}
494

495

496
std::string convertUTF8ToWin1252(const std::string& UTF8)
×
497
{
498
        return ConvertString<std::string, std::string>("UTF−8", "CP1252//TRANSLIT", UTF8);
×
499
}
500

501
std::string convertUTF8ToWin1251(const std::string& UTF8)
×
502
{
503
        return ConvertString<std::string, std::string>("UTF−8", "CP1251//TRANSLIT", UTF8);
×
504
}
505

506
std::string convertUTF8ToWin1250(const std::string& UTF8)
×
507
{
508
        return ConvertString<std::string, std::string>("UTF−8", "CP1250//TRANSLIT", UTF8);
×
509
}
510

511
std::string convert8859_15ToUTF8(const std::string& input)
×
512
{
513
        return ConvertString<std::string, std::string>("ISO−8859−15", "UTF-8//TRANSLIT", input);
×
514
}
515

516
/*
517
        Warning: Does not actually return an UTF-16 sequence.
518
        This is an implementation of the original Windows-based API which uses UTF-16 LE as the system dependent wchar_t
519
        encoding This behaviour is replicated on Linux but it uses the (system dependent) wchar_t encoding.
520
*/
521
std::wstring convert8859_15ToUTF16(const std::string& input)
×
522
{
523
        return ConvertString<std::string, std::wstring>("ISO−8859−15", "wchar_t//TRANSLIT", input);
×
524
}
525

526
std::string convertWin1250ToUTF8(const std::string& Win1250)
×
527
{
528
        return ConvertString<std::string, std::string>("CP1250", "UTF-8//TRANSLIT", Win1250);
×
529
}
530

531
std::string convertWin1251ToUTF8(const std::string& Win1251)
×
532
{
533
        return ConvertString<std::string, std::string>("CP1251", "UTF-8//TRANSLIT", Win1251);
×
534
}
535

536
std::string convertWin1252ToUTF8(const std::string& Win1252)
×
537
{
538
        return ConvertString<std::string, std::string>("CP1252", "UTF-8//TRANSLIT", Win1252);
×
539
}
540

541

542
std::wstring convertWin1252ToUTF16(const std::string& Win1252)
×
543
{
544
        return ConvertString<std::string, std::wstring>("CP1252", "wchar_t//TRANSLIT", Win1252);
×
545
}
546

547
/*
548
        Warning: Does not actually return an UTF-16 sequence.
549
        This is an implementation of the original Windows-based API which uses UTF-16 LE as the system dependent wchar_t
550
        encoding This behaviour is replicated on Linux but it uses the (system dependent) wchar_t encoding.
551
*/
552
std::wstring convertUTF8ToUTF16(const std::string& UTF8)
×
553
{
554
        std::vector<unsigned long> unicode;
×
555
        for (size_t i = 0; i < UTF8.size();)
×
556
        {
557
                uint32_t uni = '\0';
×
558
                size_t todo = 0;
×
559
                unsigned char ch = UTF8[i++];
×
560
                if (ch <= 0x7F)
×
561
                {
562
                        uni = ch;
×
563
                        todo = 0;
×
564
                }
565
                else if (ch <= 0xBF)
×
566
                {
567
                        throw std::logic_error("not a UTF-8 string");
×
568
                }
569
                else if (ch <= 0xDF)
×
570
                {
571
                        uni = ch & 0x1F;
×
572
                        todo = 1;
×
573
                }
574
                else if (ch <= 0xEF)
×
575
                {
576
                        uni = ch & 0x0F;
×
577
                        todo = 2;
×
578
                }
579
                else if (ch <= 0xF7)
×
580
                {
581
                        uni = ch & 0x07;
×
582
                        todo = 3;
×
583
                }
584
                else
585
                {
586
                        throw std::logic_error("not a UTF-8 string");
×
587
                }
588
                for (size_t j = 0; j < todo; ++j)
×
589
                {
590
                        if (i == UTF8.size())
×
591
                                throw std::logic_error("not a UTF-8 string");
×
592
                        ch = UTF8[i++];
×
593
                        if (ch < 0x80 || ch > 0xBF)
×
594
                                throw std::logic_error("not a UTF-8 string");
×
595
                        uni <<= 6;
×
596
                        uni += ch & 0x3F;
×
597
                }
598
                if (uni >= 0xD800 && uni <= 0xDFFF)
×
599
                        throw std::logic_error("not a UTF-8 string");
×
600
                if (uni > 0x10FFFF)
×
601
                        throw std::logic_error("not a UTF-8 string");
×
602
                unicode.push_back(uni);
×
603
        }
604
        std::wstring utf16;
×
605
        for (size_t i = 0; i < unicode.size(); ++i)
×
606
        {
607
                unsigned long uni = unicode[i];
×
608
                if (uni <= 0xFFFF)
×
609
                {
610
                        utf16 += static_cast<wchar_t>(uni);
×
611
                }
612
                else
613
                {
614
                        uni -= 0x10000;
×
615
                        utf16 += static_cast<wchar_t>((uni >> 10) + 0xD800);
×
616
                        utf16 += static_cast<wchar_t>((uni & 0x3FF) + 0xDC00);
×
617
                }
618
        }
619
        return utf16;
×
620
}
×
621

622

623
std::string convertToUTF8(const std::wstring& input)
×
624
{
625
        return ConvertString<std::wstring, std::string>("wchar_t", "UTF-8//TRANSLIT", input);
×
626
}
627

628

NEW
629
std::string utf16_to_utf8(const std::u16string& utf16_string)
×
630
{
NEW
631
        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conversion;
×
NEW
632
        return conversion.to_bytes(utf16_string);
×
NEW
633
}
×
634

635

NEW
636
std::string UTF16ToUTF8(const std::wstring& UTF16)
×
637
{
NEW
638
        const std::u16string u16str(UTF16.begin(), UTF16.end());
×
NEW
639
        return utf16_to_utf8(u16str);
×
NEW
640
}
×
641

642

NEW
643
std::optional<std::filesystem::path> getSteamInstallPath(const std::string& steamID)
×
644
{
645
        // TODO: Write some actual code for this when able.
646
        return std::nullopt;
×
647
}
648

649
} // namespace commonItems
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc