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

PredatorCZ / PreCore / 482

13 Sep 2023 09:36PM UTC coverage: 55.309% (-0.008%) from 55.317%
482

push

github-actions-ci

PredatorCZ
add SetDllDirectory

1 of 1 new or added line in 1 file covered. (100.0%)

4146 of 7496 relevant lines covered (55.31%)

8934.17 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
  es::SetDllDirectory(appFolder + "lib");
×
334

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

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

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

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

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

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

362
  APPContext ctx;
363

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

371
  ConsolePrintDetail(0);
×
372

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

465
    batch.FinishBatch();
×
466
  }
467

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

472
  return 0;
473
}
474

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

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

482
  CleanCurrentTempStorage();
×
483

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