A tool for adding anime to your anidb list.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

540 lignes
16KB

  1. #include <stddef.h>
  2. #include <inttypes.h>
  3. #include <getopt.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <stdbool.h>
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #include <unistd.h>
  11. #include <errno.h>
  12. #include <assert.h>
  13. //#include <toml.h>
  14. #include <limits.h>
  15. #include <time.h>
  16. #include <unistd.h>
  17. #include <ctype.h>
  18. #include "config.h"
  19. #include "error.h"
  20. #include "util.h"
  21. static int show_help(struct conf_entry *ce);
  22. static int config_parse_file();
  23. static enum error config_required_check();
  24. static int config_set_str(struct conf_entry *ce, char *arg);
  25. static int config_set_port(struct conf_entry *ce, char *arg);
  26. static int config_set_bool(struct conf_entry *ce, char *arg);
  27. //static int config_def_cachedb(struct conf_entry *ce);
  28. //static int config_action_write_config(struct conf_entry *ce);
  29. /* Everything not explicitly defined, is 0 */
  30. /* If an option only has a long name, the short name also has to be
  31. * defined. For example, a number larger than UCHAR_MAX */
  32. static struct conf_entry options[] = {
  33. { .l_name = "help", .s_name = 'h', .has_arg = no_argument,
  34. .action_func = show_help, .in_args = true,
  35. .type = OTYPE_ACTION, .handle_order = 0 },
  36. /*
  37. { .l_name = "config-dir", .s_name = 'b', .has_arg = required_argument,
  38. .default_func = config_def_config_dir, .set_func = config_set_str,
  39. .in_args = true, .type = OTYPE_S, .handle_order = 0 },
  40. { .l_name = "default-download-dir", .s_name = 'd', .has_arg = required_argument,
  41. .default_func = config_def_default_download_dir, .set_func = config_set_str,
  42. .in_file = true, .in_args = true, .type = OTYPE_S, .handle_order = 1 },
  43. { .l_name = "port", .s_name = 'p', .has_arg = required_argument,
  44. .set_func = config_set_port, .value.hu = 21729, .value_is_set = true,
  45. .in_file = true, .in_args = true, .type = OTYPE_HU, .handle_order = 1 },
  46. { .l_name = "foreground", .s_name = 'f', .has_arg = no_argument,
  47. .set_func = config_set_bool, .value.b = false, .value_is_set = true,
  48. .in_args = true, .type = OTYPE_B, .handle_order = 1 },
  49. { .l_name = "write-config", .s_name = UCHAR_MAX + 1, .has_arg = no_argument,
  50. .action_func = config_action_write_config, .value_is_set = true,
  51. .in_args = true, .type = OTYPE_ACTION, .handle_order = 2 },
  52. { .l_name = "peer-id", .s_name = UCHAR_MAX + 2, .has_arg = required_argument,
  53. .default_func = config_def_peer_id, .type = OTYPE_S, .handle_order = 1 },
  54. */
  55. { .l_name = "username", .s_name = 'u', .has_arg = required_argument,
  56. .set_func = config_set_str, .in_args = true, .in_file = true,
  57. .type = OTYPE_S, .handle_order = 1 },
  58. { .l_name = "password", .s_name = 'p', .has_arg = required_argument,
  59. .set_func = config_set_str, .in_args = true, .in_file = true,
  60. .type = OTYPE_S, .handle_order = 1 },
  61. { .l_name = "port", .s_name = 'P', .has_arg = required_argument,
  62. .set_func = config_set_port, .in_args = true, .in_file = true,
  63. .type = OTYPE_HU, .handle_order = 1, .value.hu = 29937,
  64. .value_is_set = true },
  65. { .l_name = "api-server", .s_name = UCHAR_MAX + 1, .has_arg = required_argument,
  66. .set_func = config_set_str, .in_args = true, .in_file = true,
  67. .type = OTYPE_S, .handle_order = 1, .value.s = "api.anidb.net:9000",
  68. .value_is_set = true },
  69. { .l_name = "api-key", .s_name = 'k', .has_arg = required_argument,
  70. .set_func = config_set_str, .in_args = true, .in_file = true,
  71. .type = OTYPE_S, .handle_order = 1, },
  72. { .l_name = "save-session", .s_name = 's', .has_arg = no_argument,
  73. .set_func = config_set_bool, .in_args = true, .in_file = true,
  74. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },
  75. { .l_name = "destroy-session", .s_name = 'S', .has_arg = no_argument,
  76. .set_func = config_set_bool, .in_args = true, .in_file = false,
  77. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },
  78. { .l_name = "watched", .s_name = 'w', .has_arg = no_argument,
  79. .set_func = config_set_bool, .in_args = true,
  80. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },
  81. { .l_name = "link", .s_name = 'l', .has_arg = no_argument,
  82. .set_func = config_set_bool, .in_args = true,
  83. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },
  84. { .l_name = "cachedb", .s_name = 'd', .has_arg = required_argument,
  85. .set_func = config_set_str, .in_args = true, .in_file = true,
  86. .type = OTYPE_S, .handle_order = 1, /*.default_func = config_def_cachedb*/ },
  87. { .l_name = "debug", .s_name = 'D', .has_arg = no_argument,
  88. .set_func = config_set_bool, .in_args = true, .in_file = true,
  89. .type = OTYPE_B, .handle_order = 1, .value_is_set = true, },
  90. /*### cmd ###*/
  91. { .l_name = "server-version", .s_name = UCHAR_MAX + 2,
  92. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  93. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },
  94. { .l_name = "version", .s_name = 'v',
  95. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  96. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },
  97. { .l_name = "uptime", .s_name = UCHAR_MAX + 3,
  98. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  99. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },
  100. { .l_name = "ed2k", .s_name = 'e',
  101. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  102. .type = OTYPE_B, .handle_order = 1 },
  103. { .l_name = "add", .s_name = 'a',
  104. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  105. .type = OTYPE_B, .handle_order = 1 },
  106. /*{ .l_name = "stats", .s_name = UCHAR_MAX + 4,
  107. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  108. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },*/
  109. };
  110. static const size_t options_count = sizeof(options) / sizeof(options[0]);
  111. static const char **opt_argv = NULL;
  112. static int opt_argc = 0;
  113. static void config_build_getopt_args(char out_sopt[options_count * 2 + 1],
  114. struct option out_lopt[options_count + 1])
  115. {
  116. int i_sopt = 0, i_lopt = 0;
  117. for (int i = 0; i < options_count; i++) {
  118. /* Short options */
  119. if (options[i].s_name && options[i].s_name <= UCHAR_MAX) {
  120. out_sopt[i_sopt++] = options[i].s_name;
  121. if (options[i].has_arg == required_argument)
  122. out_sopt[i_sopt++] = ':';
  123. assert(options[i].has_arg == required_argument ||
  124. options[i].has_arg == no_argument);
  125. }
  126. /* Long options */
  127. if (options[i].l_name) {
  128. assert(options[i].s_name);
  129. out_lopt[i_lopt].name = options[i].l_name;
  130. out_lopt[i_lopt].has_arg = options[i].has_arg;
  131. out_lopt[i_lopt].flag = NULL;
  132. out_lopt[i_lopt].val = options[i].s_name;
  133. i_lopt++;
  134. }
  135. }
  136. out_sopt[i_sopt] = '\0';
  137. memset(&out_lopt[i_lopt], 0, sizeof(struct option));
  138. }
  139. static int config_read_args(int argc, char **argv, char sopt[options_count * 2 + 1],
  140. struct option lopt[options_count + 1], int level)
  141. {
  142. int optc, err = NOERR;
  143. optind = 1;
  144. while ((optc = getopt_long(argc, argv, sopt,
  145. lopt, NULL)) >= 0) {
  146. bool handled = false;
  147. for (int i = 0; i < options_count; i++) {
  148. if (options[i].handle_order != level) {
  149. /* Lie a lil :x */
  150. handled = true;
  151. continue;
  152. }
  153. if (optc == options[i].s_name) {
  154. if (options[i].type != OTYPE_ACTION)
  155. err = options[i].set_func(&options[i], optarg);
  156. else
  157. err = options[i].action_func(&options[i]);
  158. if (err != NOERR)
  159. goto end;
  160. options[i].value_is_set = true;
  161. handled = true;
  162. break;
  163. }
  164. }
  165. if (handled)
  166. continue;
  167. if (optc == '?') {
  168. err = ERR_OPT_FAILED;
  169. goto end;
  170. } else {
  171. fprintf(stderr, "Unhandled option? '%c'\n", optc);
  172. err = ERR_OPT_UNHANDLED;
  173. goto end;
  174. }
  175. }
  176. end:
  177. return err;
  178. }
  179. static enum error config_required_check()
  180. {
  181. enum error err = NOERR;
  182. for (int i = 0; i < options_count; i++) {
  183. if (options[i].required && !options[i].value_is_set) {
  184. printf("Argument %s is required!\n", options[i].l_name);
  185. err = ERR_OPT_REQUIRED;
  186. }
  187. }
  188. return err;
  189. }
  190. enum error config_parse(int argc, char **argv)
  191. {
  192. enum error err = NOERR;
  193. char sopt[options_count * 2 + 1];
  194. struct option lopt[options_count + 1];
  195. opt_argv = (const char**)argv;
  196. opt_argc = argc;
  197. config_build_getopt_args(sopt, lopt);
  198. err = config_read_args(argc, argv, sopt, lopt, 0);
  199. if (err != NOERR)
  200. goto end;
  201. err = config_parse_file();
  202. if (err != NOERR)
  203. goto end;
  204. err = config_read_args(argc, argv, sopt, lopt, 1);
  205. if (err != NOERR)
  206. goto end;
  207. /* Set defaults for those, that didn't got set above */
  208. for (int i = 0; i < options_count; i++) {
  209. if (!options[i].value_is_set && options[i].type != OTYPE_ACTION &&
  210. options[i].default_func) {
  211. err = options[i].default_func(&options[i]);
  212. if (err != NOERR)
  213. goto end;
  214. options[i].value_is_set = true;
  215. }
  216. }
  217. err = config_read_args(argc, argv, sopt, lopt, 2);
  218. if (err != NOERR)
  219. goto end;
  220. err = config_required_check();
  221. end:
  222. if (err != NOERR)
  223. config_free();
  224. return err;
  225. }
  226. #if 0
  227. static int config_def_config_dir(struct conf_entry *ce)
  228. {
  229. char *dir;
  230. int len;
  231. const char *format = "%s/.config/" CONFIG_DIR_NAME;
  232. const char *home_env = getenv("HOME");
  233. if (!home_env) {
  234. /* Fix this at a later date with getuid and getpw */
  235. fprintf(stderr, "HOME environment variable not found!\n");
  236. return ERR_NOTFOUND;
  237. }
  238. len = snprintf(NULL, 0, format, home_env);
  239. if (len == -1) {
  240. int err = errno;
  241. fprintf(stderr, "Failed to call funky snpintf: %s\n", strerror(err));
  242. return err;
  243. }
  244. dir = malloc(len + 1);
  245. sprintf(dir, format, home_env);
  246. ce->value.s = dir;
  247. ce->value_is_dyn = true;
  248. return NOERR;
  249. }
  250. #endif
  251. #if 0
  252. static int config_def_cachedb(struct conf_entry *ce)
  253. {
  254. bool dh_free = false;
  255. const char *data_home = getenv("XDG_DATA_HOME");
  256. if (!data_home) {
  257. const char *home = util_get_home();
  258. if (!home)
  259. return ERR_OPT_FAILED;
  260. sprintf(NULL, "%s/.local/share", home);
  261. }
  262. return NOERR;
  263. }
  264. #endif
  265. static int config_set_str(struct conf_entry *ce, char *arg)
  266. {
  267. // TODO use realpath(3), when necessary
  268. ce->value.s = arg;
  269. return NOERR;
  270. }
  271. static int config_set_port(struct conf_entry *ce, char *arg)
  272. {
  273. long portval = strtol(arg, NULL, 10);
  274. /* A zero return will be invalid no matter if strtol succeeded or not */
  275. if (portval > UINT16_MAX || portval <= 0) {
  276. fprintf(stderr, "Invalid port value '%s'\n", arg);
  277. return ERR_OPT_INVVAL;
  278. }
  279. ce->value.hu = (uint16_t)portval;
  280. return NOERR;
  281. }
  282. static int config_set_bool(struct conf_entry *ce, char *arg)
  283. {
  284. ce->value.b = true;
  285. return NOERR;
  286. }
  287. static int show_help(struct conf_entry *ce)
  288. {
  289. printf("Todo...\n");
  290. return ERR_OPT_EXIT;
  291. }
  292. static int config_parse_file()
  293. {
  294. // TODO implement this
  295. #if 0
  296. assert(conf.config_file_path);
  297. FILE *f = fopen(conf.config_file_path, "rb");
  298. char errbuf[200];
  299. toml_table_t *tml = toml_parse_file(f, errbuf, sizeof(errbuf));
  300. fclose(f);
  301. if (!tml) {
  302. fprintf(stderr, "Failed to parse config toml: %s\n", errbuf);
  303. return ERR_TOML_PARSE_ERROR;
  304. }
  305. toml_datum_t port = toml_int_in(tml, "port");
  306. if (port.ok)
  307. conf.port = (uint16_t)port.u.i;
  308. else
  309. fprintf(stderr, "Failed to parse port from config toml: %s\n", errbuf);
  310. toml_datum_t dldir = toml_string_in(tml, "default_download_dir");
  311. if (dldir.ok) {
  312. conf.default_download_dir = dldir.u.s;
  313. printf("%s\n", dldir.u.s);
  314. conf_dyn.default_download_dir = dldir.u.s;
  315. conf_dyn.default_download_dir = true;
  316. /* TODO is this always malloced?? if yes, remve dyn check */
  317. } else {
  318. fprintf(stderr, "Failed to parse download dir from config toml: %s\n", errbuf);
  319. }
  320. toml_free(tml);
  321. #endif
  322. return NOERR;
  323. }
  324. int config_free()
  325. {
  326. for (int i = 0; i < options_count; i++) {
  327. if (options[i].value_is_dyn) {
  328. free(options[i].value.s);
  329. options[i].value.s = NULL;
  330. options[i].value_is_dyn = false;
  331. options[i].value_is_set = false;
  332. }
  333. }
  334. return NOERR;
  335. }
  336. #if 0
  337. static int config_action_write_config(struct conf_entry *ce)
  338. {
  339. /* This is the success return here */
  340. int err = ERR_OPT_EXIT, plen;
  341. const char *config_dir;
  342. FILE *f = NULL;
  343. config_dir = config_get("config-dir");
  344. plen = snprintf(NULL, 0, "%s/%s", config_dir, CONFIG_FILE_NAME);
  345. char path[plen + 1];
  346. snprintf(path, plen + 1, "%s/%s", config_dir, CONFIG_FILE_NAME);
  347. for (int i = 0; i < 2; i++) {
  348. f = fopen(path, "wb");
  349. if (!f) {
  350. int errn = errno;
  351. if (errn == ENOENT && i == 0) {
  352. /* Try to create parent directory */
  353. if (mkdir(config_dir, 0755) == -1) {
  354. err = errno;
  355. fprintf(stderr, "Config mkdir failed: %s\n", strerror(err));
  356. goto end;
  357. }
  358. } else {
  359. err = errn;
  360. fprintf(stderr, "Config fopen failed: %s\n", strerror(err));
  361. goto end;
  362. }
  363. } else {
  364. break;
  365. }
  366. }
  367. for (int i = 0; i < options_count; i++) {
  368. if (!options[i].in_file)
  369. continue;
  370. fprintf(f, "%s = ", options[i].l_name);
  371. switch (options[i].type) {
  372. case OTYPE_S:
  373. // TODO toml escaping
  374. fprintf(f, "\"%s\"\n", options[i].value.s);
  375. break;
  376. case OTYPE_HU:
  377. fprintf(f, "%hu\n", options[i].value.hu);
  378. break;
  379. case OTYPE_B:
  380. fprintf(f, "%s\n", options[i].value.b ? "true" : "false");
  381. break;
  382. default:
  383. break;
  384. }
  385. }
  386. config_dump();
  387. end:
  388. if (f)
  389. fclose(f);
  390. return err;
  391. }
  392. #endif
  393. enum error config_get(const char *key, void **out)
  394. {
  395. enum error err = ERR_OPT_NOTFOUND;
  396. for (int i = 0; i < options_count; i++) {
  397. struct conf_entry *cc = &options[i];
  398. if (strcmp(cc->l_name, key) == 0) {
  399. if (cc->value_is_set) {
  400. if (out)
  401. *out = &cc->value.s;
  402. err = NOERR;
  403. } else {
  404. err = ERR_OPT_UNSET;
  405. }
  406. break;
  407. }
  408. }
  409. return err;
  410. }
  411. const char *config_get_nonopt(int index)
  412. {
  413. if (index >= config_get_nonopt_count())
  414. return NULL;
  415. return opt_argv[optind + index];
  416. }
  417. int config_get_nonopt_count()
  418. {
  419. return opt_argc - optind;
  420. }
  421. void config_dump()
  422. {
  423. for (int i = 0; i < options_count; i++) {
  424. if (options[i].type == OTYPE_ACTION)
  425. continue;
  426. printf("%s: ", options[i].l_name);
  427. if (!options[i].value_is_set) {
  428. printf("[UNSET (>.<)]\n");
  429. continue;
  430. }
  431. switch (options[i].type) {
  432. case OTYPE_S:
  433. printf("%s\n", options[i].value.s);
  434. break;
  435. case OTYPE_HU:
  436. printf("%hu\n", options[i].value.hu);
  437. break;
  438. case OTYPE_B:
  439. printf("%s\n", options[i].value.b ? "True" : "False");
  440. break;
  441. default:
  442. printf("Error :(\n");
  443. break;
  444. }
  445. }
  446. }