A tool for adding anime to your anidb list.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

548 wiersze
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. { .l_name = "wdate", .s_name = UCHAR_MAX + 4, .has_arg = required_argument,
  91. .set_func = config_set_str, .in_args = true,
  92. .type = OTYPE_S, .handle_order = 1, },
  93. /*### cmd ###*/
  94. { .l_name = "server-version", .s_name = UCHAR_MAX + 2,
  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 = "version", .s_name = 'v',
  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 = "uptime", .s_name = UCHAR_MAX + 3,
  101. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  102. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },
  103. { .l_name = "ed2k", .s_name = 'e',
  104. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  105. .type = OTYPE_B, .handle_order = 1 },
  106. { .l_name = "add", .s_name = 'a',
  107. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  108. .type = OTYPE_B, .handle_order = 1 },
  109. { .l_name = "modify", .s_name = 'W',
  110. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  111. .type = OTYPE_B, .handle_order = 1 },
  112. /*{ .l_name = "stats", .s_name = UCHAR_MAX + 5,
  113. .has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
  114. .type = OTYPE_B, .handle_order = 1, .value_is_set = true },*/
  115. };
  116. static const size_t options_count = sizeof(options) / sizeof(options[0]);
  117. static const char **opt_argv = NULL;
  118. static int opt_argc = 0;
  119. static void config_build_getopt_args(char out_sopt[options_count * 2 + 1],
  120. struct option out_lopt[options_count + 1])
  121. {
  122. int i_sopt = 0, i_lopt = 0;
  123. for (int i = 0; i < options_count; i++) {
  124. /* Short options */
  125. if (options[i].s_name && options[i].s_name <= UCHAR_MAX) {
  126. out_sopt[i_sopt++] = options[i].s_name;
  127. if (options[i].has_arg == required_argument)
  128. out_sopt[i_sopt++] = ':';
  129. assert(options[i].has_arg == required_argument ||
  130. options[i].has_arg == no_argument);
  131. }
  132. /* Long options */
  133. if (options[i].l_name) {
  134. assert(options[i].s_name);
  135. out_lopt[i_lopt].name = options[i].l_name;
  136. out_lopt[i_lopt].has_arg = options[i].has_arg;
  137. out_lopt[i_lopt].flag = NULL;
  138. out_lopt[i_lopt].val = options[i].s_name;
  139. i_lopt++;
  140. }
  141. }
  142. out_sopt[i_sopt] = '\0';
  143. memset(&out_lopt[i_lopt], 0, sizeof(struct option));
  144. }
  145. static int config_read_args(int argc, char **argv, char sopt[options_count * 2 + 1],
  146. struct option lopt[options_count + 1], int level)
  147. {
  148. int optc, err = NOERR;
  149. optind = 1;
  150. while ((optc = getopt_long(argc, argv, sopt,
  151. lopt, NULL)) >= 0) {
  152. bool handled = false;
  153. for (int i = 0; i < options_count; i++) {
  154. if (options[i].handle_order != level) {
  155. /* Lie a lil :x */
  156. handled = true;
  157. continue;
  158. }
  159. if (optc == options[i].s_name) {
  160. if (options[i].type != OTYPE_ACTION)
  161. err = options[i].set_func(&options[i], optarg);
  162. else
  163. err = options[i].action_func(&options[i]);
  164. if (err != NOERR)
  165. goto end;
  166. options[i].value_is_set = true;
  167. handled = true;
  168. break;
  169. }
  170. }
  171. if (handled)
  172. continue;
  173. if (optc == '?') {
  174. err = ERR_OPT_FAILED;
  175. goto end;
  176. } else {
  177. fprintf(stderr, "Unhandled option? '%c'\n", optc);
  178. err = ERR_OPT_UNHANDLED;
  179. goto end;
  180. }
  181. }
  182. end:
  183. return err;
  184. }
  185. static enum error config_required_check()
  186. {
  187. enum error err = NOERR;
  188. for (int i = 0; i < options_count; i++) {
  189. if (options[i].required && !options[i].value_is_set) {
  190. printf("Argument %s is required!\n", options[i].l_name);
  191. err = ERR_OPT_REQUIRED;
  192. }
  193. }
  194. return err;
  195. }
  196. enum error config_parse(int argc, char **argv)
  197. {
  198. enum error err = NOERR;
  199. char sopt[options_count * 2 + 1];
  200. struct option lopt[options_count + 1];
  201. opt_argv = (const char**)argv;
  202. opt_argc = argc;
  203. config_build_getopt_args(sopt, lopt);
  204. err = config_read_args(argc, argv, sopt, lopt, 0);
  205. if (err != NOERR)
  206. goto end;
  207. err = config_parse_file();
  208. if (err != NOERR)
  209. goto end;
  210. err = config_read_args(argc, argv, sopt, lopt, 1);
  211. if (err != NOERR)
  212. goto end;
  213. /* Set defaults for those, that didn't got set above */
  214. for (int i = 0; i < options_count; i++) {
  215. if (!options[i].value_is_set && options[i].type != OTYPE_ACTION &&
  216. options[i].default_func) {
  217. err = options[i].default_func(&options[i]);
  218. if (err != NOERR)
  219. goto end;
  220. options[i].value_is_set = true;
  221. }
  222. }
  223. err = config_read_args(argc, argv, sopt, lopt, 2);
  224. if (err != NOERR)
  225. goto end;
  226. err = config_required_check();
  227. end:
  228. if (err != NOERR)
  229. config_free();
  230. return err;
  231. }
  232. #if 0
  233. static int config_def_config_dir(struct conf_entry *ce)
  234. {
  235. char *dir;
  236. int len;
  237. const char *format = "%s/.config/" CONFIG_DIR_NAME;
  238. const char *home_env = getenv("HOME");
  239. if (!home_env) {
  240. /* Fix this at a later date with getuid and getpw */
  241. fprintf(stderr, "HOME environment variable not found!\n");
  242. return ERR_NOTFOUND;
  243. }
  244. len = snprintf(NULL, 0, format, home_env);
  245. if (len == -1) {
  246. int err = errno;
  247. fprintf(stderr, "Failed to call funky snpintf: %s\n", strerror(err));
  248. return err;
  249. }
  250. dir = malloc(len + 1);
  251. sprintf(dir, format, home_env);
  252. ce->value.s = dir;
  253. ce->value_is_dyn = true;
  254. return NOERR;
  255. }
  256. #endif
  257. #if 0
  258. static int config_def_cachedb(struct conf_entry *ce)
  259. {
  260. bool dh_free = false;
  261. const char *data_home = getenv("XDG_DATA_HOME");
  262. if (!data_home) {
  263. const char *home = util_get_home();
  264. if (!home)
  265. return ERR_OPT_FAILED;
  266. sprintf(NULL, "%s/.local/share", home);
  267. }
  268. return NOERR;
  269. }
  270. #endif
  271. static int config_set_str(struct conf_entry *ce, char *arg)
  272. {
  273. // TODO use realpath(3), when necessary
  274. ce->value.s = arg;
  275. return NOERR;
  276. }
  277. static int config_set_port(struct conf_entry *ce, char *arg)
  278. {
  279. long portval = strtol(arg, NULL, 10);
  280. /* A zero return will be invalid no matter if strtol succeeded or not */
  281. if (portval > UINT16_MAX || portval <= 0) {
  282. fprintf(stderr, "Invalid port value '%s'\n", arg);
  283. return ERR_OPT_INVVAL;
  284. }
  285. ce->value.hu = (uint16_t)portval;
  286. return NOERR;
  287. }
  288. static int config_set_bool(struct conf_entry *ce, char *arg)
  289. {
  290. ce->value.b = true;
  291. return NOERR;
  292. }
  293. static int show_help(struct conf_entry *ce)
  294. {
  295. printf("Todo...\n");
  296. return ERR_OPT_EXIT;
  297. }
  298. static int config_parse_file()
  299. {
  300. // TODO implement this
  301. #if 0
  302. assert(conf.config_file_path);
  303. FILE *f = fopen(conf.config_file_path, "rb");
  304. char errbuf[200];
  305. toml_table_t *tml = toml_parse_file(f, errbuf, sizeof(errbuf));
  306. fclose(f);
  307. if (!tml) {
  308. fprintf(stderr, "Failed to parse config toml: %s\n", errbuf);
  309. return ERR_TOML_PARSE_ERROR;
  310. }
  311. toml_datum_t port = toml_int_in(tml, "port");
  312. if (port.ok)
  313. conf.port = (uint16_t)port.u.i;
  314. else
  315. fprintf(stderr, "Failed to parse port from config toml: %s\n", errbuf);
  316. toml_datum_t dldir = toml_string_in(tml, "default_download_dir");
  317. if (dldir.ok) {
  318. conf.default_download_dir = dldir.u.s;
  319. printf("%s\n", dldir.u.s);
  320. conf_dyn.default_download_dir = dldir.u.s;
  321. conf_dyn.default_download_dir = true;
  322. /* TODO is this always malloced?? if yes, remve dyn check */
  323. } else {
  324. fprintf(stderr, "Failed to parse download dir from config toml: %s\n", errbuf);
  325. }
  326. toml_free(tml);
  327. #endif
  328. return NOERR;
  329. }
  330. int config_free()
  331. {
  332. for (int i = 0; i < options_count; i++) {
  333. if (options[i].value_is_dyn) {
  334. free(options[i].value.s);
  335. options[i].value.s = NULL;
  336. options[i].value_is_dyn = false;
  337. options[i].value_is_set = false;
  338. }
  339. }
  340. return NOERR;
  341. }
  342. #if 0
  343. static int config_action_write_config(struct conf_entry *ce)
  344. {
  345. /* This is the success return here */
  346. int err = ERR_OPT_EXIT, plen;
  347. const char *config_dir;
  348. FILE *f = NULL;
  349. config_dir = config_get("config-dir");
  350. plen = snprintf(NULL, 0, "%s/%s", config_dir, CONFIG_FILE_NAME);
  351. char path[plen + 1];
  352. snprintf(path, plen + 1, "%s/%s", config_dir, CONFIG_FILE_NAME);
  353. for (int i = 0; i < 2; i++) {
  354. f = fopen(path, "wb");
  355. if (!f) {
  356. int errn = errno;
  357. if (errn == ENOENT && i == 0) {
  358. /* Try to create parent directory */
  359. if (mkdir(config_dir, 0755) == -1) {
  360. err = errno;
  361. fprintf(stderr, "Config mkdir failed: %s\n", strerror(err));
  362. goto end;
  363. }
  364. } else {
  365. err = errn;
  366. fprintf(stderr, "Config fopen failed: %s\n", strerror(err));
  367. goto end;
  368. }
  369. } else {
  370. break;
  371. }
  372. }
  373. for (int i = 0; i < options_count; i++) {
  374. if (!options[i].in_file)
  375. continue;
  376. fprintf(f, "%s = ", options[i].l_name);
  377. switch (options[i].type) {
  378. case OTYPE_S:
  379. // TODO toml escaping
  380. fprintf(f, "\"%s\"\n", options[i].value.s);
  381. break;
  382. case OTYPE_HU:
  383. fprintf(f, "%hu\n", options[i].value.hu);
  384. break;
  385. case OTYPE_B:
  386. fprintf(f, "%s\n", options[i].value.b ? "true" : "false");
  387. break;
  388. default:
  389. break;
  390. }
  391. }
  392. config_dump();
  393. end:
  394. if (f)
  395. fclose(f);
  396. return err;
  397. }
  398. #endif
  399. enum error config_get(const char *key, void **out)
  400. {
  401. enum error err = ERR_OPT_NOTFOUND;
  402. for (int i = 0; i < options_count; i++) {
  403. struct conf_entry *cc = &options[i];
  404. if (strcmp(cc->l_name, key) == 0) {
  405. if (cc->value_is_set) {
  406. if (out)
  407. *out = &cc->value.s;
  408. err = NOERR;
  409. } else {
  410. err = ERR_OPT_UNSET;
  411. }
  412. break;
  413. }
  414. }
  415. return err;
  416. }
  417. const char *config_get_nonopt(int index)
  418. {
  419. if (index >= config_get_nonopt_count())
  420. return NULL;
  421. return opt_argv[optind + index];
  422. }
  423. int config_get_nonopt_count()
  424. {
  425. return opt_argc - optind;
  426. }
  427. void config_dump()
  428. {
  429. for (int i = 0; i < options_count; i++) {
  430. if (options[i].type == OTYPE_ACTION)
  431. continue;
  432. printf("%s: ", options[i].l_name);
  433. if (!options[i].value_is_set) {
  434. printf("[UNSET (>.<)]\n");
  435. continue;
  436. }
  437. switch (options[i].type) {
  438. case OTYPE_S:
  439. printf("%s\n", options[i].value.s);
  440. break;
  441. case OTYPE_HU:
  442. printf("%hu\n", options[i].value.hu);
  443. break;
  444. case OTYPE_B:
  445. printf("%s\n", options[i].value.b ? "True" : "False");
  446. break;
  447. default:
  448. printf("Error :(\n");
  449. break;
  450. }
  451. }
  452. }