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

PredatorCZ / PreCore / 461

pending completion
461

push

github-actions-ci

PredatorCZ
update readme

3204 of 6096 relevant lines covered (52.56%)

354.05 hits per line

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

0.0
/src/cli/spike.cpp
1
/*  Spike is universal dedicated module handler
2
    This source contains code for CLI master app
3

4
    Copyright 2021-2023 Lukas Cone
5

6
    Licensed under the Apache License, Version 2.0 (the "License");
7
    you may not use this file except in compliance with the License.
8
    You may obtain a copy of the License at
9

10
        http://www.apache.org/licenses/LICENSE-2.0
11

12
    Unless required by applicable law or agreed to in writing, software
13
    distributed under the License is distributed on an "AS IS" BASIS,
14
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
    See the License for the specific language governing permissions and
16
    limitations under the License.
17
*/
18

19
#include "project.h"
20
#include "spike/app/batch.hpp"
21
#include "spike/app/console.hpp"
22
#include "spike/app/tmp_storage.hpp"
23
#include "spike/io/binwritter.hpp"
24
#include "spike/io/stat.hpp"
25
#include "spike/master_printer.hpp"
26
#include "spike/type/tchar.hpp"
27
#include "spike/util/pugiex.hpp"
28
#include <thread>
29

30
static const char appHeader0[] =
31
    "Simply drag'n'drop files/folders onto application or "
32
    "use as ";
33
static const char appHeader1[] =
34
    " [options] path1 path2 ...\nTool can detect and scan folders and "
35
    "uncompressed zip archives.";
36

37
struct ProcessedFiles : LoadingBar, CounterLine {
38
  char buffer[128]{};
39

40
  ProcessedFiles() : LoadingBar({buffer, sizeof(buffer)}) {}
×
41
  void PrintLine() override {
×
42
    snprintf(buffer, sizeof(buffer), "Processed %4" PRIuMAX " files.",
×
43
             curitem.load(std::memory_order_relaxed));
44
    LoadingBar::PrintLine();
×
45
  }
46
};
47

48
struct ExtractStats {
49
  std::map<JenHash, size_t> archiveFiles;
50
  size_t totalFiles = 0;
51
};
52

53
struct UILines {
54
  ProgressBar *totalProgress{nullptr};
55
  CounterLine *totalCount{nullptr};
56
  std::map<uint32, ProgressBar *> bars;
57
  std::mutex barsMutex;
58

59
  auto ChooseBar() {
×
60
    if (bars.empty()) {
×
61
      return (ProgressBar *)(nullptr);
62
    }
63

64
    auto threadId = std::this_thread::get_id();
65
    auto id = reinterpret_cast<const uint32 &>(threadId);
×
66
    auto found = bars.find(id);
67

68
    if (es::IsEnd(bars, found)) {
×
69
      std::lock_guard<std::mutex> lg(barsMutex);
×
70
      auto retVal = bars.begin()->second;
×
71
      bars.emplace(id, retVal);
×
72
      bars.erase(bars.begin());
73

74
      return retVal;
75
    }
76

77
    return found->second;
×
78
  };
79

80
  UILines(const ExtractStats &stats) {
×
81
    ModifyElements([&](ElementAPI &api) {
×
82
      const size_t minThreads =
83
          std::min(size_t(std::thread::hardware_concurrency()),
×
84
                   stats.archiveFiles.size());
×
85

86
      if (minThreads < 2) {
×
87
        return;
88
      }
89

90
      for (size_t t = 0; t < minThreads; t++) {
×
91
        auto progBar = std::make_unique<ProgressBar>("Thread:");
×
92
        auto progBarRaw = progBar.get();
×
93
        bars.emplace(t, progBarRaw);
×
94
        api.Append(std::move(progBar));
×
95
      }
96
    });
97

98
    auto prog = AppendNewLogLine<DetailedProgressBar>("Total: ");
×
99
    prog->ItemCount(stats.totalFiles);
×
100
    totalCount = prog;
×
101
  }
102

103
  UILines(size_t totalInputFiles) {
×
104
    totalCount = AppendNewLogLine<ProcessedFiles>();
×
105
    auto prog = AppendNewLogLine<DetailedProgressBar>("Total: ");
×
106
    prog->ItemCount(totalInputFiles);
×
107
    totalProgress = prog;
×
108
  }
109

110
  ~UILines() {
×
111
    ModifyElements([&](ElementAPI &api) {
×
112
      if (totalCount) {
×
113
        // Wait a little bit for internal queues to finish printing
114
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
×
115
        if (totalProgress) {
×
116
          auto data = static_cast<ProcessedFiles *>(totalCount);
×
117
          data->Finish();
118
          api.Release(data);
×
119
        } else {
120
          auto data = static_cast<DetailedProgressBar *>(totalCount);
×
121
          api.Remove(data);
×
122
        }
123
      }
124
      api.Clean();
×
125
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
×
126
    });
×
127
  }
128
};
129

130
void ScanModules(const std::string &appFolder, const std::string &appName) {
×
131
  DirectoryScanner sc;
×
132
  sc.AddFilter(std::string_view(".spk$"));
×
133
  sc.Scan(appFolder);
×
134

135
  for (auto &m : sc) {
×
136
    try {
137
      AFileInfo modulePath(m);
×
138
      auto moduleName = modulePath.GetFilename();
×
139
      const size_t firstDotPos = moduleName.find_first_of('.');
140
      std::string moduleNameStr(moduleName.substr(0, firstDotPos));
×
141
      APPContext ctx(moduleNameStr.data(), appFolder, appName);
×
142
      ctx.FromConfig();
×
143
    } catch (const std::runtime_error &e) {
×
144
      printerror(e.what());
×
145
    }
×
146
  }
147
}
148

149
void GenerateDocumentation(const std::string &appFolder,
×
150
                           const std::string &appName,
151
                           const std::string &templatePath) {
152
  DirectoryScanner sc;
×
153
  sc.AddFilter(std::string_view(".spk$"));
×
154
  sc.Scan(appFolder);
×
155
  std::set<std::string> modules;
156
  pugi::xml_document doc;
×
157

158
  if (!templatePath.empty()) {
×
159
    doc = XMLFromFile(templatePath);
×
160
  }
161

162
  for (auto &m : sc) {
×
163
    try {
164
      AFileInfo modulePath(m);
×
165
      auto moduleName = modulePath.GetFilename();
×
166
      const size_t firstDotPos = moduleName.find_first_of('.');
167
      std::string moduleNameStr(moduleName.substr(0, firstDotPos));
×
168
      modules.emplace(moduleNameStr);
169
    } catch (const std::runtime_error &e) {
×
170
      printerror(e.what());
×
171
    }
×
172
  }
173

174
  BinWritter_t<BinCoreOpenMode::Text> wr(appFolder + "/README.md");
×
175
  const char *toolsetName = "[[TOOLSET NAME]]";
176

177
  if (auto child = doc.child("toolset_name"); child) {
×
178
    toolsetName = child.text().as_string();
×
179
  }
180

181
  const char *toolsetDescription = "[[TOOLSET DESCRIPTION]]";
182

183
  if (auto child = doc.child("toolset_description"); child) {
×
184
    toolsetDescription = child.text().as_string();
×
185
  }
186

187
  wr.BaseStream() << "# " << toolsetName << "\n\n"
188
                  << toolsetDescription << "\n\n";
×
189

190
  for (auto &m : modules) {
×
191
    pugi::xml_node node = doc.child(m.data());
×
192
    APPContext ctx(m.data(), appFolder, appName);
×
193
    ctx.GetMarkdownDoc(wr.BaseStream(), node);
×
194
  }
195

196
  if (auto child = doc.child("toolset_footer"); child) {
×
197
    wr.BaseStream() << child.text().as_string();
×
198
  }
199
}
200

201
void PackModeBatch(Batch &batch) {
×
202
  struct PackData {
203
    size_t index = 0;
204
    std::unique_ptr<AppPackContext> archiveContext;
205
    std::string pbarLabel;
206
    DetailedProgressBar *progBar = nullptr;
207
    std::string folderPath;
208
  };
209

210
  auto payload = std::make_shared<PackData>();
211

212
  batch.forEachFolder = [payload, ctx = batch.ctx](const std::string &path,
×
213
                                                   AppPackStats stats) {
×
214
    payload->folderPath = path;
×
215
    payload->archiveContext.reset(ctx->NewArchive(path, stats));
216
    payload->pbarLabel = "Folder id " + std::to_string(payload->index++);
×
217
    payload->progBar =
×
218
        AppendNewLogLine<DetailedProgressBar>(payload->pbarLabel);
×
219
    payload->progBar->ItemCount(stats.numFiles);
×
220
    uint8 consoleDetail = 1 | uint8(ctx->info->multithreaded) << 1;
×
221
    ConsolePrintDetail(consoleDetail);
×
222
    printline("Processing: " << path);
×
223
  };
224

225
  batch.forEachFile = [payload](AppContextShare *iCtx) {
×
226
    payload->archiveContext->SendFile(
×
227
        iCtx->workingFile.GetFullPath().substr(payload->folderPath.size() + 1),
×
228
        iCtx->GetStream());
×
229
    (*payload->progBar)++;
×
230
  };
231

232
  batch.forEachFolderFinish = [payload] {
×
233
    ConsolePrintDetail(1);
×
234
    payload->archiveContext->Finish();
×
235
    payload->archiveContext.reset();
236
    RemoveLogLines(payload->progBar);
×
237
  };
238
}
239

240
auto ExtractStatBatch(Batch &batch) {
×
241
  struct ExtractStatsMaker : ExtractStats {
242
    std::mutex mtx;
243
    LoadingBar *scanBar;
244

245
    void Push(AppContextShare *ctx, size_t numFiles) {
×
246
      std::unique_lock<std::mutex> lg(mtx);
×
247
      archiveFiles.emplace(ctx->Hash(), numFiles);
×
248
      totalFiles += numFiles;
×
249
    }
250

251
    ~ExtractStatsMaker() { RemoveLogLines(scanBar); }
×
252
  };
253

254
  batch.keepFinishLines = false;
×
255
  auto sharedData = std::make_shared<ExtractStatsMaker>();
256
  uint8 consoleDetail = 1 | uint8(batch.ctx->info->multithreaded) << 1;
×
257
  ConsolePrintDetail(consoleDetail);
×
258
  sharedData->scanBar =
×
259
      AppendNewLogLine<LoadingBar>("Processing extract stats.");
×
260

261
  batch.forEachFile = [payload = sharedData,
×
262
                       ctx = batch.ctx](AppContextShare *iCtx) {
×
263
    payload->Push(iCtx, ctx->ExtractStat(std::bind(
×
264
                            [&](size_t offset, size_t size) {
265
                              return iCtx->GetBuffer(size, offset);
×
266
                            },
267
                            std::placeholders::_1, std::placeholders::_2)));
268
  };
269

270
  return sharedData;
×
271
}
272

273
void ProcessBatch(Batch &batch, ExtractStats *stats) {
×
274
  uint8 consoleDetail = 1 | uint8(batch.ctx->info->multithreaded) << 1;
×
275
  ConsolePrintDetail(consoleDetail);
×
276
  batch.forEachFile = [payload = std::make_shared<UILines>(*stats),
×
277
                       archiveFiles =
278
                           std::make_shared<decltype(stats->archiveFiles)>(
279
                               std::move(stats->archiveFiles)),
×
280
                       ctx = batch.ctx](AppContextShare *iCtx) {
×
281
    auto currentBar = payload->ChooseBar();
×
282
    if (currentBar) {
×
283
      currentBar->ItemCount(archiveFiles->at(iCtx->Hash()));
×
284
    }
285

286
    iCtx->forEachFile = [=] {
×
287
      if (currentBar) {
×
288
        (*currentBar)++;
289
      }
290

291
      if (payload->totalCount) {
×
292
        (*payload->totalCount)++;
293
      }
294
    };
295

296
    printline("Processing: " << iCtx->FullPath());
×
297
    ctx->ProcessFile(iCtx);
298
    if (payload->totalProgress) {
×
299
      (*payload->totalProgress)++;
300
    }
301
  };
302
}
303

304
void ProcessBatch(Batch &batch, size_t numFiles) {
×
305
  uint8 consoleDetail = 1 | uint8(batch.ctx->info->multithreaded) << 1;
×
306
  ConsolePrintDetail(consoleDetail);
×
307
  auto payload = std::make_shared<UILines>(numFiles);
308
  batch.forEachFile = [payload = payload,
×
309
                       ctx = batch.ctx](AppContextShare *iCtx) {
×
310
    printline("Processing: " << iCtx->FullPath());
×
311
    ctx->ProcessFile(iCtx);
312
    if (payload->totalProgress) {
×
313
      (*payload->totalProgress)++;
314
    }
315
    if (payload->totalCount) {
×
316
      (*payload->totalCount)++;
317
    }
318
  };
319

320
  auto totalFiles = std::make_shared<size_t>(numFiles);
×
321
  batch.updateFileCount = [payload = payload,
×
322
                           totalFiles = totalFiles](size_t addedFiles) {
×
323
    *totalFiles.get() += addedFiles;
×
324
    payload->totalProgress->ItemCount(*totalFiles);
×
325
  };
326
}
327

328
int Main(int argc, TCHAR *argv[]) {
×
329
  ConsolePrintDetail(1);
×
330
  AFileInfo appLocation(std::to_string(*argv));
×
331
  std::string appFolder(appLocation.GetFolder());
×
332
  std::string appName(appLocation.GetFilename());
×
333

334
  if (argc < 2) {
×
335
    printwarning(
×
336
        "No parameters provided, entering scan mode and generating config.");
337
    ScanModules(appFolder, appName);
×
338
    return 0;
339
  }
340

341
  auto moduleName = std::to_string(argv[1]);
×
342

343
  if (moduleName == "--make-doc") {
×
344
    std::string templatePath;
345

346
    if (argc < 3) {
×
347
      printwarning("Expexted template path!");
×
348
    } else {
349
      templatePath = std::to_string(argv[2]);
×
350
    }
351

352
    GenerateDocumentation(appFolder, appName, templatePath);
×
353
    return 0;
354
  }
355

356
  if (argc < 3) {
×
357
    printerror("Insufficient argument count, expected parameters.");
×
358
    return 1;
359
  }
360

361
  APPContext ctx;
362

363
  try {
364
    ctx = APPContext(moduleName.data(), appFolder, appName);
×
365
  } catch (const std::exception &e) {
×
366
    printerror(e.what());
×
367
    return 2;
368
  }
×
369

370
  ConsolePrintDetail(0);
×
371

372
  printline(ctx.info->header);
×
373
  printline(appHeader0 << appLocation.GetFilename() << ' ' << moduleName
×
374
                       << appHeader1);
375

376
  if (IsHelp(argv[2])) {
×
377
    ctx.PrintCLIHelp();
×
378
    return 0;
379
  }
380

381
  ConsolePrintDetail(1);
×
382
  bool dontLoadConfig = false;
×
383
  std::vector<bool> markedFiles(size_t(argc), false);
×
384
  size_t totalFiles = 0;
385

386
  // Handle cli options and switches
387
  for (int a = 2; a < argc; a++) {
×
388
    auto opt = argv[a];
×
389

390
    if (opt[0] == '-') {
×
391
      auto optStr = std::to_string(opt);
392
      std::string_view optsw(optStr);
393

394
      if (optsw != "--out") {
×
395
        // We won't use config file, reset all booleans to false,
396
        // so we can properly use cli switches
397
        [&] {
×
398
          if (dontLoadConfig) {
×
399
            return;
400
          }
401

402
          printinfo("CLI option detected, config won't be loaded, all booleans "
×
403
                    "set to false!");
404
          ctx.ResetSwitchSettings();
×
405
        }();
×
406
        dontLoadConfig = true;
×
407
      }
408
      optsw.remove_prefix(1);
409

410
      if (opt[0] == '-') {
×
411
        optsw.remove_prefix(1);
412
      }
413

414
      auto valStr = std::to_string(argv[a + 1]);
×
415

416
      if (auto retVal = ctx.ApplySetting(optsw, valStr); retVal > 0) {
×
417
        a++;
×
418
      }
419

420
    } else {
421
      markedFiles[a] = true;
422
      totalFiles++;
×
423
    }
424
  }
425

426
  if (!dontLoadConfig) {
×
427
    printinfo("Loading config: " << appName << ".config");
×
428
    ctx.FromConfig();
×
429
  }
430

431
  InitTempStorage();
×
432
  ctx.SetupModule();
×
433
  {
434
    Batch batch(&ctx, ctx.info->multithreaded * 50);
×
435

436
    if (ctx.NewArchive) {
×
437
      PackModeBatch(batch);
×
438
    } else {
439
      if (ctx.ExtractStat) {
×
440
        auto stats = ExtractStatBatch(batch);
×
441
        for (int a = 2; a < argc; a++) {
×
442
          if (!markedFiles.at(a)) {
×
443
            continue;
×
444
          }
445
          batch.AddFile(std::to_string(argv[a]));
×
446
        }
447

448
        batch.FinishBatch();
×
449
        batch.Clean();
×
450
        stats.get()->totalFiles += totalFiles;
×
451
        ProcessBatch(batch, stats.get());
×
452
      } else {
453
        ProcessBatch(batch, totalFiles);
×
454
      }
455
    }
456

457
    for (int a = 2; a < argc; a++) {
×
458
      if (!markedFiles.at(a)) {
×
459
        continue;
×
460
      }
461
      batch.AddFile(std::to_string(argv[a]));
×
462
    }
463

464
    batch.FinishBatch();
×
465
  }
466

467
  if (ctx.FinishContext) {
×
468
    ctx.FinishContext();
469
  }
470

471
  return 0;
472
}
473

474
int _tmain(int argc, TCHAR *argv[]) {
×
475
  es::SetupWinApiConsole();
476
  InitConsole();
×
477
  CleanTempStorages();
×
478

479
  int retVal = Main(argc, argv);
×
480

481
  CleanCurrentTempStorage();
×
482

483
#ifndef NDEBUG
484
  auto cacheStats = CacheGenerator::GlobalMetrics();
485
  PrintInfo("Cache search hits: ", cacheStats.numSearchHits,
486
            " search misses: ", cacheStats.numSearchMisses);
487
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
488
#endif
489
  TerminateConsole();
×
490
  return retVal;
491
}
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