Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmPkgConfigResolver.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
4
#include "cmPkgConfigResolver.h"
5
6
#include <algorithm>
7
#include <cctype>
8
#include <cstring>
9
#include <iterator>
10
#include <string>
11
#include <unordered_map>
12
#include <utility>
13
#include <vector>
14
15
#include <cm/optional>
16
#include <cm/string_view>
17
18
#include "cmPkgConfigParser.h"
19
#include "cmStringAlgorithms.h"
20
21
namespace {
22
23
void TrimBack(std::string& str)
24
0
{
25
0
  if (!str.empty()) {
26
0
    auto it = str.end() - 1;
27
0
    for (; std::isspace(*it); --it) {
28
0
      if (it == str.begin()) {
29
0
        str.clear();
30
0
        return;
31
0
      }
32
0
    }
33
0
    str.erase(++it, str.end());
34
0
  }
35
0
}
36
37
std::string AppendAndTrim(std::string& str, cm::string_view sv)
38
0
{
39
0
  auto size = str.length();
40
0
  str += sv;
41
0
  if (str.empty()) {
42
0
    return {};
43
0
  }
44
45
0
  auto begin = str.begin() + size;
46
0
  auto cur = str.end() - 1;
47
48
0
  while (cur != begin && std::isspace(*cur)) {
49
0
    --cur;
50
0
  }
51
52
0
  if (std::isspace(*cur)) {
53
0
    return {};
54
0
  }
55
56
0
  return { &*begin, static_cast<std::size_t>(cur - begin) + 1 };
57
0
}
58
59
cm::string_view TrimFlag(cm::string_view flag)
60
0
{
61
0
  std::size_t trim_size = 2;
62
0
  for (auto c = flag.rbegin(); c != flag.rend() && std::isspace(*c); ++c) {
63
0
    ++trim_size;
64
0
  }
65
0
  return { flag.data() + 2, flag.size() - trim_size };
66
0
}
67
68
} // namespace
69
70
std::string cmPkgConfigVersionReq::string() const
71
0
{
72
0
  switch (Operation) {
73
0
    case ANY:
74
0
      return "";
75
0
    case LT:
76
0
      return cmStrCat('<', Version);
77
0
    case LT_EQ:
78
0
      return cmStrCat("<=", Version);
79
0
    case EQ:
80
0
      return cmStrCat('=', Version);
81
0
    case NEQ:
82
0
      return cmStrCat("!=", Version);
83
0
    case GT_EQ:
84
0
      return cmStrCat(">=", Version);
85
0
    case GT:
86
0
      return cmStrCat('>', Version);
87
0
  }
88
0
  return "";
89
0
}
90
91
std::string cmPkgConfigResult::StrOrDefault(std::string const& key,
92
                                            cm::string_view def)
93
0
{
94
0
  auto it = Keywords.find(key);
95
0
  return it == Keywords.end() ? std::string{ def } : it->second;
96
0
};
97
98
std::string cmPkgConfigResult::Name()
99
0
{
100
0
  return StrOrDefault("Name");
101
0
}
102
103
std::string cmPkgConfigResult::Description()
104
0
{
105
0
  return StrOrDefault("Description");
106
0
}
107
108
std::string cmPkgConfigResult::Version()
109
0
{
110
0
  return StrOrDefault("Version");
111
0
}
112
113
std::vector<cmPkgConfigDependency> cmPkgConfigResult::Conflicts()
114
0
{
115
0
  auto it = Keywords.find("Conflicts");
116
0
  if (it == Keywords.end()) {
117
0
    return {};
118
0
  }
119
120
0
  return cmPkgConfigResolver::ParseDependencies(it->second);
121
0
}
122
123
std::vector<cmPkgConfigDependency> cmPkgConfigResult::Provides()
124
0
{
125
0
  auto it = Keywords.find("Provides");
126
0
  if (it == Keywords.end()) {
127
0
    return {};
128
0
  }
129
130
0
  return cmPkgConfigResolver::ParseDependencies(it->second);
131
0
}
132
133
std::vector<cmPkgConfigDependency> cmPkgConfigResult::Requires(bool priv)
134
0
{
135
0
  auto it = Keywords.find(priv ? "Requires.private" : "Requires");
136
0
  if (it == Keywords.end()) {
137
0
    return {};
138
0
  }
139
140
0
  return cmPkgConfigResolver::ParseDependencies(it->second);
141
0
}
142
143
cmPkgConfigCflagsResult cmPkgConfigResult::Cflags(bool priv)
144
0
{
145
0
  std::string cflags;
146
0
  auto it = Keywords.find(priv ? "Cflags.private" : "Cflags");
147
0
  if (it != Keywords.end()) {
148
0
    cflags += it->second;
149
0
  }
150
151
0
  it = Keywords.find(priv ? "CFlags.private" : "CFlags");
152
0
  if (it != Keywords.end()) {
153
0
    if (!cflags.empty()) {
154
0
      cflags += " ";
155
0
    }
156
0
    cflags += it->second;
157
0
  }
158
159
0
  auto tokens = cmPkgConfigResolver::TokenizeFlags(cflags);
160
161
0
  if (env->AllowSysCflags) {
162
0
    if (env->SysrootDir) {
163
0
      return cmPkgConfigResolver::MangleCflags(tokens, *env->SysrootDir);
164
0
    }
165
0
    return cmPkgConfigResolver::MangleCflags(tokens);
166
0
  }
167
168
0
  if (env->SysCflags) {
169
0
    if (env->SysrootDir) {
170
0
      return cmPkgConfigResolver::MangleCflags(tokens, *env->SysrootDir,
171
0
                                               *env->SysCflags);
172
0
    }
173
0
    return cmPkgConfigResolver::MangleCflags(tokens, *env->SysCflags);
174
0
  }
175
176
0
  if (env->SysrootDir) {
177
0
    return cmPkgConfigResolver::MangleCflags(
178
0
      tokens, *env->SysrootDir, std::vector<std::string>{ "/usr/include" });
179
0
  }
180
181
0
  return cmPkgConfigResolver::MangleCflags(
182
0
    tokens, std::vector<std::string>{ "/usr/include" });
183
0
}
184
185
cmPkgConfigLibsResult cmPkgConfigResult::Libs(bool priv)
186
0
{
187
0
  auto it = Keywords.find(priv ? "Libs.private" : "Libs");
188
0
  if (it == Keywords.end()) {
189
0
    return cmPkgConfigLibsResult();
190
0
  }
191
192
0
  auto tokens = cmPkgConfigResolver::TokenizeFlags(it->second);
193
194
0
  if (env->AllowSysLibs) {
195
0
    if (env->SysrootDir) {
196
0
      return cmPkgConfigResolver::MangleLibs(tokens, *env->SysrootDir);
197
0
    }
198
0
    return cmPkgConfigResolver::MangleLibs(tokens);
199
0
  }
200
201
0
  if (env->SysLibs) {
202
0
    if (env->SysrootDir) {
203
0
      return cmPkgConfigResolver::MangleLibs(tokens, *env->SysrootDir,
204
0
                                             *env->SysLibs);
205
0
    }
206
0
    return cmPkgConfigResolver::MangleLibs(tokens, *env->SysLibs);
207
0
  }
208
209
0
  if (env->SysrootDir) {
210
0
    return cmPkgConfigResolver::MangleLibs(
211
0
      tokens, *env->SysrootDir, std::vector<std::string>{ "/usr/lib" });
212
0
  }
213
214
0
  return cmPkgConfigResolver::MangleLibs(
215
0
    tokens, std::vector<std::string>{ "/usr/lib" });
216
0
}
217
218
void cmPkgConfigResolver::ReplaceSep(std::string& list)
219
0
{
220
0
#ifndef _WIN32
221
0
  std::replace(list.begin(), list.end(), ':', ';');
222
#else
223
  static_cast<void>(list); // Unused parameter
224
#endif
225
0
}
226
227
cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolveStrict(
228
  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
229
0
{
230
0
  cm::optional<cmPkgConfigResult> result;
231
0
  cmPkgConfigResult config;
232
0
  auto& keys = config.Keywords;
233
234
0
  if (env.SysrootDir) {
235
0
    config.Variables["pc_sysrootdir"] = *env.SysrootDir;
236
0
  } else {
237
0
    config.Variables["pc_sysrootdir"] = "/";
238
0
  }
239
240
0
  if (env.TopBuildDir) {
241
0
    config.Variables["pc_top_builddir"] = *env.TopBuildDir;
242
0
  }
243
244
0
  config.env = &env;
245
246
0
  for (auto const& entry : entries) {
247
0
    std::string key(entry.Key);
248
0
    if (entry.IsVariable) {
249
0
      if (config.Variables.find(key) != config.Variables.end()) {
250
0
        return result;
251
0
      }
252
0
      auto var = HandleVariableStrict(entry, config.Variables);
253
0
      if (!var) {
254
0
        return result;
255
0
      }
256
0
      config.Variables[key] = *var;
257
0
    } else {
258
0
      if (key == "Cflags" && keys.find("CFlags") != keys.end()) {
259
0
        return result;
260
0
      }
261
0
      if (key == "CFlags" && keys.find("Cflags") != keys.end()) {
262
0
        return result;
263
0
      }
264
0
      if (key == "Cflags.private" &&
265
0
          keys.find("CFlags.private") != keys.end()) {
266
0
        return result;
267
0
      }
268
0
      if (key == "CFlags.private" &&
269
0
          keys.find("Cflags.private") != keys.end()) {
270
0
        return result;
271
0
      }
272
0
      if (keys.find(key) != keys.end()) {
273
0
        return result;
274
0
      }
275
0
      keys[key] = HandleKeyword(entry, config.Variables);
276
0
    }
277
0
  }
278
279
0
  if (keys.find("Name") == keys.end() ||
280
0
      keys.find("Description") == keys.end() ||
281
0
      keys.find("Version") == keys.end()) {
282
0
    return result;
283
0
  }
284
285
0
  result = std::move(config);
286
0
  return result;
287
0
}
288
289
cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolvePermissive(
290
  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
291
0
{
292
0
  cm::optional<cmPkgConfigResult> result;
293
294
0
  cmPkgConfigResult config = ResolveBestEffort(entries, std::move(env));
295
0
  auto const& keys = config.Keywords;
296
297
0
  if (keys.find("Name") == keys.end() ||
298
0
      keys.find("Description") == keys.end() ||
299
0
      keys.find("Version") == keys.end()) {
300
0
    return result;
301
0
  }
302
303
0
  result = std::move(config);
304
0
  return result;
305
0
}
306
307
cmPkgConfigResult cmPkgConfigResolver::ResolveBestEffort(
308
  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
309
0
{
310
0
  cmPkgConfigResult result;
311
312
0
  if (env.SysrootDir) {
313
0
    result.Variables["pc_sysrootdir"] = *env.SysrootDir;
314
0
  } else {
315
0
    result.Variables["pc_sysrootdir"] = "/";
316
0
  }
317
318
0
  if (env.TopBuildDir) {
319
0
    result.Variables["pc_top_builddir"] = *env.TopBuildDir;
320
0
  }
321
322
0
  result.env = &env;
323
324
0
  for (auto const& entry : entries) {
325
0
    std::string key(entry.Key);
326
0
    if (entry.IsVariable) {
327
0
      result.Variables[key] =
328
0
        HandleVariablePermissive(entry, result.Variables);
329
0
    } else {
330
0
      result.Keywords[key] += HandleKeyword(entry, result.Variables);
331
0
    }
332
0
  }
333
0
  return result;
334
0
}
335
336
std::string cmPkgConfigResolver::HandleVariablePermissive(
337
  cmPkgConfigEntry const& entry,
338
  std::unordered_map<std::string, std::string> const& variables)
339
0
{
340
0
  std::string result;
341
0
  for (auto const& segment : entry.Val) {
342
0
    if (!segment.IsVariable) {
343
0
      result += segment.Data;
344
0
    } else if (entry.Key != segment.Data) {
345
0
      auto it = variables.find(std::string{ segment.Data });
346
0
      if (it != variables.end()) {
347
0
        result += it->second;
348
0
      }
349
0
    }
350
0
  }
351
352
0
  TrimBack(result);
353
0
  return result;
354
0
}
355
356
cm::optional<std::string> cmPkgConfigResolver::HandleVariableStrict(
357
  cmPkgConfigEntry const& entry,
358
  std::unordered_map<std::string, std::string> const& variables)
359
0
{
360
0
  cm::optional<std::string> result;
361
362
0
  std::string value;
363
0
  for (auto const& segment : entry.Val) {
364
0
    if (!segment.IsVariable) {
365
0
      value += segment.Data;
366
0
    } else if (entry.Key == segment.Data) {
367
0
      return result;
368
0
    } else {
369
0
      auto it = variables.find(std::string{ segment.Data });
370
0
      if (it != variables.end()) {
371
0
        value += it->second;
372
0
      } else {
373
0
        return result;
374
0
      }
375
0
    }
376
0
  }
377
378
0
  TrimBack(value);
379
0
  result = std::move(value);
380
0
  return result;
381
0
}
382
383
std::string cmPkgConfigResolver::HandleKeyword(
384
  cmPkgConfigEntry const& entry,
385
  std::unordered_map<std::string, std::string> const& variables)
386
0
{
387
0
  std::string result;
388
0
  for (auto const& segment : entry.Val) {
389
0
    if (!segment.IsVariable) {
390
0
      result += segment.Data;
391
0
    } else {
392
0
      auto it = variables.find(std::string{ segment.Data });
393
0
      if (it != variables.end()) {
394
0
        result += it->second;
395
0
      }
396
0
    }
397
0
  }
398
399
0
  TrimBack(result);
400
0
  return result;
401
0
}
402
403
std::vector<cm::string_view> cmPkgConfigResolver::TokenizeFlags(
404
  std::string const& flagline)
405
0
{
406
0
  std::vector<cm::string_view> result;
407
408
0
  auto it = flagline.begin();
409
0
  while (it != flagline.end() && std::isspace(*it)) {
410
0
    ++it;
411
0
  }
412
413
0
  while (it != flagline.end()) {
414
0
    char const* start = &(*it);
415
0
    std::size_t len = 0;
416
417
0
    for (; it != flagline.end() && !std::isspace(*it); ++it) {
418
0
      ++len;
419
0
    }
420
421
0
    for (; it != flagline.end() && std::isspace(*it); ++it) {
422
0
      ++len;
423
0
    }
424
425
0
    result.emplace_back(start, len);
426
0
  }
427
428
0
  return result;
429
0
}
430
431
cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags(
432
  std::vector<cm::string_view> const& flags)
433
0
{
434
0
  cmPkgConfigCflagsResult result;
435
436
0
  for (auto flag : flags) {
437
0
    if (flag.rfind("-I", 0) == 0) {
438
0
      result.Includes.emplace_back(AppendAndTrim(result.Flagline, flag));
439
0
    } else {
440
0
      result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
441
0
    }
442
0
  }
443
444
0
  return result;
445
0
}
446
447
cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags(
448
  std::vector<cm::string_view> const& flags, std::string const& sysroot)
449
0
{
450
0
  cmPkgConfigCflagsResult result;
451
452
0
  for (auto flag : flags) {
453
0
    if (flag.rfind("-I", 0) == 0) {
454
0
      std::string reroot = Reroot(flag, "-I", sysroot);
455
0
      result.Includes.emplace_back(AppendAndTrim(result.Flagline, reroot));
456
0
    } else {
457
0
      result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
458
0
    }
459
0
  }
460
461
0
  return result;
462
0
}
463
464
cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags(
465
  std::vector<cm::string_view> const& flags,
466
  std::vector<std::string> const& syspaths)
467
0
{
468
0
  cmPkgConfigCflagsResult result;
469
470
0
  for (auto flag : flags) {
471
0
    if (flag.rfind("-I", 0) == 0) {
472
0
      cm::string_view trimmed = TrimFlag(flag);
473
0
      if (std::all_of(
474
0
            syspaths.begin(), syspaths.end(),
475
0
            [&](std::string const& path) { return path != trimmed; })) {
476
0
        result.Includes.emplace_back(AppendAndTrim(result.Flagline, flag));
477
0
      }
478
479
0
    } else {
480
0
      result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
481
0
    }
482
0
  }
483
484
0
  return result;
485
0
}
486
487
cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags(
488
  std::vector<cm::string_view> const& flags, std::string const& sysroot,
489
  std::vector<std::string> const& syspaths)
490
0
{
491
0
  cmPkgConfigCflagsResult result;
492
493
0
  for (auto flag : flags) {
494
0
    if (flag.rfind("-I", 0) == 0) {
495
0
      std::string reroot = Reroot(flag, "-I", sysroot);
496
0
      cm::string_view trimmed = TrimFlag(reroot);
497
0
      if (std::all_of(
498
0
            syspaths.begin(), syspaths.end(),
499
0
            [&](std::string const& path) { return path != trimmed; })) {
500
0
        result.Includes.emplace_back(AppendAndTrim(result.Flagline, reroot));
501
0
      }
502
503
0
    } else {
504
0
      result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
505
0
    }
506
0
  }
507
508
0
  return result;
509
0
}
510
511
cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs(
512
  std::vector<cm::string_view> const& flags)
513
0
{
514
0
  cmPkgConfigLibsResult result;
515
516
0
  for (auto flag : flags) {
517
0
    if (flag.rfind("-L", 0) == 0) {
518
0
      result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, flag));
519
0
    } else if (flag.rfind("-l", 0) == 0) {
520
0
      result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag));
521
0
    } else {
522
0
      result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
523
0
    }
524
0
  }
525
526
0
  return result;
527
0
}
528
529
cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs(
530
  std::vector<cm::string_view> const& flags, std::string const& sysroot)
531
0
{
532
0
  cmPkgConfigLibsResult result;
533
534
0
  for (auto flag : flags) {
535
0
    if (flag.rfind("-L", 0) == 0) {
536
0
      std::string reroot = Reroot(flag, "-L", sysroot);
537
0
      result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, reroot));
538
0
    } else if (flag.rfind("-l", 0) == 0) {
539
0
      result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag));
540
0
    } else {
541
0
      result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
542
0
    }
543
0
  }
544
545
0
  return result;
546
0
}
547
548
cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs(
549
  std::vector<cm::string_view> const& flags,
550
  std::vector<std::string> const& syspaths)
551
0
{
552
0
  cmPkgConfigLibsResult result;
553
554
0
  for (auto flag : flags) {
555
0
    if (flag.rfind("-L", 0) == 0) {
556
0
      cm::string_view trimmed = TrimFlag(flag);
557
0
      if (std::all_of(
558
0
            syspaths.begin(), syspaths.end(),
559
0
            [&](std::string const& path) { return path != trimmed; })) {
560
0
        result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, flag));
561
0
      }
562
563
0
    } else if (flag.rfind("-l", 0) == 0) {
564
0
      result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag));
565
0
    } else {
566
0
      result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
567
0
    }
568
0
  }
569
570
0
  return result;
571
0
}
572
573
cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs(
574
  std::vector<cm::string_view> const& flags, std::string const& sysroot,
575
  std::vector<std::string> const& syspaths)
576
0
{
577
0
  cmPkgConfigLibsResult result;
578
579
0
  for (auto flag : flags) {
580
0
    if (flag.rfind("-L", 0) == 0) {
581
0
      std::string reroot = Reroot(flag, "-L", sysroot);
582
0
      cm::string_view trimmed = TrimFlag(reroot);
583
0
      if (std::all_of(
584
0
            syspaths.begin(), syspaths.end(),
585
0
            [&](std::string const& path) { return path != trimmed; })) {
586
0
        result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, reroot));
587
0
      }
588
589
0
    } else if (flag.rfind("-l", 0) == 0) {
590
0
      result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag));
591
0
    } else {
592
0
      result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag));
593
0
    }
594
0
  }
595
596
0
  return result;
597
0
}
598
599
std::string cmPkgConfigResolver::Reroot(cm::string_view flag,
600
                                        cm::string_view prefix,
601
                                        std::string const& sysroot)
602
0
{
603
0
  std::string result = std::string{ prefix };
604
0
  result += sysroot;
605
0
  result += cm::string_view{ flag.data() + prefix.length(),
606
0
                             flag.size() - prefix.length() };
607
0
  return result;
608
0
}
609
610
cmPkgConfigVersionReq cmPkgConfigResolver::ParseVersion(
611
  std::string::const_iterator& cur, std::string::const_iterator end)
612
0
{
613
0
  cmPkgConfigVersionReq result;
614
0
  if (*cur == '=') {
615
0
    result.Operation = result.EQ;
616
0
    ++cur;
617
0
  } else if (*cur == '>') {
618
0
    ++cur;
619
620
0
    if (cur == end) {
621
0
      result.Operation = result.GT;
622
0
      return result;
623
0
    }
624
625
0
    if (*cur == '=') {
626
0
      result.Operation = result.GT_EQ;
627
0
      ++cur;
628
0
    } else {
629
0
      result.Operation = result.GT;
630
0
    }
631
632
0
  } else if (*cur == '<') {
633
0
    ++cur;
634
635
0
    if (cur == end) {
636
0
      result.Operation = result.LT;
637
0
      return result;
638
0
    }
639
640
0
    if (*cur == '=') {
641
0
      result.Operation = result.LT_EQ;
642
0
      ++cur;
643
0
    } else {
644
0
      result.Operation = result.LT;
645
0
    }
646
647
0
  } else if (*cur == '!') {
648
0
    ++cur;
649
650
0
    if (cur == end) {
651
0
      result.Operation = result.ANY;
652
0
      return result;
653
0
    }
654
655
0
    if (*cur == '=') {
656
0
      result.Operation = result.NEQ;
657
0
      ++cur;
658
0
    } else {
659
0
      result.Operation = result.ANY;
660
0
    }
661
0
  }
662
663
0
  for (;; ++cur) {
664
0
    if (cur == end) {
665
0
      return result;
666
0
    }
667
668
0
    if (!std::isspace(*cur)) {
669
0
      break;
670
0
    }
671
0
  }
672
673
0
  for (; cur != end && !std::isspace(*cur) && *cur != ','; ++cur) {
674
0
    result.Version += *cur;
675
0
  }
676
677
0
  return result;
678
0
}
679
680
std::vector<cmPkgConfigDependency> cmPkgConfigResolver::ParseDependencies(
681
  std::string const& deps)
682
0
{
683
684
0
  std::vector<cmPkgConfigDependency> result;
685
686
0
  auto cur = deps.begin();
687
0
  auto end = deps.end();
688
689
0
  while (cur != end) {
690
0
    while ((std::isspace(*cur) || *cur == ',')) {
691
0
      if (++cur == end) {
692
0
        return result;
693
0
      }
694
0
    }
695
696
0
    result.emplace_back();
697
0
    auto& dep = result.back();
698
699
0
    while (!std::isspace(*cur) && *cur != ',') {
700
0
      dep.Name += *cur;
701
0
      if (++cur == end) {
702
0
        return result;
703
0
      }
704
0
    }
705
706
0
    auto in_operator = [&]() -> bool {
707
0
      for (;; ++cur) {
708
0
        if (cur == end) {
709
0
          return false;
710
0
        }
711
712
0
        if (*cur == '>' || *cur == '=' || *cur == '<' || *cur == '!') {
713
0
          return true;
714
0
        }
715
716
0
        if (!std::isspace(*cur)) {
717
0
          return false;
718
0
        }
719
0
      }
720
0
    };
721
722
0
    if (!in_operator()) {
723
0
      continue;
724
0
    }
725
726
0
    dep.VerReq = ParseVersion(cur, end);
727
0
  }
728
729
0
  return result;
730
0
}
731
732
bool cmPkgConfigResolver::CheckVersion(cmPkgConfigVersionReq const& desired,
733
                                       std::string const& provided)
734
0
{
735
736
0
  if (desired.Operation == cmPkgConfigVersionReq::ANY) {
737
0
    return true;
738
0
  }
739
740
  // https://blog.jasonantman.com/2014/07/how-yum-and-rpm-compare-versions/
741
742
0
  auto check_with_op = [&](int comp) -> bool {
743
0
    switch (desired.Operation) {
744
0
      case cmPkgConfigVersionReq::EQ:
745
0
        return comp == 0;
746
0
      case cmPkgConfigVersionReq::NEQ:
747
0
        return comp != 0;
748
0
      case cmPkgConfigVersionReq::GT:
749
0
        return comp < 0;
750
0
      case cmPkgConfigVersionReq::GT_EQ:
751
0
        return comp <= 0;
752
0
      case cmPkgConfigVersionReq::LT:
753
0
        return comp > 0;
754
0
      case cmPkgConfigVersionReq::LT_EQ:
755
0
        return comp >= 0;
756
0
      default:
757
0
        return true;
758
0
    }
759
0
  };
760
761
0
  if (desired.Version == provided) {
762
0
    return check_with_op(0);
763
0
  }
764
765
0
  auto a_cur = desired.Version.begin();
766
0
  auto a_end = desired.Version.end();
767
768
0
  auto b_cur = provided.begin();
769
0
  auto b_end = provided.end();
770
771
0
  while (a_cur != a_end && b_cur != b_end) {
772
0
    while (a_cur != a_end && !std::isalnum(*a_cur) && *a_cur != '~') {
773
0
      ++a_cur;
774
0
    }
775
776
0
    while (b_cur != b_end && !std::isalnum(*b_cur) && *b_cur != '~') {
777
0
      ++b_cur;
778
0
    }
779
780
0
    if (a_cur == a_end || b_cur == b_end) {
781
0
      break;
782
0
    }
783
784
0
    if (*a_cur == '~' || *b_cur == '~') {
785
0
      if (*a_cur != '~') {
786
0
        return check_with_op(1);
787
0
      }
788
789
0
      if (*b_cur != '~') {
790
0
        return check_with_op(-1);
791
0
      }
792
793
0
      ++a_cur;
794
0
      ++b_cur;
795
0
      continue;
796
0
    }
797
798
0
    auto a_seg = a_cur;
799
0
    auto b_seg = b_cur;
800
0
    bool is_num;
801
802
0
    if (std::isdigit(*a_cur)) {
803
0
      is_num = true;
804
0
      while (a_cur != a_end && std::isdigit(*a_cur)) {
805
0
        ++a_cur;
806
0
      }
807
808
0
      while (b_cur != b_end && std::isdigit(*b_cur)) {
809
0
        ++b_cur;
810
0
      }
811
812
0
    } else {
813
0
      is_num = false;
814
0
      while (a_cur != a_end && std::isalpha(*a_cur)) {
815
0
        ++a_cur;
816
0
      }
817
818
0
      while (b_cur != b_end && std::isalpha(*b_cur)) {
819
0
        ++b_cur;
820
0
      }
821
0
    }
822
823
0
    auto a_len = std::distance(a_seg, a_cur);
824
0
    auto b_len = std::distance(b_seg, b_cur);
825
826
0
    if (!b_len) {
827
0
      return check_with_op(is_num ? 1 : -1);
828
0
    }
829
830
0
    if (is_num) {
831
0
      while (a_seg != a_cur && *a_seg == '0') {
832
0
        ++a_seg;
833
0
      }
834
835
0
      while (b_seg != b_cur && *b_seg == '0') {
836
0
        ++b_seg;
837
0
      }
838
839
0
      a_len = std::distance(a_seg, a_cur);
840
0
      b_len = std::distance(b_seg, b_cur);
841
842
0
      if (a_len != b_len) {
843
0
        return check_with_op(a_len > b_len ? 1 : -1);
844
0
      }
845
846
0
      auto cmp = std::memcmp(&*a_seg, &*b_seg, a_len);
847
0
      if (cmp) {
848
0
        return check_with_op(cmp);
849
0
      }
850
0
    } else {
851
0
      auto cmp = std::memcmp(&*a_seg, &*b_seg, std::min(a_len, b_len));
852
0
      if (cmp) {
853
0
        return check_with_op(cmp);
854
0
      }
855
856
0
      if (a_len != b_len) {
857
0
        return check_with_op(a_len > b_len ? 1 : -1);
858
0
      }
859
0
    }
860
0
  }
861
862
0
  if (a_cur == a_end) {
863
0
    if (b_cur == b_end) {
864
0
      return check_with_op(0);
865
0
    }
866
0
    return check_with_op(-1);
867
0
  }
868
869
0
  return check_with_op(1);
870
0
}
871
872
cmPkgConfigVersionReq cmPkgConfigResolver::ParseVersion(
873
  std::string const& version)
874
0
{
875
0
  cmPkgConfigVersionReq result;
876
877
0
  auto cur = version.begin();
878
0
  auto end = version.end();
879
880
0
  if (cur == end) {
881
0
    result.Operation = cmPkgConfigVersionReq::EQ;
882
0
    return result;
883
0
  }
884
885
0
  result = ParseVersion(cur, end);
886
0
  cur = version.begin();
887
888
0
  if (*cur != '=' && *cur != '!' && *cur != '<' && *cur != '>') {
889
0
    result.Operation = cmPkgConfigVersionReq::EQ;
890
0
  }
891
892
0
  return result;
893
0
}