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

ParadoxGameConverters / commonItems / 15655744459

14 Jun 2025 08:17PM UTC coverage: 78.62% (+2.6%) from 75.975%
15655744459

Pull #286

github

web-flow
Merge 666ba10a6 into fd3186d2c
Pull Request #286: Remove deprecated functions.

1721 of 2189 relevant lines covered (78.62%)

244.39 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
void WriteToConsole(LogLevel level, const std::string& logMessage)
×
238
{
239
        if (level != LogLevel::Debug) // Don't log debug messages to console.
×
240
        {
241
                std::cout << logMessage;
×
242
        }
243
}
×
244

245
/*
246
        Implemented all messages for error codes used in LinuxUtils.cpp.
247
*/
248
std::string GetLastErrorString()
×
249
{
250
        return strerror(errno);
×
251
}
252

253
bool deleteFile(const std::string& unresolvedFile)
×
254
{
255
        fs::remove(fs::u8path(unresolvedFile));
×
256
        return true;
×
257
}
258

259

260
/*
261
 * Forward declarations for conversion helper function and types
262
 */
263
class ConversionInputBuffer;
264

265
class ConversionOutputBuffer;
266

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

269
/*
270
 * helper class to copy buffer from string and keep track of remainder and current position
271
 */
272
class ConversionInputBuffer
273
{
274
        char* data;
275
        char* in_buffer;
276
        std::size_t remainder;
277

278
  public:
279
        explicit ConversionInputBuffer(const std::string& input): data(new char[input.length()]), in_buffer(data), remainder(input.length())
×
280
        {
281
                // POSIX iconv expects a pointer to char *, not to const char * and consequently does not guarantee that the input
282
                // sequence is not modified so we copy it into the buffer before attempting conversion
283
                std::copy(input.begin(), input.end(), data);
×
284
        }
×
285
        ConversionInputBuffer(const ConversionInputBuffer&) = delete;
286
        ConversionInputBuffer& operator=(const ConversionInputBuffer&) = delete;
287
        ConversionInputBuffer(ConversionInputBuffer&&) = delete;
288
        ConversionInputBuffer& operator=(ConversionInputBuffer&&) = delete;
289

290
        template <typename String> explicit ConversionInputBuffer(const String& input)
×
291
        {
292
                typedef typename String::value_type Char;
293
                remainder = sizeof(Char) * input.length();
×
294
                data = new char[remainder];
×
295
                in_buffer = data;
×
296
                const auto* const input_str = reinterpret_cast<const char*>(input.c_str());
×
297
                std::copy(input_str, input_str + remainder, data);
×
298
        }
×
299

300
        ~ConversionInputBuffer() { delete[] data; }
×
301

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

304
        friend bool ConvertBuffer(const char* fromCode, const char* toCode, ConversionInputBuffer& from, ConversionOutputBuffer& to);
305
};
306

307
/*
308
 * helper class to manage buffer pointers and sizes while providing access to internal buffers for use in iconv
309
 * functions
310
 */
311
class ConversionOutputBuffer
312
{
313
        std::size_t size;
314
        std::size_t remainder;
315
        std::size_t block_size;
316
        char* data = nullptr;
317
        char* out_buffer = nullptr;
318

319
        template <typename String> struct OutputStrHelper
320
        {
321
                typedef typename String::value_type Char;
322

323
                static void str(String& output, const char* buffer, std::size_t length)
×
324
                {
325
                        size_t output_length = length / sizeof(Char);
×
326
                        wchar_t output_buffer[output_length];
×
327
                        memset(output_buffer, 0, output_length * sizeof(wchar_t));
×
328
                        std::mbstowcs(output_buffer, buffer, output_length);
×
329
                        output.assign(output_buffer, output_length);
×
330
                }
×
331
        };
332

333
        template <typename Traits, typename Alloc> struct OutputStrHelper<std::basic_string<char, Traits, Alloc>>
334
        {
335
                static void str(std::basic_string<char, Traits, Alloc>& output, char* buffer, std::size_t length) { output.assign(buffer, length); }
×
336
        };
337

338
  public:
339
        explicit ConversionOutputBuffer(std::size_t initial_size = 0, std::size_t increment_block_size = 1024 * 1024):
×
340
                 size(initial_size), remainder(initial_size), block_size(increment_block_size)
×
341
        {
342
                if (size != 0)
×
343
                {
344
                        data = new char[size];
×
345
                        out_buffer = data;
×
346
                }
347
        }
×
348
        ConversionOutputBuffer(const ConversionOutputBuffer&) = delete;
349
        ConversionOutputBuffer& operator=(const ConversionOutputBuffer&) = delete;
350
        ConversionOutputBuffer(ConversionOutputBuffer&&) = delete;
351
        ConversionOutputBuffer& operator=(ConversionOutputBuffer&&) = delete;
352

353
        ~ConversionOutputBuffer() { delete[] data; }
×
354

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

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

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

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

363
        bool ensure_capacity(std::size_t capacity)
×
364
        {
365
                if (size < capacity)
×
366
                {
367
                        const size_t len = length();
×
368
                        char* new_data = new char[capacity];
×
369
                        std::copy(data, data + len, new_data);
×
370
                        delete[] data;
×
371
                        data = new_data;
×
372
                        out_buffer = data + len;
×
373
                        remainder += capacity - size;
×
374
                        size = capacity;
×
375
                        return true;
×
376
                }
377

378
                return false;
×
379
        }
380

381
        bool grow() { return ensure_capacity(size + block_size); }
×
382

383
        friend bool ConvertBuffer(const char* fromCode, const char* toCode, ConversionInputBuffer& from, ConversionOutputBuffer& to);
384
};
385

386
bool ConvertBuffer(const char* fromCode, const char* toCode, ConversionInputBuffer& from, ConversionOutputBuffer& to)
×
387
{
388
        iconv_t descriptor = iconv_open(toCode, fromCode);
×
389
        if (descriptor == reinterpret_cast<iconv_t>(-1))
×
390
        {
391
                LOG(LogLevel::Error) << "unable to recode string from '" << fromCode << "' to '" << toCode << ": not supported on this system";
×
392
                return false;
×
393
        }
394
        while (from.has_remaining_bytes())
×
395
        {
396
                if (iconv(descriptor, &from.in_buffer, &from.remainder, &to.out_buffer, &to.remainder) == static_cast<size_t>(-1))
×
397
                {
398
                        switch (errno)
×
399
                        {
400
                                case E2BIG:
×
401
                                        to.grow();
×
402
                                        break;
×
403
                                case EILSEQ:
×
404
                                        LOG(LogLevel::Error) << "invalid input sequence encountered during conversion of " << from.data << " from " << fromCode << " to " << toCode
×
405
                                                                                                << " : " << std::hex << *reinterpret_cast<uint16_t*>(from.in_buffer);
×
406
                                        return false;
×
407
                                case EINVAL:
×
408
                                        LOG(LogLevel::Error) << "incomplete input sequence encountered during conversion from " << fromCode << " to " << toCode;
×
409
                                        return false;
×
410
                                default:
×
411
                                        break;
×
412
                        }
413
                }
414
        }
415
        iconv_close(descriptor);
×
416
        return true;
×
417
}
418

419
/*
420
 * InputString and OutputString should be instantiations of basic_string, or have a similar API
421
 * InputString should implement:
422
 * member type value_type
423
 * member function value_type begin() const
424
 * member function value_type end() const
425
 * member function size_t length() const
426
 *
427
 */
428
template <typename InputString, typename OutputString>
429
bool ConvertString(const char* fromCode, const char* toCode, const InputString& from, OutputString& to, std::size_t to_buffer_size = 0)
×
430
{
431
        if (to_buffer_size == 0)
×
432
        {
433
                to_buffer_size = from.length();
×
434
        }
435
        ConversionInputBuffer from_buffer(from);
×
436
        ConversionOutputBuffer to_buffer(to_buffer_size);
×
437
        if (ConvertBuffer(fromCode, toCode, from_buffer, to_buffer))
×
438
        {
439
                to_buffer.str(to);
×
440
                return true;
×
441
        }
442

443
        return false;
×
444
}
×
445

446
template <typename InputString, typename OutputString>
447
OutputString ConvertString(const char* fromCode, const char* toCode, const InputString& from, std::size_t to_buffer_size = 0)
×
448
{
449
        OutputString to;
×
450
        ConvertString(fromCode, toCode, from, to, to_buffer_size);
×
451
        return to;
×
452
}
×
453

454
std::string convertUTF8ToASCII(const std::string& UTF8)
×
455
{
456
        return ConvertString<std::string, std::string>("UTF-8", "ASCII//TRANSLIT", UTF8);
×
457
}
458

459
std::string convertUTF8To8859_15(const std::string& UTF8)
×
460
{
461
        return ConvertString<std::string, std::string>("UTF−8", "ISO−8859−15//TRANSLIT", UTF8);
×
462
}
463

464

465
std::string convertUTF8ToWin1252(const std::string& UTF8)
×
466
{
467
        return ConvertString<std::string, std::string>("UTF−8", "CP1252//TRANSLIT", UTF8);
×
468
}
469

470
std::string convertUTF8ToWin1251(const std::string& UTF8)
×
471
{
472
        return ConvertString<std::string, std::string>("UTF−8", "CP1251//TRANSLIT", UTF8);
×
473
}
474

475
std::string convertUTF8ToWin1250(const std::string& UTF8)
×
476
{
477
        return ConvertString<std::string, std::string>("UTF−8", "CP1250//TRANSLIT", UTF8);
×
478
}
479

480
std::string convert8859_15ToUTF8(const std::string& input)
×
481
{
482
        return ConvertString<std::string, std::string>("ISO−8859−15", "UTF-8//TRANSLIT", input);
×
483
}
484

485
/*
486
        Warning: Does not actually return an UTF-16 sequence.
487
        This is an implementation of the original Windows-based API which uses UTF-16 LE as the system dependent wchar_t
488
        encoding This behaviour is replicated on Linux but it uses the (system dependent) wchar_t encoding.
489
*/
490
std::wstring convert8859_15ToUTF16(const std::string& input)
×
491
{
492
        return ConvertString<std::string, std::wstring>("ISO−8859−15", "wchar_t//TRANSLIT", input);
×
493
}
494

495
std::string convertWin1250ToUTF8(const std::string& Win1250)
×
496
{
497
        return ConvertString<std::string, std::string>("CP1250", "UTF-8//TRANSLIT", Win1250);
×
498
}
499

500
std::string convertWin1251ToUTF8(const std::string& Win1251)
×
501
{
502
        return ConvertString<std::string, std::string>("CP1251", "UTF-8//TRANSLIT", Win1251);
×
503
}
504

505
std::string convertWin1252ToUTF8(const std::string& Win1252)
×
506
{
507
        return ConvertString<std::string, std::string>("CP1252", "UTF-8//TRANSLIT", Win1252);
×
508
}
509

510

511
std::wstring convertWin1252ToUTF16(const std::string& Win1252)
×
512
{
513
        return ConvertString<std::string, std::wstring>("CP1252", "wchar_t//TRANSLIT", Win1252);
×
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 convertUTF8ToUTF16(const std::string& UTF8)
×
522
{
523
        std::vector<unsigned long> unicode;
×
524
        for (size_t i = 0; i < UTF8.size();)
×
525
        {
526
                uint32_t uni = '\0';
×
527
                size_t todo = 0;
×
528
                unsigned char ch = UTF8[i++];
×
529
                if (ch <= 0x7F)
×
530
                {
531
                        uni = ch;
×
532
                        todo = 0;
×
533
                }
534
                else if (ch <= 0xBF)
×
535
                {
536
                        throw std::logic_error("not a UTF-8 string");
×
537
                }
538
                else if (ch <= 0xDF)
×
539
                {
540
                        uni = ch & 0x1F;
×
541
                        todo = 1;
×
542
                }
543
                else if (ch <= 0xEF)
×
544
                {
545
                        uni = ch & 0x0F;
×
546
                        todo = 2;
×
547
                }
548
                else if (ch <= 0xF7)
×
549
                {
550
                        uni = ch & 0x07;
×
551
                        todo = 3;
×
552
                }
553
                else
554
                {
555
                        throw std::logic_error("not a UTF-8 string");
×
556
                }
557
                for (size_t j = 0; j < todo; ++j)
×
558
                {
559
                        if (i == UTF8.size())
×
560
                                throw std::logic_error("not a UTF-8 string");
×
561
                        ch = UTF8[i++];
×
562
                        if (ch < 0x80 || ch > 0xBF)
×
563
                                throw std::logic_error("not a UTF-8 string");
×
564
                        uni <<= 6;
×
565
                        uni += ch & 0x3F;
×
566
                }
567
                if (uni >= 0xD800 && uni <= 0xDFFF)
×
568
                        throw std::logic_error("not a UTF-8 string");
×
569
                if (uni > 0x10FFFF)
×
570
                        throw std::logic_error("not a UTF-8 string");
×
571
                unicode.push_back(uni);
×
572
        }
573
        std::wstring utf16;
×
574
        for (size_t i = 0; i < unicode.size(); ++i)
×
575
        {
576
                unsigned long uni = unicode[i];
×
577
                if (uni <= 0xFFFF)
×
578
                {
579
                        utf16 += static_cast<wchar_t>(uni);
×
580
                }
581
                else
582
                {
583
                        uni -= 0x10000;
×
584
                        utf16 += static_cast<wchar_t>((uni >> 10) + 0xD800);
×
585
                        utf16 += static_cast<wchar_t>((uni & 0x3FF) + 0xDC00);
×
586
                }
587
        }
588
        return utf16;
×
589
}
×
590

591

592
std::string convertToUTF8(const std::wstring& input)
×
593
{
594
        return ConvertString<std::wstring, std::string>("wchar_t", "UTF-8//TRANSLIT", input);
×
595
}
596

597

598
std::string utf16_to_utf8(const std::u16string& utf16_string)
×
599
{
600
        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conversion;
×
601
        return conversion.to_bytes(utf16_string);
×
602
}
×
603

604

605
std::string UTF16ToUTF8(const std::wstring& UTF16)
×
606
{
607
        const std::u16string u16str(UTF16.begin(), UTF16.end());
×
608
        return utf16_to_utf8(u16str);
×
609
}
×
610

611

612
std::optional<std::filesystem::path> getSteamInstallPath(const std::string& steamID)
×
613
{
614
        // TODO: Write some actual code for this when able.
615
        return std::nullopt;
×
616
}
617

618
} // 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