a.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #include <err.h>
  2. #include <stdio.h>
  3. #include <stdint.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. #include <interface99.h>
  7. #include "arg.h"
  8. #include "util.h"
  9. #include "archives.h"
  10. char* argv0; /* for arg.h */
  11. /* clang-format off */
  12. static const char usage[] =
  13. "[OPTIONS] -- <subcmd> <args...>\n\n"
  14. "OPTIONS:\n"
  15. "\t-v verbose\n"
  16. "\t-V print version\n"
  17. "\t-h show this help\n"
  18. "\t-n dry run\n"
  19. "\t-e <ext:str> set custom extension\n"
  20. "\t-o <dir:path> set output directory (default: .)\n"
  21. "\t-f <file:path> set output file\n"
  22. "\t (only for {de,}compression of single files)\n"
  23. "\n"
  24. "\t-T <threads:int> set number of threads\n"
  25. "\t (for {de,}compressors that supports it)\n"
  26. "\t-M <limit:str> set memory limit\n"
  27. "\t (for {de,}compressors that supports it)\n"
  28. "\n"
  29. "<subcmds>:\n"
  30. "\tc <archive> <file...> create <archive> with files <file...>\n"
  31. "\tx <archive> extract files from <archive>\n"
  32. "\tl <archive> list contents of <archive>"
  33. ;
  34. /* clang-format on */
  35. static inline _Noreturn void
  36. die_usage(const char* msg)
  37. {
  38. errx(1, "%s\n\n%s", msg, usage);
  39. }
  40. /*
  41. * add -D_clang_check__ to you code checker to suppres errors
  42. * vim with neomake:
  43. * ```vim
  44. * let g:neomake_c_clangtidy_args = ['%t', '--', '-D_clang_check__']
  45. * let g:neomake_c_clang_args = neomake#makers#ft#c#clang().args + [
  46. * \ '-D_clang_check__']
  47. * ```
  48. */
  49. #ifndef VERSION
  50. # ifndef _clang_check__
  51. # error VERSION not defined
  52. # else
  53. # define VERSION "<_clang_check__>"
  54. # endif
  55. #endif /* VERSION */
  56. int
  57. main(int argc, char* argv[])
  58. {
  59. char action, *archive, *ext = NULL;
  60. struct Config cfg = {
  61. .dry_run = 0,
  62. .verbose = 0,
  63. .out_dir = ".",
  64. .out_file = NULL,
  65. .custom_ext = NULL,
  66. .threads = NULL,
  67. .memory_limit = NULL,
  68. };
  69. ARGBEGIN
  70. {
  71. case 'h':
  72. errx(0, "%s", usage);
  73. case 'V':
  74. errx(0, "%s", VERSION);
  75. case 'n':
  76. cfg.dry_run = !0;
  77. break;
  78. case 'v':
  79. cfg.verbose = !0;
  80. break;
  81. case 'o':
  82. cfg.out_dir = EARGF(die_usage("specify output directory"));
  83. break;
  84. case 'f':
  85. cfg.out_file = EARGF(die_usage("specify custom file name"));
  86. break;
  87. case 'e':
  88. ext = EARGF(die_usage("specify custom extension"));
  89. break;
  90. case 'T':
  91. cfg.threads = EARGF(die_usage("specify threads count"));
  92. break;
  93. case 'M':
  94. cfg.memory_limit = EARGF(die_usage("specify memory limit"));
  95. break;
  96. default:
  97. warnx("invalid argument: -%c", ARGC());
  98. }
  99. ARGEND;
  100. size_t custom_ext_size =
  101. ext ? (STR_SIZE("archive.") + strlen(ext) + 1) : 0;
  102. char custom_ext_buf[custom_ext_size
  103. + (!custom_ext_size /* to not be zero-size */)];
  104. if (!!custom_ext_size) {
  105. snprintf(custom_ext_buf, custom_ext_size, "archive.%s", ext);
  106. cfg.custom_ext = custom_ext_buf;
  107. }
  108. if (!*argv) die_usage("specify action");
  109. if (!**argv || !!(*argv)[1]) die_usage("invalid action");
  110. action = **(argv++);
  111. if (!*argv) die_usage("specify archive file");
  112. archive = *(argv++);
  113. cfg.files = argv;
  114. cfg.files_count = argc - 2;
  115. cfg.custom_ext = OR(cfg.custom_ext, archive);
  116. for (size_t i = 0; i < LEN(archives); i++) {
  117. if (!!VCALL_SUPER(archives[i], Match, match, cfg.custom_ext)) {
  118. int (*func)(void*, char* const);
  119. switch (action) {
  120. case 'l':
  121. func = archives[i].vptr->List->list;
  122. break;
  123. case 'x':
  124. func = archives[i].vptr->Extract->extract;
  125. break;
  126. case 'c':
  127. if (cfg.files_count < 1)
  128. die_usage(
  129. "provide some files to create "
  130. "archive");
  131. func = archives[i].vptr->Create->create;
  132. break;
  133. default:
  134. errx(1,
  135. "invalid action: %c\n\n%s",
  136. action,
  137. usage);
  138. }
  139. return func(&cfg, archive);
  140. }
  141. }
  142. errx(1, "no awailiable extractros found for '%s'", cfg.custom_ext);
  143. }