Compare commits
7 Commits
bc99462436
...
9ddafb8240
Author | SHA1 | Date | |
---|---|---|---|
9ddafb8240 | |||
1fb7a4b2fa | |||
d6fc97a87d | |||
f1ed4b0944 | |||
b7619f4404 | |||
9cdf2df3b3 | |||
43b2249d91 |
@ -35,8 +35,6 @@ make
|
||||
- If file is not found at a scan
|
||||
- Buffer up mylistadd api cmds when waiting for ratelimit
|
||||
- Handle C-c gracefully at any time
|
||||
- Rework cmd line args
|
||||
- Should be multiple 'menus' like `caniadd add [paths...]`, `caniadd uptime`, `caniadd watched`...
|
||||
- After some time passes between now and cache moddate and it's not watched, then query and update it
|
||||
- Update cache entry is modify is used
|
||||
- Pretty hashing with color and progress bars and the other fancy stuff
|
||||
|
10
src/api.c
10
src/api.c
@ -719,10 +719,16 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len,
|
||||
if (err == ERR_API_OOS || err == ERR_API_SRV_BUSY ||
|
||||
err == ERR_API_TIMEOUT) {
|
||||
struct timespec ts;
|
||||
time_t wait_ms = API_TRYAGAIN_TIME;
|
||||
|
||||
MS_TO_TIMESPEC_L(ts, API_TRYAGAIN_TIME);
|
||||
if (err == ERR_API_OOS) {
|
||||
/* In this case, we should wait 30 minutes as per the wiki */
|
||||
wait_ms = 30 * 60 * 1000;
|
||||
}
|
||||
|
||||
MS_TO_TIMESPEC_L(ts, wait_ms);
|
||||
retry_count++;
|
||||
uio_debug("Retry after %ld ms (%d/%d)", API_TRYAGAIN_TIME,
|
||||
uio_debug("Retry after %ld ms (%d/%d)", wait_ms,
|
||||
retry_count, API_MAX_TRYAGAIN);
|
||||
if (nanosleep(&ts, NULL) == -1) {
|
||||
if (errno == EINTR && should_exit) {
|
||||
|
10
src/cmd.c
10
src/cmd.c
@ -14,6 +14,7 @@ struct cmd_entry {
|
||||
bool need_cache : 1; /* Does this cmd needs the file cache? */
|
||||
const char *arg_name; /* If this argument is present, execute this cmd */
|
||||
|
||||
enum error (*argcheck)(void); /* Function to check argument correctness before calling fn */
|
||||
enum error (*fn)(void *data); /* The function for the command */
|
||||
};
|
||||
|
||||
@ -22,8 +23,8 @@ static const struct cmd_entry ents[] = {
|
||||
{ .arg_name = "server-version", .fn = cmd_server_version, .need_api = true },
|
||||
{ .arg_name = "uptime", .fn = cmd_server_uptime, .need_auth = true },
|
||||
{ .arg_name = "ed2k", .fn = cmd_ed2k, },
|
||||
{ .arg_name = "add", .fn = cmd_add, .need_auth = true, .need_cache = true, },
|
||||
{ .arg_name = "modify", .fn = cmd_modify, .need_auth = true, .need_cache = false, },
|
||||
{ .arg_name = "add", .fn = cmd_add, .argcheck = cmd_add_argcheck, .need_auth = true, .need_cache = true, },
|
||||
{ .arg_name = "modify", .fn = cmd_modify, .argcheck = cmd_modify_argcheck, .need_auth = true, .need_cache = true, },
|
||||
};
|
||||
static const int32_t ents_len = sizeof(ents)/sizeof(*ents);
|
||||
|
||||
@ -31,6 +32,11 @@ static enum error cmd_run_one(const struct cmd_entry *ent)
|
||||
{
|
||||
enum error err = NOERR;
|
||||
|
||||
if (ent->argcheck) {
|
||||
err = ent->argcheck();
|
||||
if (err != NOERR)
|
||||
goto end;
|
||||
}
|
||||
if (ent->need_cache) {
|
||||
err = cache_init();
|
||||
if (err != NOERR)
|
||||
|
@ -13,6 +13,7 @@ enum error cmd_main();
|
||||
* Add files to the AniDB list
|
||||
*/
|
||||
enum error cmd_add(void *);
|
||||
enum error cmd_add_argcheck();
|
||||
|
||||
/*
|
||||
* Take in a file/folder and print out
|
||||
@ -39,5 +40,6 @@ enum error cmd_prog_version(void *);
|
||||
* Modifies a mylist entry
|
||||
*/
|
||||
enum error cmd_modify(void *data);
|
||||
enum error cmd_modify_argcheck();
|
||||
|
||||
#endif /* _CMD_H */
|
||||
|
@ -175,3 +175,11 @@ enum error cmd_add(void *data)
|
||||
return err;
|
||||
}
|
||||
|
||||
enum error cmd_add_argcheck()
|
||||
{
|
||||
if (config_get_nonopt_count() == 0) {
|
||||
uio_error("No files specified");
|
||||
return ERR_CMD_ARG;
|
||||
}
|
||||
return NOERR;
|
||||
}
|
||||
|
@ -49,13 +49,12 @@ enum error cmd_ed2k(void *data)
|
||||
return ERR_CMD_ARG;
|
||||
}
|
||||
|
||||
if (config_get("link", (void**)&link) == NOERR)
|
||||
if (config_get_subopt("ed2k", "link", (void**)&link) == NOERR)
|
||||
opts.link = *link;
|
||||
|
||||
for (int i = 0; i < fcount; i++) {
|
||||
err = ed2k_util_iterpath(config_get_nonopt(i), &ed2k_opts);
|
||||
if (err != NOERR)
|
||||
break;
|
||||
ed2k_util_iterpath(config_get_nonopt(i), &ed2k_opts);
|
||||
/* Above may fail if the path doesn't exists or smth, but still continue */
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
158
src/cmd_modify.c
158
src/cmd_modify.c
@ -1,7 +1,9 @@
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "cmd.h"
|
||||
#include "error.h"
|
||||
@ -59,6 +61,107 @@ uint64_t cmd_modify_arg_parse(const char *str, uint64_t *out_wdate)
|
||||
return ce.lid;
|
||||
}
|
||||
|
||||
static enum error cmd_modify_api_send(uint64_t lid, struct api_mylistadd_opts *opts)
|
||||
{
|
||||
struct api_result res;
|
||||
enum error err = api_cmd_mylistmod(lid, opts, &res);
|
||||
if (err != NOERR)
|
||||
return err;
|
||||
|
||||
if (res.code == APICODE_NO_SUCH_MYLIST_ENTRY) {
|
||||
uio_error("No mylist entry with id: '%lu'", lid);
|
||||
return ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!cache_is_init()) {
|
||||
if ((err = cache_init()) != NOERR)
|
||||
return err;
|
||||
did_cache_init = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = cache_update(lid, opts);
|
||||
if (err == NOERR) {
|
||||
uio_user("Successfully updated entry with lid:%lu", lid);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static enum error cmd_modify_file_cb(const char *path, const struct stat *fstat, void *data)
|
||||
{
|
||||
struct cache_entry ce;
|
||||
enum error err;
|
||||
struct api_mylistadd_opts *opts = data;
|
||||
char *basename = util_basename(path);
|
||||
|
||||
uio_debug("Modifying mylist by file: %s", basename);
|
||||
|
||||
/* Get the mylist id from the cache */
|
||||
err = cache_get(basename, fstat->st_size, CACHE_S_LID, &ce);
|
||||
if (err != NOERR) {
|
||||
uio_error("Failed to get file from cache: %lu:%s. Maybe we should add it now?",
|
||||
fstat->st_size, basename);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* We have the mylistid, so actually send the change to the api */
|
||||
return cmd_modify_api_send(ce.lid, opts);
|
||||
}
|
||||
|
||||
static enum error cmd_modify_by_file(const char *fpath, struct api_mylistadd_opts *opts)
|
||||
{
|
||||
return util_iterpath(fpath, cmd_modify_file_cb, opts);
|
||||
}
|
||||
|
||||
static enum error cmd_modify_by_mylistid(const char *arg, struct api_mylistadd_opts *opts)
|
||||
{
|
||||
struct api_mylistadd_opts local_opts = *opts;
|
||||
uint64_t wdate, lid;
|
||||
|
||||
lid = cmd_modify_arg_parse(arg, &wdate);
|
||||
|
||||
if (lid == 0) {
|
||||
uio_error("Argument '%s' is not valid. Skipping", arg);
|
||||
return ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
/* If info is present in the argument line set it for the update */
|
||||
if (wdate != 0) {
|
||||
local_opts.watched = true;
|
||||
local_opts.watched_set = true;
|
||||
|
||||
local_opts.wdate = wdate;
|
||||
local_opts.wdate_set = true;
|
||||
}
|
||||
|
||||
return cmd_modify_api_send(lid, &local_opts);
|
||||
}
|
||||
|
||||
static enum error cmd_modify_process_arg(const char *arg, struct api_mylistadd_opts *opts)
|
||||
{
|
||||
struct stat fs;
|
||||
|
||||
/* Check if the given path is a file/directory */
|
||||
if (stat(arg, &fs) == 0) {
|
||||
/* File exists handle with the file handler */
|
||||
return cmd_modify_by_file(arg, opts);
|
||||
}
|
||||
|
||||
/* If stat failed */
|
||||
int eno = errno;
|
||||
if (eno != ENOENT) {
|
||||
/* Failed for some other reason apart from file not existing */
|
||||
uio_warning("Stat for file '%s' failed with: %s", arg, strerror(eno));
|
||||
return ERR_UNKNOWN;
|
||||
}
|
||||
/* File simply not found, try the with the mylistid syntax */
|
||||
|
||||
return cmd_modify_by_mylistid(arg, opts);
|
||||
|
||||
}
|
||||
|
||||
enum error cmd_modify(void *data)
|
||||
{
|
||||
struct api_mylistadd_opts mopt = {0};
|
||||
@ -68,16 +171,12 @@ enum error cmd_modify(void *data)
|
||||
int fcount;
|
||||
|
||||
fcount = config_get_nonopt_count();
|
||||
if (fcount == 0) {
|
||||
uio_error("No mylist ids specified");
|
||||
return ERR_CMD_ARG;
|
||||
}
|
||||
|
||||
if (config_get("watched", (void**)&watched) == NOERR && *watched) {
|
||||
if (config_get_subopt("modify", "watched", (void**)&watched) == NOERR && *watched) {
|
||||
mopt.watched = *watched;
|
||||
mopt.watched_set = true;
|
||||
|
||||
if (config_get("wdate", (void**)&wdate_str) == NOERR) {
|
||||
if (config_get_subopt("modify", "wdate", (void**)&wdate_str) == NOERR) {
|
||||
uint64_t wdate = util_iso2unix(*wdate_str);
|
||||
|
||||
if (wdate == 0) {
|
||||
@ -90,49 +189,26 @@ enum error cmd_modify(void *data)
|
||||
}
|
||||
|
||||
for (int i = 0; i < fcount; i++) {
|
||||
struct api_result res;
|
||||
struct api_mylistadd_opts l_opts = mopt;
|
||||
const char *arg = config_get_nonopt(i);
|
||||
uint64_t wdate;
|
||||
uint64_t lid = cmd_modify_arg_parse(arg, &wdate);
|
||||
|
||||
if (lid == 0) {
|
||||
uio_error("Argument '%s' is not valid. Skipping", arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wdate != 0) {
|
||||
l_opts.watched = true;
|
||||
l_opts.watched_set = true;
|
||||
|
||||
l_opts.wdate = wdate;
|
||||
l_opts.wdate_set = true;
|
||||
}
|
||||
|
||||
err = api_cmd_mylistmod(lid, &l_opts, &res);
|
||||
if (err != NOERR)
|
||||
break;
|
||||
|
||||
if (res.code == APICODE_NO_SUCH_MYLIST_ENTRY) {
|
||||
uio_error("No mylist entry with id: '%lu'", lid);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cache_is_init()) {
|
||||
if ((err = cache_init()) != NOERR)
|
||||
return err;
|
||||
did_cache_init = true;
|
||||
}
|
||||
|
||||
err = cache_update(lid, &l_opts);
|
||||
if (err != NOERR)
|
||||
break;
|
||||
cmd_modify_process_arg(arg, &mopt);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (did_cache_init) {
|
||||
did_cache_init = false;
|
||||
cache_free();
|
||||
}
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
enum error cmd_modify_argcheck()
|
||||
{
|
||||
if (config_get_nonopt_count() == 0) {
|
||||
uio_error("No arguments given for modify");
|
||||
return ERR_CMD_ARG;
|
||||
}
|
||||
return NOERR;
|
||||
}
|
||||
|
348
src/config.c
348
src/config.c
@ -19,8 +19,10 @@
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
#include "util.h"
|
||||
#include "uio.h"
|
||||
|
||||
static int show_help(struct conf_entry *ce);
|
||||
static int show_subcomm_help(struct conf_entry *ce);
|
||||
static int config_parse_file();
|
||||
static enum error config_required_check();
|
||||
|
||||
@ -35,35 +37,44 @@ static int config_set_bool(struct conf_entry *ce, char *arg);
|
||||
/* Everything not explicitly defined, is 0 */
|
||||
/* If an option only has a long name, the short name also has to be
|
||||
* defined. For example, a number larger than UCHAR_MAX */
|
||||
|
||||
#define SUBCOMM_HELP { \
|
||||
.l_name = "help", .s_name = 'h', .has_arg = no_argument, \
|
||||
.action_func = show_subcomm_help, .in_args = true, \
|
||||
.type = OTYPE_ACTION, .handle_order = 0, \
|
||||
.h_desc = "Display the help for the subcommand and exit", }
|
||||
|
||||
|
||||
static struct conf_entry subcomm_def_help_opt = SUBCOMM_HELP;
|
||||
|
||||
static struct conf_entry ed2k_subopts[] = {
|
||||
SUBCOMM_HELP,
|
||||
|
||||
{ .l_name = "link", .s_name = 'l', .has_arg = no_argument,
|
||||
.set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 0, .value_is_set = true,
|
||||
.h_desc = "Print an ed2k link for the files", },
|
||||
};
|
||||
|
||||
static struct conf_entry modify_add_subopts[] = {
|
||||
SUBCOMM_HELP,
|
||||
|
||||
{ .l_name = "watched", .s_name = 'w', .has_arg = no_argument,
|
||||
.set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 0, .value_is_set = true,
|
||||
.h_desc = "Mark the episode as watched when adding/modifying files", },
|
||||
|
||||
{ .l_name = "wdate", .s_name = UCHAR_MAX + 4, .has_arg = required_argument,
|
||||
.set_func = config_set_str, .in_args = true,
|
||||
.type = OTYPE_S, .handle_order = 0,
|
||||
.h_desc = "Set the watched date when adding/modifying files. Either in unix time or in YY-MM-DDTHH:MM:SS", },
|
||||
};
|
||||
|
||||
static struct conf_entry options[] = {
|
||||
{ .l_name = "help", .s_name = 'h', .has_arg = no_argument,
|
||||
.action_func = show_help, .in_args = true,
|
||||
.type = OTYPE_ACTION, .handle_order = 0,
|
||||
.h_desc = "Display the help and exit", },
|
||||
/*
|
||||
{ .l_name = "config-dir", .s_name = 'b', .has_arg = required_argument,
|
||||
.default_func = config_def_config_dir, .set_func = config_set_str,
|
||||
.in_args = true, .type = OTYPE_S, .handle_order = 0 },
|
||||
|
||||
{ .l_name = "default-download-dir", .s_name = 'd', .has_arg = required_argument,
|
||||
.default_func = config_def_default_download_dir, .set_func = config_set_str,
|
||||
.in_file = true, .in_args = true, .type = OTYPE_S, .handle_order = 1 },
|
||||
|
||||
{ .l_name = "port", .s_name = 'p', .has_arg = required_argument,
|
||||
.set_func = config_set_port, .value.hu = 21729, .value_is_set = true,
|
||||
.in_file = true, .in_args = true, .type = OTYPE_HU, .handle_order = 1 },
|
||||
|
||||
{ .l_name = "foreground", .s_name = 'f', .has_arg = no_argument,
|
||||
.set_func = config_set_bool, .value.b = false, .value_is_set = true,
|
||||
.in_args = true, .type = OTYPE_B, .handle_order = 1 },
|
||||
|
||||
{ .l_name = "write-config", .s_name = UCHAR_MAX + 1, .has_arg = no_argument,
|
||||
.action_func = config_action_write_config, .value_is_set = true,
|
||||
.in_args = true, .type = OTYPE_ACTION, .handle_order = 2 },
|
||||
|
||||
{ .l_name = "peer-id", .s_name = UCHAR_MAX + 2, .has_arg = required_argument,
|
||||
.default_func = config_def_peer_id, .type = OTYPE_S, .handle_order = 1 },
|
||||
*/
|
||||
|
||||
{ .l_name = "username", .s_name = 'u', .has_arg = required_argument,
|
||||
.set_func = config_set_str, .in_args = true, .in_file = true,
|
||||
@ -102,16 +113,6 @@ static struct conf_entry options[] = {
|
||||
.type = OTYPE_B, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "not implemented", },
|
||||
|
||||
{ .l_name = "watched", .s_name = 'w', .has_arg = no_argument,
|
||||
.set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "Mark the episode as watched when adding files", },
|
||||
|
||||
{ .l_name = "link", .s_name = 'l', .has_arg = no_argument,
|
||||
.set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "Print an ed2k link when running the ed2k command", },
|
||||
|
||||
{ .l_name = "cachedb", .s_name = 'd', .has_arg = required_argument,
|
||||
.set_func = config_set_str, .in_args = true, .in_file = true,
|
||||
.type = OTYPE_S, .handle_order = 1, /*.default_func = config_def_cachedb*/
|
||||
@ -122,45 +123,53 @@ static struct conf_entry options[] = {
|
||||
.type = OTYPE_B, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "Enable debug output", },
|
||||
|
||||
{ .l_name = "wdate", .s_name = UCHAR_MAX + 4, .has_arg = required_argument,
|
||||
.set_func = config_set_str, .in_args = true,
|
||||
.type = OTYPE_S, .handle_order = 1,
|
||||
.h_desc = "Set the watched date when adding files", },
|
||||
|
||||
/*### cmd ###*/
|
||||
/*### cmds ###*/
|
||||
|
||||
{ .l_name = "server-version", .s_name = UCHAR_MAX + 2,
|
||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "CMD: Request the server version", },
|
||||
.type = OTYPE_SUBCOMMAND, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "Request the server version",
|
||||
},
|
||||
|
||||
{ .l_name = "version", .s_name = 'v',
|
||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "CMD: Print the caniadd version", },
|
||||
.type = OTYPE_SUBCOMMAND, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "Print the caniadd version", },
|
||||
|
||||
{ .l_name = "uptime", .s_name = UCHAR_MAX + 3,
|
||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "CMD: Request the uptime of the api servers", },
|
||||
.type = OTYPE_SUBCOMMAND, .handle_order = 1, .value_is_set = true,
|
||||
.h_desc = "Request the uptime of the api servers", },
|
||||
|
||||
{ .l_name = "ed2k", .s_name = 'e',
|
||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 1,
|
||||
.h_desc = "CMD: Run an ed2k hash on the file arguments", },
|
||||
.type = OTYPE_SUBCOMMAND, .handle_order = 1,
|
||||
.h_desc = "Run an ed2k hash on the file arguments",
|
||||
.subopts = ed2k_subopts,
|
||||
.subopts_count = sizeof(ed2k_subopts) / sizeof(ed2k_subopts[0]),
|
||||
},
|
||||
|
||||
{ .l_name = "add", .s_name = 'a',
|
||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 1,
|
||||
.h_desc = "CMD: Add files to your anidb list", },
|
||||
.type = OTYPE_SUBCOMMAND, .handle_order = 1,
|
||||
.h_desc = "Add files to your anidb list",
|
||||
.subopts = modify_add_subopts,
|
||||
.subopts_count = sizeof(modify_add_subopts) / sizeof(modify_add_subopts[0]),
|
||||
},
|
||||
|
||||
/* Arguments are either mylist id's, or file sizes and names
|
||||
* in the format '[watch_date/]<size>/<filename>'. The filename can't contain
|
||||
* '/' characters. */
|
||||
{ .l_name = "modify", .s_name = 'W',
|
||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
|
||||
.type = OTYPE_B, .handle_order = 1,
|
||||
.h_desc = "CMD: Modify files in your anidb list", },
|
||||
.type = OTYPE_SUBCOMMAND, .handle_order = 1,
|
||||
.h_desc = "Modify files in your anidb list",
|
||||
.h_desc_more =
|
||||
"The arguments are either mylist id's, a file/folder path, or file sizes and names\n"
|
||||
"in the format '[watch_date/]<size>/<filename>'. The filename can't contain '/' characters\n",
|
||||
.subopts = modify_add_subopts,
|
||||
.subopts_count = sizeof(modify_add_subopts) / sizeof(modify_add_subopts[0]),
|
||||
},
|
||||
|
||||
/*{ .l_name = "stats", .s_name = UCHAR_MAX + 5,
|
||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true,
|
||||
@ -169,33 +178,42 @@ static struct conf_entry options[] = {
|
||||
};
|
||||
|
||||
static const size_t options_count = sizeof(options) / sizeof(options[0]);
|
||||
static const char **opt_argv = NULL;
|
||||
/* Used in show_subcomm_help to output info about the subcommand */
|
||||
static struct conf_entry *current_subcommand = NULL;
|
||||
static char **opt_argv = NULL;
|
||||
static int opt_argc = 0;
|
||||
|
||||
static void config_build_getopt_args(char out_sopt[options_count * 2 + 1],
|
||||
struct option out_lopt[options_count + 1])
|
||||
static void config_build_getopt_args(int opts_count, const struct conf_entry opts[opts_count],
|
||||
char out_sopt[opts_count * 2 + 1], struct option out_lopt[opts_count + 1],
|
||||
bool stop_at_1st_nonopt)
|
||||
{
|
||||
int i_sopt = 0, i_lopt = 0;
|
||||
if (stop_at_1st_nonopt) {
|
||||
/* Tell getopts to stop at the 1st non-option argument */
|
||||
out_sopt[i_sopt++] = '+';
|
||||
}
|
||||
|
||||
for (int i = 0; i < options_count; i++) {
|
||||
for (int i = 0; i < opts_count; i++) {
|
||||
if (opts[i].type == OTYPE_SUBCOMMAND)
|
||||
continue;
|
||||
|
||||
/* Short options */
|
||||
if (options[i].s_name && options[i].s_name <= UCHAR_MAX) {
|
||||
out_sopt[i_sopt++] = options[i].s_name;
|
||||
if (options[i].has_arg == required_argument)
|
||||
if (opts[i].s_name && opts[i].s_name <= UCHAR_MAX) {
|
||||
out_sopt[i_sopt++] = opts[i].s_name;
|
||||
if (opts[i].has_arg == required_argument)
|
||||
out_sopt[i_sopt++] = ':';
|
||||
assert(options[i].has_arg == required_argument ||
|
||||
options[i].has_arg == no_argument);
|
||||
assert(opts[i].has_arg == required_argument ||
|
||||
opts[i].has_arg == no_argument);
|
||||
}
|
||||
|
||||
/* Long options */
|
||||
if (options[i].l_name) {
|
||||
assert(options[i].s_name);
|
||||
/* Long opts */
|
||||
if (opts[i].l_name) {
|
||||
assert(opts[i].s_name);
|
||||
|
||||
out_lopt[i_lopt].name = options[i].l_name;
|
||||
out_lopt[i_lopt].has_arg = options[i].has_arg;
|
||||
out_lopt[i_lopt].name = opts[i].l_name;
|
||||
out_lopt[i_lopt].has_arg = opts[i].has_arg;
|
||||
out_lopt[i_lopt].flag = NULL;
|
||||
out_lopt[i_lopt].val = options[i].s_name;
|
||||
out_lopt[i_lopt].val = opts[i].s_name;
|
||||
|
||||
i_lopt++;
|
||||
}
|
||||
@ -205,33 +223,37 @@ static void config_build_getopt_args(char out_sopt[options_count * 2 + 1],
|
||||
memset(&out_lopt[i_lopt], 0, sizeof(struct option));
|
||||
}
|
||||
|
||||
static int config_read_args(int argc, char **argv, char sopt[options_count * 2 + 1],
|
||||
struct option lopt[options_count + 1], int level)
|
||||
static int config_read_args(int argc, char **argv,
|
||||
int opts_count, struct conf_entry opts[opts_count],
|
||||
char sopt[opts_count * 2 + 1], struct option lopt[opts_count + 1], int level)
|
||||
{
|
||||
int optc, err = NOERR;
|
||||
optind = 1;
|
||||
/* Reinitialize getopt with 0 here */
|
||||
optind = 0;
|
||||
|
||||
//uio_debug("Before %d %s", argc, argv[0]);
|
||||
while ((optc = getopt_long(argc, argv, sopt,
|
||||
lopt, NULL)) >= 0) {
|
||||
|
||||
bool handled = false;
|
||||
//uio_debug("Optc: %c", optc);
|
||||
|
||||
for (int i = 0; i < options_count; i++) {
|
||||
if (options[i].handle_order != level) {
|
||||
for (int i = 0; i < opts_count; i++) {
|
||||
if (opts[i].handle_order != level) {
|
||||
/* Lie a lil :x */
|
||||
handled = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (optc == options[i].s_name) {
|
||||
if (options[i].type != OTYPE_ACTION)
|
||||
err = options[i].set_func(&options[i], optarg);
|
||||
if (optc == opts[i].s_name) {
|
||||
if (opts[i].type != OTYPE_ACTION)
|
||||
err = opts[i].set_func(&opts[i], optarg);
|
||||
else
|
||||
err = options[i].action_func(&options[i]);
|
||||
err = opts[i].action_func(&opts[i]);
|
||||
|
||||
if (err != NOERR)
|
||||
goto end;
|
||||
options[i].value_is_set = true;
|
||||
opts[i].value_is_set = true;
|
||||
|
||||
handled = true;
|
||||
break;
|
||||
@ -269,16 +291,72 @@ static enum error config_required_check()
|
||||
return err;
|
||||
}
|
||||
|
||||
static enum error config_parse_subcomm_subopts(struct conf_entry *subcomm)
|
||||
{
|
||||
char sopt[64];
|
||||
struct option lopt[32];
|
||||
enum error err;
|
||||
|
||||
/* Set the global current subcommand pointer */
|
||||
current_subcommand = subcomm;
|
||||
config_build_getopt_args(subcomm->subopts_count, subcomm->subopts, sopt, lopt, false);
|
||||
|
||||
//uio_debug("Parsing subconn %s", subcomm->l_name);
|
||||
//uio_debug("sopt: %s", sopt);
|
||||
|
||||
/* Update args for next parsing and nonopts parsing for later */
|
||||
opt_argv = &opt_argv[optind];
|
||||
opt_argc = config_get_nonopt_count();
|
||||
/* argv[0] (which is the subcommand string) will be treated as the filename here, and skipped */
|
||||
err = config_read_args(opt_argc, opt_argv,
|
||||
subcomm->subopts_count, subcomm->subopts, sopt, lopt, 0);
|
||||
if (err == NOERR) {
|
||||
/* Mark subcommand as set */
|
||||
subcomm->value_is_set = true;
|
||||
subcomm->value.b = true;
|
||||
}
|
||||
|
||||
current_subcommand = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
static enum error config_parse_subcommands()
|
||||
{
|
||||
const char *subcomm_str;
|
||||
enum error err = ERR_OPT_NOTFOUND;
|
||||
|
||||
if (config_get_nonopt_count() <= 0) {
|
||||
return ERR_OPT_NO_SUBCOMMAND;
|
||||
}
|
||||
|
||||
subcomm_str = config_get_nonopt(0);
|
||||
for (int i = 0; i < options_count; i++) {
|
||||
if (options[i].type != OTYPE_SUBCOMMAND)
|
||||
continue;
|
||||
if (strcmp(options[i].l_name, subcomm_str) != 0)
|
||||
continue;
|
||||
if (options[i].subopts == NULL) {
|
||||
/* If no suboptions is defined, define the default
|
||||
* help one here */
|
||||
options[i].subopts = &subcomm_def_help_opt;
|
||||
options[i].subopts_count = 1;
|
||||
}
|
||||
/* Found, parse subopts */
|
||||
err = config_parse_subcomm_subopts(&options[i]);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
enum error config_parse(int argc, char **argv)
|
||||
{
|
||||
enum error err = NOERR;
|
||||
char sopt[options_count * 2 + 1];
|
||||
struct option lopt[options_count + 1];
|
||||
opt_argv = (const char**)argv;
|
||||
opt_argv = argv;
|
||||
opt_argc = argc;
|
||||
config_build_getopt_args(sopt, lopt);
|
||||
config_build_getopt_args(options_count, options, sopt, lopt, true);
|
||||
|
||||
err = config_read_args(argc, argv, sopt, lopt, 0);
|
||||
err = config_read_args(argc, argv, options_count, options, sopt, lopt, 0);
|
||||
if (err != NOERR)
|
||||
goto end;
|
||||
|
||||
@ -286,7 +364,7 @@ enum error config_parse(int argc, char **argv)
|
||||
if (err != NOERR)
|
||||
goto end;
|
||||
|
||||
err = config_read_args(argc, argv, sopt, lopt, 1);
|
||||
err = config_read_args(argc, argv, options_count, options, sopt, lopt, 1);
|
||||
if (err != NOERR)
|
||||
goto end;
|
||||
|
||||
@ -301,11 +379,24 @@ enum error config_parse(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
err = config_read_args(argc, argv, sopt, lopt, 2);
|
||||
err = config_read_args(argc, argv, options_count, options, sopt, lopt, 2);
|
||||
if (err != NOERR)
|
||||
goto end;
|
||||
|
||||
err = config_required_check();
|
||||
if (err != NOERR)
|
||||
goto end;
|
||||
|
||||
/* Now that all of the global arguments are parsed, do the subcommands */
|
||||
err = config_parse_subcommands();
|
||||
if (err != NOERR) {
|
||||
if (err == ERR_OPT_NO_SUBCOMMAND)
|
||||
printf("No subcommand given!\n\n");
|
||||
else if (err == ERR_OPT_NOTFOUND)
|
||||
printf("The given subcommand doesn't exist\n\n");
|
||||
if (err != ERR_OPT_EXIT) /* If err is this, then help() was already called */
|
||||
show_help(NULL);
|
||||
}
|
||||
|
||||
end:
|
||||
if (err != NOERR)
|
||||
@ -384,15 +475,65 @@ static int config_set_bool(struct conf_entry *ce, char *arg)
|
||||
return NOERR;
|
||||
}
|
||||
|
||||
static int show_subcomm_help(struct conf_entry *ce)
|
||||
{
|
||||
assert(current_subcommand);
|
||||
const struct conf_entry *subopts = current_subcommand->subopts;
|
||||
|
||||
printf(
|
||||
"Usage: caniadd [OPTIONS] %s [SUBCOMMAND_OPTIONS]\n"
|
||||
"%s\n"
|
||||
"%s"
|
||||
"\n"
|
||||
"SUBCOMMAND_OPTIONS:\n",
|
||||
current_subcommand->l_name,
|
||||
current_subcommand->h_desc,
|
||||
current_subcommand->h_desc_more ? current_subcommand->h_desc_more : ""
|
||||
);
|
||||
|
||||
for (size_t i = 0; i < current_subcommand->subopts_count; i++) {
|
||||
int printed = 0, pad;
|
||||
|
||||
printed += printf(" ");
|
||||
|
||||
if (subopts[i].l_name)
|
||||
printed += printf("--%s", subopts[i].l_name);
|
||||
if (subopts[i].s_name < UCHAR_MAX)
|
||||
printed += printf(", -%c", subopts[i].s_name);
|
||||
if (subopts[i].has_arg != no_argument)
|
||||
printed += printf(" arg");
|
||||
|
||||
pad = 25 - printed;
|
||||
if (pad <= 0)
|
||||
pad = 1;
|
||||
|
||||
printf("%*s%s", pad, "", subopts[i].h_desc);
|
||||
if (subopts[i].value_is_set) {
|
||||
printf(" Val: ");
|
||||
if (subopts[i].type == OTYPE_S)
|
||||
printed += printf("%s", subopts[i].value.s);
|
||||
else if (subopts[i].type == OTYPE_HU)
|
||||
printed += printf("%hu", subopts[i].value.hu);
|
||||
else if (subopts[i].type == OTYPE_B)
|
||||
printed += printf("%s", subopts[i].value.b ? "true" : "false");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return ERR_OPT_EXIT;
|
||||
}
|
||||
|
||||
static int show_help(struct conf_entry *ce)
|
||||
{
|
||||
printf(
|
||||
"Usage: caniadd [OPTION]...\n"
|
||||
"Usage: caniadd [OPTIONS] SUBCOMMAND [SUBCOMMAND_OPTIONS]\n"
|
||||
"Caniadd will add files to an AniDB list, and possibly more.\n"
|
||||
"\n"
|
||||
"OPTIONS:\n"
|
||||
);
|
||||
for (size_t i = 0; i < options_count; i++) {
|
||||
if (options[i].type == OTYPE_SUBCOMMAND)
|
||||
continue;
|
||||
|
||||
int printed = 0, pad;
|
||||
|
||||
printed += printf(" ");
|
||||
@ -410,7 +551,7 @@ static int show_help(struct conf_entry *ce)
|
||||
|
||||
printf("%*s%s", pad, "", options[i].h_desc);
|
||||
if (options[i].value_is_set) {
|
||||
printf(" Def: ");
|
||||
printf(" Val: ");
|
||||
if (options[i].type == OTYPE_S)
|
||||
printed += printf("%s", options[i].value.s);
|
||||
else if (options[i].type == OTYPE_HU)
|
||||
@ -420,6 +561,19 @@ static int show_help(struct conf_entry *ce)
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("\nSUBCOMMANDS:\n");
|
||||
for (size_t i = 0; i < options_count; i++) {
|
||||
if (options[i].type != OTYPE_SUBCOMMAND)
|
||||
continue;
|
||||
|
||||
int printed = 0, pad;
|
||||
|
||||
printed += printf(" %s", options[i].l_name);
|
||||
|
||||
pad = 25 - printed;
|
||||
printf("%*s%s\n", pad, "", options[i].h_desc);
|
||||
}
|
||||
return ERR_OPT_EXIT;
|
||||
}
|
||||
|
||||
@ -537,17 +691,18 @@ end:
|
||||
}
|
||||
#endif
|
||||
|
||||
enum error config_get(const char *key, void **out)
|
||||
enum error config_get_inter(int cenf_count, const struct conf_entry cenf[cenf_count],
|
||||
const char *key, void **out)
|
||||
{
|
||||
enum error err = ERR_OPT_NOTFOUND;
|
||||
|
||||
for (int i = 0; i < options_count; i++) {
|
||||
struct conf_entry *cc = &options[i];
|
||||
for (int i = 0; i < cenf_count; i++) {
|
||||
const struct conf_entry *cc = &cenf[i];
|
||||
|
||||
if (strcmp(cc->l_name, key) == 0) {
|
||||
if (cc->value_is_set) {
|
||||
if (out)
|
||||
*out = &cc->value.s;
|
||||
*out = (void**)&cc->value.s;
|
||||
err = NOERR;
|
||||
} else {
|
||||
err = ERR_OPT_UNSET;
|
||||
@ -559,6 +714,23 @@ enum error config_get(const char *key, void **out)
|
||||
return err;
|
||||
}
|
||||
|
||||
enum error config_get(const char *key, void **out)
|
||||
{
|
||||
return config_get_inter(options_count, options, key, out);
|
||||
}
|
||||
|
||||
enum error config_get_subopt(const char *subcomm, const char *key, void **out)
|
||||
{
|
||||
for (int i = 0; i < options_count; i++) {
|
||||
struct conf_entry *cc = &options[i];
|
||||
|
||||
if (strcmp(cc->l_name, subcomm) == 0) {
|
||||
return config_get_inter(cc->subopts_count, cc->subopts, key, out);
|
||||
}
|
||||
}
|
||||
return ERR_OPT_NOTFOUND;
|
||||
}
|
||||
|
||||
const char *config_get_nonopt(int index)
|
||||
{
|
||||
if (index >= config_get_nonopt_count())
|
||||
|
34
src/config.h
34
src/config.h
@ -16,14 +16,41 @@ enum option_type {
|
||||
/* Does not store anything, does an action. Handled after every
|
||||
* other option are parsed, and defaults set */
|
||||
OTYPE_ACTION,
|
||||
OTYPE_SUBCOMMAND,
|
||||
|
||||
_OTYPE_COUNT
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* This should be used to make loopups O(1), but w/e for now */
|
||||
enum config_global_options {
|
||||
OPT_HELP = 0,
|
||||
OPT_USERNAME,
|
||||
OPT_PASSWORD,
|
||||
OPT_PORT,
|
||||
OPT_API_SERVER,
|
||||
OPT_API_KEY,
|
||||
OPT_SAVE_SESSION,
|
||||
OPT_DESTROY_SESSION,
|
||||
OPT_CACHEDB,
|
||||
OPT_DEBUG,
|
||||
};
|
||||
|
||||
enum config_subcommands {
|
||||
SUBCOMM_SERVER_VERSION = 0,
|
||||
SUBCOMM_VERSION,
|
||||
SUBCOMM_UPTIME,
|
||||
SUBCOMM_ED2k,
|
||||
SUBCOMM_ADD,
|
||||
SUBCOMM_MODIFY,
|
||||
};
|
||||
#endif
|
||||
|
||||
struct conf_entry {
|
||||
const char *l_name; /* The long name for the option, or for the config file */
|
||||
int s_name; /* Short option name */
|
||||
const char *h_desc; /* Description for the help screen */
|
||||
const char *h_desc_more; /* More description for the help screen */
|
||||
union { /* Value of the param */
|
||||
char *s;
|
||||
uint16_t hu;
|
||||
@ -56,6 +83,12 @@ struct conf_entry {
|
||||
* 4. Execute action arguments
|
||||
*/
|
||||
int handle_order : 4;
|
||||
/* If type is a subcommand, this is the arguments for that subcommand */
|
||||
/* '-h' option is available for all of the subcommands if this is not set */
|
||||
int subopts_count;
|
||||
struct conf_entry *subopts;
|
||||
/* The subcommand parent of this entry, if it is inside of a subcommand */
|
||||
//struct conf_entry *subcomm_parent;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -84,6 +117,7 @@ void config_dump();
|
||||
* It the options is not found, it returns ERR_OPT_NOTFOUND, and out is unchanged
|
||||
*/
|
||||
enum error config_get(const char *key, void **out);
|
||||
enum error config_get_subopt(const char *subcomm, const char *key, void **out);
|
||||
|
||||
/*
|
||||
* Return an cmd line that is not an option
|
||||
|
@ -2,28 +2,27 @@
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ftw.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "ed2k.h"
|
||||
#include "ed2k_util.h"
|
||||
#include "uio.h"
|
||||
#include "globals.h"
|
||||
|
||||
static struct ed2k_util_opts l_opts;
|
||||
|
||||
static enum error ed2k_util_hash(const char *file_path, blksize_t blksize,
|
||||
const struct stat *st)
|
||||
static enum error ed2k_util_hash(const char *file_path, const struct stat *st, void *data)
|
||||
{
|
||||
off_t blksize = st->st_blksize;
|
||||
unsigned char buf[blksize], hash[ED2K_HASH_SIZE];
|
||||
struct ed2k_util_opts *opts = data;
|
||||
struct ed2k_ctx ed2k;
|
||||
enum error err;
|
||||
FILE *f;
|
||||
size_t read_len;
|
||||
int en;
|
||||
|
||||
if (l_opts.pre_hash_fn) {
|
||||
err = l_opts.pre_hash_fn(file_path, st, l_opts.data);
|
||||
if (opts->pre_hash_fn) {
|
||||
err = opts->pre_hash_fn(file_path, st, opts->data);
|
||||
if (err == ED2KUTIL_DONTHASH)
|
||||
return NOERR;
|
||||
else if (err != NOERR)
|
||||
@ -38,7 +37,7 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize,
|
||||
if (en == EINTR && should_exit)
|
||||
return ERR_SHOULD_EXIT;
|
||||
else
|
||||
return ERR_ED2KUTIL_FS;
|
||||
return ERR_ITERPATH;
|
||||
}
|
||||
|
||||
ed2k_init(&ed2k);
|
||||
@ -54,7 +53,7 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize,
|
||||
}
|
||||
if (ferror(f)) { /* Loop stopped bcuz of error, not EOF */
|
||||
uio_error("Failure while reading file");
|
||||
err = ERR_ED2KUTIL_FS;
|
||||
err = ERR_ITERPATH;
|
||||
goto fail;
|
||||
}
|
||||
assert(feof(f));
|
||||
@ -67,11 +66,11 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize,
|
||||
if (en == EINTR && should_exit)
|
||||
return ERR_SHOULD_EXIT;
|
||||
else
|
||||
return ERR_ED2KUTIL_FS;
|
||||
return ERR_ITERPATH;
|
||||
}
|
||||
|
||||
if (l_opts.post_hash_fn)
|
||||
return l_opts.post_hash_fn(file_path, hash, st, l_opts.data);
|
||||
if (opts->post_hash_fn)
|
||||
return opts->post_hash_fn(file_path, hash, st, opts->data);
|
||||
return NOERR;
|
||||
|
||||
fail:
|
||||
@ -80,46 +79,7 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ed2k_util_walk(const char *fpath, const struct stat *sb,
|
||||
int typeflag, struct FTW *ftwbuf)
|
||||
{
|
||||
if (typeflag == FTW_DNR) {
|
||||
uio_error("Cannot read directory '%s'. Skipping", fpath);
|
||||
return NOERR;
|
||||
}
|
||||
if (typeflag == FTW_D)
|
||||
return NOERR;
|
||||
if (typeflag != FTW_F) {
|
||||
uio_error("Unhandled error '%d'", typeflag);
|
||||
return ERR_ED2KUTIL_UNSUP;
|
||||
}
|
||||
|
||||
return ed2k_util_hash(fpath, sb->st_blksize, sb);
|
||||
}
|
||||
|
||||
enum error ed2k_util_iterpath(const char *path, const struct ed2k_util_opts *opts)
|
||||
{
|
||||
struct stat ts;
|
||||
|
||||
if (stat(path, &ts) != 0) {
|
||||
uio_error("Stat failed for path: '%s' (%s)",
|
||||
path, strerror(errno));
|
||||
return ERR_ED2KUTIL_FS;
|
||||
}
|
||||
|
||||
l_opts = *opts;
|
||||
|
||||
if (S_ISREG(ts.st_mode)) {
|
||||
return ed2k_util_hash(path, ts.st_blksize, &ts);
|
||||
} else if (S_ISDIR(ts.st_mode)) {
|
||||
int ftwret = nftw(path, ed2k_util_walk, 20, 0);
|
||||
if (ftwret == -1) {
|
||||
uio_error("nftw failure");
|
||||
return ERR_ED2KUTIL_FS;
|
||||
}
|
||||
return ftwret;
|
||||
}
|
||||
|
||||
uio_error("Unsupported file type: %d", ts.st_mode & S_IFMT);
|
||||
return ERR_ED2KUTIL_UNSUP;
|
||||
return util_iterpath(path, ed2k_util_hash, (void*)opts);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
E(ERR_OPT_EXIT) /* We should exit in main, if config_parse returns this */ \
|
||||
E(ERR_OPT_UNSET) /* In config_get, if the value isn't set */ \
|
||||
E(ERR_OPT_NOTFOUND) /* In config_get, if the options is not found */ \
|
||||
E(ERR_OPT_NO_SUBCOMMAND) \
|
||||
\
|
||||
E(ERR_NET_APIADDR) /* If there are problems with the api servers address */ \
|
||||
E(ERR_NET_SOCKET) /* If there are problems with the udp socket */ \
|
||||
@ -24,8 +25,7 @@
|
||||
E(ERR_CMD_NONE) /* No command was run */ \
|
||||
E(ERR_CMD_ARG) /* Some problem with the command arguments */ \
|
||||
\
|
||||
E(ERR_ED2KUTIL_FS) /* Some filesystem problem */ \
|
||||
E(ERR_ED2KUTIL_UNSUP) /* Operation or file type is unsupported */ \
|
||||
E(ERR_ITERPATH) /* Couldn't start iterating over the given path */ \
|
||||
E(ED2KUTIL_DONTHASH) /* Skip the hashing part. pre_hash_fn can return this */ \
|
||||
\
|
||||
E(ERR_API_ENCRYPTFAIL) /* Cannot start encryption with the api */ \
|
||||
|
12
src/uio.c
12
src/uio.c
@ -20,9 +20,9 @@ void uio_error(const char *format, ...)
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
printf("\033[31m[ERROR]: ");
|
||||
vprintf(format, ap);
|
||||
printf("\033[0m\n");
|
||||
fprintf(stderr, "\033[31m[ERROR]: ");
|
||||
vfprintf(stderr, format, ap);
|
||||
fprintf(stderr, "\033[0m\n");
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
@ -38,9 +38,9 @@ void uio_debug(const char *format, ...)
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
printf("\033[35m[DEBUG]: ");
|
||||
vprintf(format, ap);
|
||||
printf("\033[0m\n");
|
||||
fprintf(stderr, "\033[35m[DEBUG]: ");
|
||||
vfprintf(stderr, format, ap);
|
||||
fprintf(stderr, "\033[0m\n");
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
67
src/util.c
67
src/util.c
@ -1,9 +1,15 @@
|
||||
#define _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 500
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ftw.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "error.h"
|
||||
#include "uio.h"
|
||||
|
||||
void util_byte2hex(const uint8_t* bytes, size_t bytes_len,
|
||||
bool uppercase, char* out)
|
||||
@ -86,3 +92,62 @@ uint64_t util_iso2unix(const char *isotime)
|
||||
|
||||
return mktime(&tm);
|
||||
}
|
||||
|
||||
/* nftw doesn't support passing in user data to the callback function :/ */
|
||||
static util_itercb global_iterpath_cb = NULL;
|
||||
static void *global_iterpath_data = NULL;
|
||||
|
||||
static int util_iterpath_walk(const char *fpath, const struct stat *sb,
|
||||
int typeflag, struct FTW *ftwbuf)
|
||||
{
|
||||
if (typeflag == FTW_DNR) {
|
||||
uio_error("Cannot read directory '%s'. Skipping", fpath);
|
||||
return NOERR;
|
||||
}
|
||||
if (typeflag == FTW_D)
|
||||
return NOERR;
|
||||
if (typeflag != FTW_F) {
|
||||
uio_error("Unhandled error '%d'", typeflag);
|
||||
return ERR_ITERPATH;
|
||||
}
|
||||
|
||||
return global_iterpath_cb(fpath, sb, global_iterpath_data);
|
||||
}
|
||||
|
||||
enum error util_iterpath(const char *path, util_itercb cb, void *data)
|
||||
{
|
||||
assert(global_iterpath_cb == NULL);
|
||||
enum error ret = ERR_ITERPATH;
|
||||
struct stat ts;
|
||||
|
||||
if (stat(path, &ts) != 0) {
|
||||
uio_error("Stat failed for path: '%s' (%s)",
|
||||
path, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (S_ISREG(ts.st_mode)) {
|
||||
/* If the path is a regular file, call the cb once */
|
||||
ret = cb(path, &ts, data);
|
||||
goto end;
|
||||
} else if (S_ISDIR(ts.st_mode)) {
|
||||
global_iterpath_cb = cb;
|
||||
global_iterpath_data = data;
|
||||
|
||||
/* If a directory, walk over it */
|
||||
ret = nftw(path, util_iterpath_walk, 20, 0);
|
||||
if (ret == -1) {
|
||||
uio_error("nftw failure");
|
||||
goto error;
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
uio_error("Unsupported file type: %d", ts.st_mode & S_IFMT);
|
||||
end:
|
||||
global_iterpath_cb = global_iterpath_data = NULL;
|
||||
return ret;
|
||||
error:
|
||||
ret = ERR_ITERPATH;
|
||||
goto end;
|
||||
}
|
||||
|
14
src/util.h
14
src/util.h
@ -1,9 +1,13 @@
|
||||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#define MS_TO_TIMESPEC(ts, ms) { \
|
||||
ts->tv_sec = ms / 1000; \
|
||||
ts->tv_nsec = (ms % 1000) * 1000000; \
|
||||
@ -50,4 +54,14 @@ uint64_t util_timespec_diff(const struct timespec *past,
|
||||
*/
|
||||
uint64_t util_iso2unix(const char *isotime);
|
||||
|
||||
/*
|
||||
* Iterate over a given path and call the 'cb' function for each file
|
||||
* If 'cb' returns anything other than NOERR, the iteration will stop and
|
||||
* that error will be returned.
|
||||
* !! THIS FUNCTION IS NOT THREAD SAFE !!
|
||||
*/
|
||||
typedef enum error (*util_itercb)(const char *path, const struct stat *fstat, void *data);
|
||||
enum error util_iterpath(const char *path, util_itercb cb, void *data);
|
||||
|
||||
|
||||
#endif /* _UTIL_H */
|
||||
|
Loading…
Reference in New Issue
Block a user