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

PredatorCZ / PreCore / 488

02 Oct 2023 03:56PM UTC coverage: 55.265% (-0.05%) from 55.317%
488

push

github-actions-ci

PredatorCZ
fix sending wrong paths to pack context

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

4146 of 7502 relevant lines covered (55.27%)

8926.43 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
    if (iCtx->workingFile.GetFullPath().starts_with(payload->folderPath)) {
×
227
      int notSlash = !payload->folderPath.ends_with('/');
×
228
      payload->archiveContext->SendFile(iCtx->workingFile.GetFullPath().substr(
×
229
                                            payload->folderPath.size() + notSlash),
×
230
                                        iCtx->GetStream());
×
231
    } else {
232
      payload->archiveContext->SendFile(iCtx->workingFile.GetFullPath(),
×
233
                                        iCtx->GetStream());
×
234
    }
235
    (*payload->progBar)++;
×
236
  };
237

238
  batch.forEachFolderFinish = [payload] {
×
239
    ConsolePrintDetail(1);
×
240
    payload->archiveContext->Finish();
×
241
    payload->archiveContext.reset();
242
    RemoveLogLines(payload->progBar);
×
243
  };
244
}
245

246
auto ExtractStatBatch(Batch &batch) {
×
247
  struct ExtractStatsMaker : ExtractStats {
248
    std::mutex mtx;
249
    LoadingBar *scanBar;
250

251
    void Push(AppContextShare *ctx, size_t numFiles) {
×
252
      std::unique_lock<std::mutex> lg(mtx);
×
253
      archiveFiles.emplace(ctx->Hash(), numFiles);
×
254
      totalFiles += numFiles;
×
255
    }
256

257
    ~ExtractStatsMaker() { RemoveLogLines(scanBar); }
×
258
  };
259

260
  batch.keepFinishLines = false;
×
261
  auto sharedData = std::make_shared<ExtractStatsMaker>();
262
  uint8 consoleDetail = 1 | uint8(batch.ctx->info->multithreaded) << 1;
×
263
  ConsolePrintDetail(consoleDetail);
×
264
  sharedData->scanBar =
×
265
      AppendNewLogLine<LoadingBar>("Processing extract stats.");
×
266

267
  batch.forEachFile = [payload = sharedData,
×
268
                       ctx = batch.ctx](AppContextShare *iCtx) {
×
269
    payload->Push(iCtx, ctx->ExtractStat(std::bind(
×
270
                            [&](size_t offset, size_t size) {
271
                              return iCtx->GetBuffer(size, offset);
×
272
                            },
273
                            std::placeholders::_1, std::placeholders::_2)));
274
  };
275

276
  return sharedData;
×
277
}
278

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

292
    iCtx->forEachFile = [=] {
×
293
      if (currentBar) {
×
294
        (*currentBar)++;
295
      }
296

297
      if (payload->totalCount) {
×
298
        (*payload->totalCount)++;
299
      }
300
    };
301

302
    printline("Processing: " << iCtx->FullPath());
×
303
    ctx->ProcessFile(iCtx);
304
    if (payload->totalProgress) {
×
305
      (*payload->totalProgress)++;
306
    }
307
  };
308
}
309

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

326
  auto totalFiles = std::make_shared<size_t>(numFiles);
×
327
  batch.updateFileCount = [payload = payload,
×
328
                           totalFiles = totalFiles](size_t addedFiles) {
×
329
    *totalFiles.get() += addedFiles;
×
330
    payload->totalProgress->ItemCount(*totalFiles);
×
331
  };
332
}
333

334
int Main(int argc, TCHAR *argv[]) {
×
335
  ConsolePrintDetail(1);
×
336
  AFileInfo appLocation(std::to_string(*argv));
×
337
  std::string appFolder(appLocation.GetFolder());
×
338
  std::string appName(appLocation.GetFilename());
×
339
  es::SetDllRunPath(appFolder + "lib");
×
340

341
  if (argc < 2) {
×
342
    printwarning(
×
343
        "No parameters provided, entering scan mode and generating config.");
344
    ScanModules(appFolder, appName);
×
345
    return 0;
346
  }
347

348
  auto moduleName = std::to_string(argv[1]);
×
349

350
  if (moduleName == "--make-doc") {
×
351
    std::string templatePath;
352

353
    if (argc < 3) {
×
354
      printwarning("Expexted template path!");
×
355
    } else {
356
      templatePath = std::to_string(argv[2]);
×
357
    }
358

359
    GenerateDocumentation(appFolder, appName, templatePath);
×
360
    return 0;
361
  }
362

363
  if (argc < 3) {
×
364
    printerror("Insufficient argument count, expected parameters.");
×
365
    return 1;
366
  }
367

368
  APPContext ctx;
369

370
  try {
371
    ctx = APPContext(moduleName.data(), appFolder, appName);
×
372
  } catch (const std::exception &e) {
×
373
    printerror(e.what());
×
374
    return 2;
375
  }
×
376

377
  ConsolePrintDetail(0);
×
378

379
  printline(ctx.info->header);
×
380
  printline(appHeader0 << appLocation.GetFilename() << ' ' << moduleName
×
381
                       << appHeader1);
382

383
  if (IsHelp(argv[2])) {
×
384
    ctx.PrintCLIHelp();
×
385
    return 0;
386
  }
387

388
  ConsolePrintDetail(1);
×
389
  bool dontLoadConfig = false;
×
390
  std::vector<bool> markedFiles(size_t(argc), false);
×
391
  size_t totalFiles = 0;
392

393
  // Handle cli options and switches
394
  for (int a = 2; a < argc; a++) {
×
395
    auto opt = argv[a];
×
396

397
    if (opt[0] == '-') {
×
398
      auto optStr = std::to_string(opt);
399
      std::string_view optsw(optStr);
400

401
      if (optsw != "--out") {
×
402
        // We won't use config file, reset all booleans to false,
403
        // so we can properly use cli switches
404
        [&] {
×
405
          if (dontLoadConfig) {
×
406
            return;
407
          }
408

409
          printinfo("CLI option detected, config won't be loaded, all booleans "
×
410
                    "set to false!");
411
          ctx.ResetSwitchSettings();
×
412
        }();
×
413
        dontLoadConfig = true;
×
414
      }
415
      optsw.remove_prefix(1);
416

417
      if (opt[0] == '-') {
×
418
        optsw.remove_prefix(1);
419
      }
420

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

423
      if (auto retVal = ctx.ApplySetting(optsw, valStr); retVal > 0) {
×
424
        a++;
×
425
      }
426

427
    } else {
428
      markedFiles[a] = true;
429
      totalFiles++;
×
430
    }
431
  }
432

433
  if (!dontLoadConfig) {
×
434
    printinfo("Loading config: " << appName << ".config");
×
435
    ctx.FromConfig();
×
436
  }
437

438
  InitTempStorage();
×
439
  ctx.SetupModule();
×
440
  {
441
    Batch batch(&ctx, ctx.info->multithreaded * 50);
×
442

443
    if (ctx.NewArchive) {
×
444
      PackModeBatch(batch);
×
445
    } else {
446
      if (ctx.ExtractStat) {
×
447
        auto stats = ExtractStatBatch(batch);
×
448
        for (int a = 2; a < argc; a++) {
×
449
          if (!markedFiles.at(a)) {
×
450
            continue;
×
451
          }
452
          batch.AddFile(std::to_string(argv[a]));
×
453
        }
454

455
        batch.FinishBatch();
×
456
        batch.Clean();
×
457
        stats.get()->totalFiles += totalFiles;
×
458
        ProcessBatch(batch, stats.get());
×
459
      } else {
460
        ProcessBatch(batch, totalFiles);
×
461
      }
462
    }
463

464
    for (int a = 2; a < argc; a++) {
×
465
      if (!markedFiles.at(a)) {
×
466
        continue;
×
467
      }
468
      batch.AddFile(std::to_string(argv[a]));
×
469
    }
470

471
    batch.FinishBatch();
×
472
  }
473

474
  if (ctx.FinishContext) {
×
475
    ctx.FinishContext();
476
  }
477

478
  return 0;
479
}
480

481
int _tmain(int argc, TCHAR *argv[]) {
×
482
  es::SetupWinApiConsole();
483
  InitConsole();
×
484
  CleanTempStorages();
×
485

486
  int retVal = Main(argc, argv);
×
487

488
  CleanCurrentTempStorage();
×
489

490
#ifndef NDEBUG
491
  auto cacheStats = CacheGenerator::GlobalMetrics();
492
  PrintInfo("Cache search hits: ", cacheStats.numSearchHits,
493
            " search misses: ", cacheStats.numSearchMisses);
494
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
495
#endif
496
  TerminateConsole();
×
497
  return retVal;
498
}
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