Coverage Report

Created: 2025-04-11 06:56

/src/unrar/strfn.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "rar.hpp"
2
3
const char *NullToEmpty(const char *Str)
4
0
{
5
0
  return Str==nullptr ? "":Str;
6
0
}
7
8
9
const wchar *NullToEmpty(const wchar *Str)
10
0
{
11
0
  return Str==nullptr ? L"":Str;
12
0
}
13
14
15
// Convert from OEM encoding.
16
void OemToExt(const std::string &Src,std::string &Dest)
17
1.67k
{
18
#ifdef _WIN_ALL
19
  if (std::addressof(Src)!=std::addressof(Dest))
20
    Dest=Src;
21
  // OemToCharA use seems to be discouraged. So we use OemToCharBuffA,
22
  // which doesn't stop at 0 and converts the entire passed length.
23
  OemToCharBuffA(&Dest[0],&Dest[0],(DWORD)Dest.size());
24
25
  std::string::size_type Pos=Dest.find('\0'); // Avoid zeroes inside of Dest.
26
  if (Pos!=std::string::npos)
27
    Dest.erase(Pos);
28
  
29
#else
30
1.67k
  if (std::addressof(Src)!=std::addressof(Dest))
31
1.67k
    Dest=Src;
32
1.67k
#endif
33
1.67k
}
34
35
36
// Convert archived names and comments to Unicode.
37
// Allows user to select a code page in GUI.
38
void ArcCharToWide(const char *Src,std::wstring &Dest,ACTW_ENCODING Encoding)
39
3.29k
{
40
#if defined(_WIN_ALL) // Console Windows RAR.
41
  if (Encoding==ACTW_UTF8)
42
    UtfToWide(Src,Dest);
43
  else
44
  {
45
    std::string NameA;
46
    if (Encoding==ACTW_OEM)
47
    {
48
      OemToExt(Src,NameA);
49
      Src=NameA.data();
50
    }
51
    CharToWide(Src,Dest);
52
  }
53
#else // RAR for Unix.
54
3.29k
  if (Encoding==ACTW_UTF8)
55
0
    UtfToWide(Src,Dest);
56
3.29k
  else
57
3.29k
    CharToWide(Src,Dest);
58
3.29k
#endif
59
3.29k
  TruncateAtZero(Dest); // Ensure there are no zeroes inside of string.
60
3.29k
}
61
62
63
64
65
66
67
int stricomp(const char *s1,const char *s2)
68
0
{
69
#ifdef _WIN_ALL
70
  return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2;
71
#else
72
0
  while (toupper(*s1)==toupper(*s2))
73
0
  {
74
0
    if (*s1==0)
75
0
      return 0;
76
0
    s1++;
77
0
    s2++;
78
0
  }
79
0
  return s1 < s2 ? -1 : 1;
80
0
#endif
81
0
}
82
83
84
int strnicomp(const char *s1,const char *s2,size_t n)
85
0
{
86
#ifdef _WIN_ALL
87
  // If we specify 'n' exceeding the actual string length, CompareString goes
88
  // beyond the trailing zero and compares garbage. So we need to limit 'n'
89
  // to real string length.
90
  // It is important to use strnlen (or memchr(...,0)) instead of strlen,
91
  // because data can be not zero terminated.
92
  size_t l1=Min(strnlen(s1,n),n);
93
  size_t l2=Min(strnlen(s2,n),n);
94
  return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2;
95
#else
96
0
  if (n==0)
97
0
    return 0;
98
0
  while (toupper(*s1)==toupper(*s2))
99
0
  {
100
0
    if (*s1==0 || --n==0)
101
0
      return 0;
102
0
    s1++;
103
0
    s2++;
104
0
  }
105
0
  return s1 < s2 ? -1 : 1;
106
0
#endif
107
0
}
108
109
110
wchar* RemoveEOL(wchar *Str)
111
0
{
112
0
  for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--)
113
0
    Str[I]=0;
114
0
  return Str;
115
0
}
116
117
118
void RemoveEOL(std::wstring &Str)
119
0
{
120
0
  while (!Str.empty())
121
0
  {
122
0
    wchar c=Str.back();
123
0
    if (c=='\r' || c=='\n' || c==' ' || c=='\t')
124
0
      Str.pop_back();
125
0
    else
126
0
      break;
127
0
  }
128
0
}
129
130
131
wchar* RemoveLF(wchar *Str)
132
0
{
133
0
  for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--)
134
0
    Str[I]=0;
135
0
  return Str;
136
0
}
137
138
139
void RemoveLF(std::wstring &Str)
140
0
{
141
0
  for (int I=(int)Str.size()-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--)
142
0
    Str.erase(I);
143
0
}
144
145
146
#if defined(SFX_MODULE)
147
// char version of etoupperw. Used in console SFX module only.
148
// Fast toupper for English only input and output. Additionally to speed,
149
// it also avoids Turkish small i to big I with dot conversion problem.
150
// We do not define 'c' as 'int' to avoid necessity to cast all
151
// signed chars passed to this function to unsigned char.
152
unsigned char etoupper(unsigned char c)
153
{
154
  return c>='a' && c<='z' ? c-'a'+'A' : c;
155
}
156
#endif
157
158
159
// Fast toupper for English only input and output. Additionally to speed,
160
// it also avoids Turkish small i to big I with dot conversion problem.
161
// We do not define 'c' as 'int' to avoid necessity to cast all
162
// signed wchars passed to this function to unsigned char.
163
wchar etoupperw(wchar c)
164
3.15k
{
165
3.15k
  return c>='a' && c<='z' ? c-'a'+'A' : c;
166
3.15k
}
167
168
169
// We do not want to cast every signed char to unsigned when passing to
170
// isdigit, so we implement the replacement. Shall work for Unicode too.
171
// If chars are signed, conversion from char to int could generate negative
172
// values, resulting in undefined behavior in standard isdigit.
173
bool IsDigit(int ch)
174
6.90k
{
175
6.90k
  return ch>='0' && ch<='9';
176
6.90k
}
177
178
179
// We do not want to cast every signed char to unsigned when passing to
180
// isspace, so we implement the replacement. Shall work for Unicode too.
181
// If chars are signed, conversion from char to int could generate negative
182
// values, resulting in undefined behavior in standard isspace.
183
bool IsSpace(int ch)
184
0
{
185
0
  return ch==' ' || ch=='\t';
186
0
}
187
188
189
// We do not want to cast every signed char to unsigned when passing to
190
// isalpha, so we implement the replacement. Shall work for Unicode too.
191
// If chars are signed, conversion from char to int could generate negative
192
// values, resulting in undefined behavior in standard function.
193
bool IsAlpha(int ch)
194
0
{
195
0
  return ch>='A' && ch<='Z' || ch>='a' && ch<='z';
196
0
}
197
198
199
200
201
void BinToHex(const byte *Bin,size_t BinSize,std::wstring &Hex)
202
0
{
203
0
  Hex.clear();
204
0
  for (uint I=0;I<BinSize;I++)
205
0
  {
206
0
    uint High=Bin[I] >> 4;
207
0
    uint Low=Bin[I] & 0xf;
208
0
    uint HighHex=High>9 ? 'a'+High-10 : '0'+High;
209
0
    uint LowHex=Low>9 ? 'a'+Low-10 : '0'+Low;
210
0
    Hex+=HighHex;
211
0
    Hex+=LowHex;
212
0
  }
213
0
}
214
215
216
#ifndef SFX_MODULE
217
uint GetDigits(uint Number)
218
0
{
219
0
  uint Digits=1;
220
0
  while (Number>=10)
221
0
  {
222
0
    Number/=10;
223
0
    Digits++;
224
0
  }
225
0
  return Digits;
226
0
}
227
#endif
228
229
230
bool LowAscii(const std::string &Str)
231
0
{
232
0
  for (char Ch : Str)
233
0
  {
234
    // We convert char to byte in case char is signed.
235
0
    if (/*(uint)Ch<32 || */(byte)Ch>127)
236
0
      return false;
237
0
  }
238
0
  return true;
239
0
}
240
241
242
bool LowAscii(const std::wstring &Str)
243
0
{
244
0
  for (wchar Ch : Str)
245
0
  {
246
    // We convert wchar_t to uint just in case if some compiler
247
    // uses signed wchar_t.
248
0
    if (/*(uint)Ch<32 || */(uint)Ch>127)
249
0
      return false;
250
0
  }
251
0
  return true;
252
0
}
253
254
255
int wcsicompc(const wchar *s1,const wchar *s2) // For path comparison.
256
76.2k
{
257
76.2k
#if defined(_UNIX)
258
76.2k
  return wcscmp(s1,s2);
259
#else
260
  return wcsicomp(s1,s2);
261
#endif
262
76.2k
}
263
264
265
int wcsicompc(const std::wstring &s1,const std::wstring &s2)
266
76.2k
{
267
76.2k
  return wcsicompc(s1.c_str(),s2.c_str());
268
76.2k
}
269
270
271
int wcsnicompc(const wchar *s1,const wchar *s2,size_t n)
272
0
{
273
0
#if defined(_UNIX)
274
0
  return wcsncmp(s1,s2,n);
275
#else
276
  return wcsnicomp(s1,s2,n);
277
#endif
278
0
}
279
280
281
int wcsnicompc(const std::wstring &s1,const std::wstring &s2,size_t n)
282
0
{
283
0
  return wcsnicompc(s1.c_str(),s2.c_str(),n);
284
0
}
285
286
287
// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest.
288
void strncpyz(char *dest, const char *src, size_t maxlen)
289
0
{
290
0
  if (maxlen>0)
291
0
  {
292
0
    while (--maxlen>0 && *src!=0)
293
0
      *dest++=*src++;
294
0
    *dest=0;
295
0
  }
296
0
}
297
298
299
// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest.
300
void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen)
301
0
{
302
0
  if (maxlen>0)
303
0
  {
304
0
    while (--maxlen>0 && *src!=0)
305
0
      *dest++=*src++;
306
0
    *dest=0;
307
0
  }
308
0
}
309
310
311
// Safe append: resulting dest length cannot exceed maxlen and dest 
312
// is always zero terminated. 'maxlen' parameter defines the entire
313
// dest buffer size and is not compatible with wcsncat.
314
void strncatz(char* dest, const char* src, size_t maxlen)
315
0
{
316
0
  size_t length = strlen(dest);
317
0
  if (maxlen > length)
318
0
    strncpyz(dest + length, src, maxlen - length);
319
0
}
320
321
322
// Safe append: resulting dest length cannot exceed maxlen and dest 
323
// is always zero terminated. 'maxlen' parameter defines the entire
324
// dest buffer size and is not compatible with wcsncat.
325
void wcsncatz(wchar* dest, const wchar* src, size_t maxlen)
326
0
{
327
0
  size_t length = wcslen(dest);
328
0
  if (maxlen > length)
329
0
    wcsncpyz(dest + length, src, maxlen - length);
330
0
}
331
332
333
void itoa(int64 n,char *Str,size_t MaxSize)
334
0
{
335
0
  char NumStr[50];
336
0
  size_t Pos=0;
337
338
0
  int Neg=n < 0 ? 1 : 0;
339
0
  if (Neg)
340
0
    n=-n;
341
342
0
  do
343
0
  {
344
0
    if (Pos+1>=MaxSize-Neg)
345
0
      break;
346
0
    NumStr[Pos++]=char(n%10)+'0';
347
0
    n=n/10;
348
0
  } while (n!=0);
349
350
0
  if (Neg)
351
0
    NumStr[Pos++]='-';
352
353
0
  for (size_t I=0;I<Pos;I++)
354
0
    Str[I]=NumStr[Pos-I-1];
355
0
  Str[Pos]=0;
356
0
}
357
358
359
void itoa(int64 n,wchar *Str,size_t MaxSize)
360
0
{
361
0
  wchar NumStr[50];
362
0
  size_t Pos=0;
363
364
0
  int Neg=n < 0 ? 1 : 0;
365
0
  if (Neg)
366
0
    n=-n;
367
368
0
  do
369
0
  {
370
0
    if (Pos+1>=MaxSize-Neg)
371
0
      break;
372
0
    NumStr[Pos++]=wchar(n%10)+'0';
373
0
    n=n/10;
374
0
  } while (n!=0);
375
376
0
  if (Neg)
377
0
    NumStr[Pos++]='-';
378
379
0
  for (size_t I=0;I<Pos;I++)
380
0
    Str[I]=NumStr[Pos-I-1];
381
0
  Str[Pos]=0;
382
0
}
383
384
385
// Convert the number to string using thousand separators.
386
void fmtitoa(int64 n,wchar *Str,size_t MaxSize)
387
0
{
388
0
  static wchar ThSep=0; // Thousands separator.
389
#ifdef _WIN_ALL
390
  wchar Info[10];
391
  if (!ThSep!=0 && GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,Info,ASIZE(Info))>0)
392
    ThSep=*Info;
393
#elif defined(_UNIX)
394
  ThSep=*localeconv()->thousands_sep;
395
0
#endif
396
0
  if (ThSep==0) // If failed to detect the actual separator value.
397
0
    ThSep=' ';
398
0
  wchar RawText[30]; // 20 characters are enough for largest unsigned 64 bit int.
399
0
  itoa(n,RawText,ASIZE(RawText));
400
0
  uint S=0,D=0,L=wcslen(RawText)%3;
401
0
  while (RawText[S]!=0 && D+1<MaxSize)
402
0
  {
403
0
    if (S!=0 && (S+3-L)%3==0)
404
0
      Str[D++]=ThSep;
405
0
    Str[D++]=RawText[S++];
406
0
  }
407
0
  Str[D]=0;
408
0
}
409
410
411
std::wstring GetWide(const char *Src)
412
0
{
413
0
  std::wstring Str;
414
0
  CharToWide(Src,Str);
415
0
  return Str;
416
0
}
417
418
419
// Parse string containing parameters separated with spaces.
420
// Support quote marks. Accepts and updates the current position in the string.
421
// Returns false if there is nothing to parse.
422
bool GetCmdParam(const std::wstring &CmdLine,std::wstring::size_type &Pos,std::wstring &Param)
423
0
{
424
0
  Param.clear();
425
426
0
  while (IsSpace(CmdLine[Pos]))
427
0
    Pos++;
428
0
  if (Pos==CmdLine.size())
429
0
    return false;
430
431
0
  bool Quote=false;
432
0
  while (Pos<CmdLine.size() && (Quote || !IsSpace(CmdLine[Pos])))
433
0
  {
434
0
    if (CmdLine[Pos]=='\"')
435
0
    {
436
0
      if (CmdLine[Pos+1]=='\"')
437
0
      {
438
        // Insert the quote character instead of two adjoining quote characters.
439
0
        Param+='\"';
440
0
        Pos++;
441
0
      }
442
0
      else
443
0
        Quote=!Quote;
444
0
    }
445
0
    else
446
0
      Param+=CmdLine[Pos];
447
0
    Pos++;
448
0
  }
449
0
  return true;
450
0
}
451
452
453
454
455
#ifndef RARDLL
456
// For compatibility with existing translations we use %s to print Unicode
457
// strings in format strings and convert them to %ls here. %s could work
458
// without such conversion in Windows, but not in Unix wprintf.
459
void PrintfPrepareFmt(const wchar *Org,std::wstring &Cvt)
460
{
461
  size_t Src=0;
462
  while (Org[Src]!=0)
463
  {
464
    if (Org[Src]=='%' && (Src==0 || Org[Src-1]!='%'))
465
    {
466
      size_t SPos=Src+1;
467
      // Skipping a possible width specifier like %-50s.
468
      while (IsDigit(Org[SPos]) || Org[SPos]=='-')
469
        SPos++;
470
      if (Org[SPos]=='s')
471
      {
472
        while (Src<SPos)
473
          Cvt.push_back(Org[Src++]);
474
        Cvt.push_back('l');
475
      }
476
    }
477
#ifdef _WIN_ALL
478
    // Convert \n to \r\n in Windows. Important when writing to log,
479
    // so other tools like Notebook can view resulting log properly.
480
    if (Org[Src]=='\n' && (Src==0 || Org[Src-1]!='\r'))
481
      Cvt.push_back('\r');
482
#endif
483
484
    Cvt.push_back(Org[Src++]);
485
  }
486
}
487
488
489
// Print output to std::wstring.
490
std::wstring wstrprintf(const wchar *fmt,...)
491
{
492
  va_list arglist;
493
  va_start(arglist,fmt);
494
  std::wstring s=vwstrprintf(fmt,arglist);
495
  va_end(arglist);
496
  return s;
497
}
498
499
500
std::wstring vwstrprintf(const wchar *fmt,va_list arglist)
501
{
502
  std::wstring fmtw;
503
  PrintfPrepareFmt(fmt,fmtw);
504
505
  // We also tried to use _vscwprintf in MSVC to calculate the required buffer
506
  // size and allocate the exactly such size, but it seemed to be a little
507
  // slower than approach below.
508
  
509
  const size_t MaxAllocSize=0x10000; // Prevent the excessive allocation.
510
511
  // vswprintf returns only the error status without required buffer size,
512
  // so we try different buffer sizes. Start from reasonably small size
513
  // to reduce the zero initialization cost, but still large enough to fit
514
  // most of strings and avoid additional loop iterations.
515
  std::wstring Msg(256,L'\0');
516
  while (true)
517
  {
518
    va_list argscopy;
519
    va_copy(argscopy, arglist);
520
    int r=vswprintf(&Msg[0],Msg.size(),fmtw.c_str(),argscopy);
521
    va_end(argscopy);
522
    if (r>=0 || Msg.size()>MaxAllocSize)
523
      break;
524
    Msg.resize(Msg.size()*4);
525
  }
526
  std::wstring::size_type ZeroPos=Msg.find(L'\0');
527
  if (ZeroPos!=std::wstring::npos)
528
    Msg.resize(ZeroPos); // Remove excessive zeroes at the end.
529
  
530
  return Msg;
531
}
532
#endif
533
534
535
#ifdef _WIN_ALL
536
bool ExpandEnvironmentStr(std::wstring &Str)
537
{
538
  DWORD ExpCode=ExpandEnvironmentStrings(Str.c_str(),nullptr,0);
539
  if (ExpCode==0)
540
    return false;
541
  std::vector<wchar> Buf(ExpCode);
542
  ExpCode=ExpandEnvironmentStrings(Str.c_str(),Buf.data(),(DWORD)Buf.size());
543
  if (ExpCode==0 || ExpCode>Buf.size())
544
    return false;
545
  Str=Buf.data();
546
  return true;
547
}
548
#endif
549
550
551
void TruncateAtZero(std::wstring &Str)
552
84.2k
{
553
84.2k
  std::wstring::size_type Pos=Str.find(L'\0');
554
84.2k
  if (Pos!=std::wstring::npos)
555
1.61k
    Str.erase(Pos);
556
84.2k
}
557
558
559
void ReplaceEsc(std::wstring &Str)
560
0
{
561
0
  std::wstring::size_type Pos=0;
562
0
  while (true)
563
0
  {
564
0
    Pos=Str.find(L'\033',Pos);
565
0
    if (Pos==std::wstring::npos)
566
0
      break;
567
0
    Str[Pos]=L'\'';
568
0
    Str.insert(Pos+1,L"\\033'");
569
0
    Pos+=6;
570
0
  }
571
0
}